feature: client: vim: add ability to show terminal with 'tt'

This commit is contained in:
coderaiser 2023-01-29 17:57:15 +02:00
parent 203117d2b9
commit a27e4ecf61
4 changed files with 196 additions and 166 deletions

View file

@ -222,6 +222,7 @@ When the `--vim` option is provided, or the configuration parameter `vim` is set
| `N` | navigate to previous found file
| `md` | make directory
| `mf` | make file
| `tt` | show terminal
Commands can be joined, for example:

View file

@ -16,35 +16,39 @@ module.exports = async (key, event) => {
const getOperations = (event) => ({
escape: DOM.unselectFiles,
remove: () => {
CloudCmd.Operation.show('delete');
},
makeDirectory: () => {
event.stopImmediatePropagation();
event.preventDefault();
DOM.promptNewDir();
},
makeFile: () => {
event.stopImmediatePropagation();
event.preventDefault();
DOM.promptNewFile();
},
terminal: () => {
CloudCmd.Terminal.show();
},
copy: () => {
DOM.Buffer.copy();
DOM.unselectFiles();
},
select: () => {
const current = Info.element;
DOM.toggleSelectedFile(current);
},
paste: DOM.Buffer.paste,
moveNext: ({count, isVisual, isDelete}) => {
setCurrent('next', {
count,
@ -52,7 +56,7 @@ const getOperations = (event) => ({
isDelete,
});
},
movePrevious: ({count, isVisual, isDelete}) => {
setCurrent('previous', {
count,
@ -60,25 +64,25 @@ const getOperations = (event) => ({
isDelete,
});
},
find: async () => {
event.preventDefault();
const [, value] = await Dialog.prompt('Find', '');
if (!value)
return;
const names = Info.files.map(DOM.getCurrentName);
const [result] = finder.find(value, names);
DOM.setCurrentByName(result);
},
findNext: () => {
const name = finder.findNext();
DOM.setCurrentByName(name);
},
findPrevious: () => {
const name = finder.findPrevious();
DOM.setCurrentByName(name);
@ -89,33 +93,33 @@ module.exports.selectFile = selectFile;
function selectFile(current) {
const name = DOM.getCurrentName(current);
if (name === '..')
return;
DOM.selectFile(current);
}
function setCurrent(sibling, {count, isVisual, isDelete}) {
let current = Info.element;
const select = isVisual ? selectFile : DOM.unselectFile;
select(current);
const position = `${sibling}Sibling`;
for (let i = 0; i < count; i++) {
const next = current[position];
if (!next)
break;
current = next;
select(current);
}
DOM.setCurrentFile(current);
if (isDelete)
CloudCmd.Operation.show('delete');
}

View file

@ -23,7 +23,10 @@ global.CloudCmd = getCloudCmd();
const vim = require(pathVim);
const {assign} = Object;
const {DOM} = global;
const {
DOM,
CloudCmd,
} = global;
const {Buffer} = DOM;
const pathFind = join(dir, 'vim', 'find');
@ -31,14 +34,14 @@ const {reRequire, stopAll} = mockRequire;
test('cloudcmd: client: key: set next file: no', (t) => {
const element = {};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
vim('j', {});
t.calledWith(setCurrentFile, [element], 'should set next file');
t.end();
});
@ -48,14 +51,14 @@ test('cloudcmd: client: key: set next file current: j', (t) => {
const element = {
nextSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
vim('j', {});
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
t.end();
});
@ -65,16 +68,16 @@ test('cloudcmd: client: key: set next file current: mjj', (t) => {
const element = {
nextSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
vim('m', {});
vim('j', {});
vim('j', {});
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
t.end();
});
@ -84,15 +87,15 @@ test('cloudcmd: client: key: set next file current: g', (t) => {
const element = {
nextSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
vim('g', {});
vim('j', {});
t.calledWith(setCurrentFile, [nextSibling], 'should ignore g');
t.end();
});
@ -105,17 +108,17 @@ test('cloudcmd: client: key: set +2 file current', (t) => {
const element = {
nextSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
const event = {};
vim('2', event);
vim('j', event);
t.calledWith(setCurrentFile, [last], 'should set next file');
t.end();
});
@ -128,21 +131,21 @@ test('cloudcmd: client: key: select +2 files from current before delete', (t) =>
const element = {
nextSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
global.DOM.selectFile = stub();
global.DOM.getCurrentName = () => false;
global.CloudCmd.Operation.show = stub();
const event = {};
vim('d', event);
vim('2', event);
vim('j', event);
t.calledWith(setCurrentFile, [last], 'should set next file');
t.end();
});
@ -155,22 +158,22 @@ test('cloudcmd: client: key: delete +2 files from current', (t) => {
const element = {
nextSibling,
};
const setCurrentFile = stub();
const show = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
global.DOM.selectFile = stub();
global.DOM.getCurrentName = () => false;
global.CloudCmd.Operation.show = show;
const event = {};
vim('d', event);
vim('2', event);
vim('j', event);
t.calledWith(show, ['delete'], 'should call delete');
t.end();
});
@ -180,87 +183,87 @@ test('cloudcmd: client: key: set previous file current', (t) => {
const element = {
previousSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
vim('k', {});
t.calledWith(setCurrentFile, [previousSibling], 'should set previous file');
t.end();
});
test('cloudcmd: client: key: copy: no', (t) => {
const copy = stub();
Buffer.copy = copy;
vim('y', {});
t.notOk(copy.called, 'should not copy files');
t.end();
});
test('cloudcmd: client: key: copy', (t) => {
const copy = stub();
Buffer.copy = copy;
vim('v', {});
vim('y', {});
t.ok(copy.calledWith(), 'should copy files');
t.end();
});
test('cloudcmd: client: key: copy: unselectFiles', (t) => {
const unselectFiles = stub();
DOM.unselectFiles = unselectFiles;
vim('v', {});
vim('y', {});
t.ok(unselectFiles.calledWith(), 'should unselect files');
t.end();
});
test('cloudcmd: client: key: paste', (t) => {
const paste = stub();
Buffer.paste = paste;
vim('p', {});
t.ok(paste.calledWith(), 'should paste files');
t.end();
});
test('cloudcmd: client: key: selectFile: ..', (t) => {
const getCurrentName = stub();
DOM.selectFile = stub();
DOM.getCurrentName = () => '..';
const current = {};
vim.selectFile(current);
t.notOk(getCurrentName.called, 'should not call selectFile');
t.end();
});
test('cloudcmd: client: key: selectFile', (t) => {
const selectFile = stub();
DOM.selectFile = selectFile;
DOM.getCurrentName = (a) => a.name;
const current = {};
vim.selectFile(current);
t.calledWith(selectFile, [current], 'should call selectFile');
t.end();
});
@ -273,14 +276,14 @@ test('cloudcmd: client: key: set last file current: shift + g', (t) => {
const element = {
nextSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
vim('G', {});
t.calledWith(setCurrentFile, [last], 'should set last file');
t.end();
});
@ -293,14 +296,14 @@ test('cloudcmd: client: key: set last file current: $', (t) => {
const element = {
nextSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
vim('$', {});
t.calledWith(setCurrentFile, [last], 'should set last file');
t.end();
});
@ -310,19 +313,19 @@ test('cloudcmd: client: key: set first file current: gg', (t) => {
const previousSibling = {
previousSibling: first,
};
const element = {
previousSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
vim('g', {});
vim('g', {});
t.calledWith(setCurrentFile, [first], 'should set first file');
t.end();
});
@ -332,46 +335,46 @@ test('cloudcmd: client: key: set first file current: ^', (t) => {
const previousSibling = {
previousSibling: first,
};
const element = {
previousSibling,
};
const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.setCurrentFile = setCurrentFile;
vim('^', {});
t.calledWith(setCurrentFile, [first], 'should set first file');
t.end();
});
test('cloudcmd: client: key: visual', (t) => {
const element = {};
const toggleSelectedFile = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.toggleSelectedFile = toggleSelectedFile;
vim('v', {});
t.calledWith(toggleSelectedFile, [element], 'should toggle selection');
t.end();
});
test('cloudcmd: client: key: ESC', (t) => {
const element = {};
const unselectFiles = stub();
global.DOM.CurrentInfo.element = element;
global.DOM.unselectFiles = unselectFiles ;
vim('Escape');
t.ok(unselectFiles.calledWith(), 'should toggle selection');
t.end();
});
@ -381,16 +384,16 @@ test('cloudcmd: client: key: Enter', (t) => {
const element = {
nextSibling,
};
const setCurrentFile = stub();
DOM.CurrentInfo.element = element;
DOM.setCurrentFile = setCurrentFile;
vim('Enter');
vim('j');
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
t.end();
});
@ -398,14 +401,14 @@ test('cloudcmd: client: key: Enter', (t) => {
test('cloudcmd: client: key: /', (t) => {
const preventDefault = stub();
const element = {};
DOM.CurrentInfo.element = element;
DOM.getCurrentName = () => '';
vim('/', {
preventDefault,
});
t.calledWithNoArgs(preventDefault, 'should call preventDefault');
t.end();
});
@ -414,95 +417,111 @@ test('cloudcmd: client: find', (t) => {
assign(DOM.Dialog, {
prompt: stub().returns([]),
});
const setCurrentByName = stub();
assign(DOM, {
setCurrentByName,
});
const vim = reRequire(pathVim);
const event = {
preventDefault: stub(),
};
vim('/', event);
stopAll();
t.notCalled(setCurrentByName);
t.end();
});
test('cloudcmd: client: key: n', (t) => {
const findNext = stub();
mockRequire(pathFind, {
findNext,
});
const vim = reRequire(pathVim);
const event = {};
vim('n', event);
stopAll();
t.ok(findNext.calledWith(), 'should call findNext');
t.end();
});
test('cloudcmd: client: key: N', (t) => {
const findPrevious = stub();
mockRequire(pathFind, {
findPrevious,
});
const vim = reRequire(dir + 'vim');
const event = {};
vim('N', event);
stopAll();
t.ok(findPrevious.calledWith(), 'should call findPrevious');
t.end();
});
test('cloudcmd: client: key: make directory', (t) => {
const vim = reRequire(pathVim);
assign(DOM, {
promptNewDir: stub(),
});
const event = {
stopImmediatePropagation: stub(),
preventDefault: stub(),
};
vim('m', event);
vim('d', event);
t.calledWithNoArgs(DOM.promptNewDir);
t.end();
});
test('cloudcmd: client: key: make file', (t) => {
const vim = reRequire(pathVim);
assign(DOM, {
promptNewFile: stub(),
});
const event = {
stopImmediatePropagation: stub(),
preventDefault: stub(),
};
vim('m', event);
vim('f', event);
t.calledWithNoArgs(DOM.promptNewDir);
t.end();
});
test('cloudcmd: client: vim: terminal', (t) => {
assign(CloudCmd, {
Terminal: {
show: stub(),
},
});
const event = {
};
vim('t', event);
vim('t', event);
t.calledWithNoArgs(CloudCmd.Terminal.show);
t.end();
});

View file

@ -38,123 +38,129 @@ module.exports = (key, operations) => {
findPrevious = noop,
makeFile = noop,
makeDirectory = noop,
terminal = noop,
} = operations;
if (key === 'Enter')
return end();
if (key === 'Escape') {
visual(false);
escape();
return end();
}
if (key === 'j' || key === 'w') {
const {
count,
isDelete,
isVisual,
} = handleDelete(prevStore);
!isNaN(count) && moveNext({
count,
isVisual,
isDelete,
});
return end();
}
if (key === 'k' || key === 'b') {
const {
count,
isDelete,
isVisual,
} = handleDelete(prevStore);
!isNaN(count) && movePrevious({
count,
isVisual,
isDelete,
});
return end();
}
if (value === 'gg' || key === '^') {
const {
isDelete,
isVisual,
} = handleDelete(prevStore);
movePrevious({
count: Infinity,
isVisual,
isDelete,
});
return end();
}
if (value === 'md') {
makeDirectory();
return end();
}
if (value === 'tt') {
terminal();
return end();
}
if (value === 'mf') {
makeFile();
return end();
}
if (key === 'd' && (visual() || prevStore === 'd')) {
stopVisual();
remove();
return end();
}
if (key === 'G' || key === '$') {
moveNext({
count: Infinity,
isVisual,
});
return end();
}
if (key === 'y') {
if (!visual())
return end();
stopVisual();
copy();
return end();
}
if (/^p$/i.test(key)) {
paste();
return end();
}
if (/^v$/i.test(key)) {
visual(!visual());
select();
return end();
}
if (key === '/') {
find();
return end();
}
if (key === 'n') {
findNext();
return end();
}
if (key === 'N') {
findPrevious();
return end();
@ -163,15 +169,15 @@ module.exports = (key, operations) => {
function handleDelete(prevStore) {
const isDelete = prevStore[0] === 'd';
if (isDelete) {
visual(true);
prevStore = rmFirst(prevStore);
}
const count = getNumber(prevStore);
const isVisual = visual();
return {
count,
isDelete,
@ -182,10 +188,10 @@ function handleDelete(prevStore) {
function getNumber(value) {
if (!value)
return 1;
if (value === 'g')
return 1;
return parseInt(value);
}