189 lines
5.4 KiB
JavaScript
189 lines
5.4 KiB
JavaScript
import cledit from './cleditCore';
|
|
|
|
function Keystroke(handler, priority) {
|
|
this.handler = handler;
|
|
this.priority = priority || 100;
|
|
}
|
|
|
|
cledit.Keystroke = Keystroke;
|
|
|
|
let clearNewline;
|
|
const charTypes = Object.create(null);
|
|
|
|
// Word separators, as in Sublime Text
|
|
'./\\()"\'-:,.;<>~!@#$%^&*|+=[]{}`~?'.split('').cl_each((wordSeparator) => {
|
|
charTypes[wordSeparator] = 'wordSeparator';
|
|
});
|
|
charTypes[' '] = 'space';
|
|
charTypes['\t'] = 'space';
|
|
charTypes['\n'] = 'newLine';
|
|
|
|
function getNextWordOffset(text, offset, isBackward) {
|
|
let previousType;
|
|
let result = offset;
|
|
while ((isBackward && result > 0) || (!isBackward && result < text.length)) {
|
|
const currentType = charTypes[isBackward ? text[result - 1] : text[result]] || 'word';
|
|
if (previousType && currentType !== previousType) {
|
|
if (previousType === 'word' || currentType === 'space' || previousType === 'newLine' || currentType === 'newLine') {
|
|
break;
|
|
}
|
|
}
|
|
previousType = currentType;
|
|
if (isBackward) {
|
|
result -= 1;
|
|
} else {
|
|
result += 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
cledit.defaultKeystrokes = [
|
|
|
|
new Keystroke((evt, state, editor) => {
|
|
if ((!evt.ctrlKey && !evt.metaKey) || evt.altKey) {
|
|
return false;
|
|
}
|
|
const keyCode = evt.charCode || evt.keyCode;
|
|
const keyCodeChar = String.fromCharCode(keyCode).toLowerCase();
|
|
let action;
|
|
switch (keyCodeChar) {
|
|
case 'y':
|
|
action = 'redo';
|
|
break;
|
|
case 'z':
|
|
action = evt.shiftKey ? 'redo' : 'undo';
|
|
break;
|
|
default:
|
|
}
|
|
if (action) {
|
|
evt.preventDefault();
|
|
setTimeout(() => editor.undoMgr[action](), 10);
|
|
return true;
|
|
}
|
|
return false;
|
|
}),
|
|
|
|
new Keystroke((evt, state) => {
|
|
if (evt.which !== 9 /* tab */ || evt.metaKey || evt.ctrlKey) {
|
|
return false;
|
|
}
|
|
|
|
const strSplice = (str, i, remove, add = '') =>
|
|
str.slice(0, i) + add + str.slice(i + (+remove || 0));
|
|
|
|
evt.preventDefault();
|
|
const isInverse = evt.shiftKey;
|
|
const lf = state.before.lastIndexOf('\n') + 1;
|
|
if (isInverse) {
|
|
if (/\s/.test(state.before.charAt(lf))) {
|
|
state.before = strSplice(state.before, lf, 1);
|
|
}
|
|
state.selection = state.selection.replace(/^[ \t]/gm, '');
|
|
} else if (state.selection) {
|
|
state.before = strSplice(state.before, lf, 0, '\t');
|
|
state.selection = state.selection.replace(/\n(?=[\s\S])/g, '\n\t');
|
|
} else {
|
|
state.before += '\t';
|
|
}
|
|
return true;
|
|
}),
|
|
|
|
new Keystroke((evt, state, editor) => {
|
|
if (evt.which !== 13 /* enter */) {
|
|
clearNewline = false;
|
|
return false;
|
|
}
|
|
|
|
evt.preventDefault();
|
|
const lf = state.before.lastIndexOf('\n') + 1;
|
|
if (clearNewline) {
|
|
state.before = state.before.substring(0, lf);
|
|
state.selection = '';
|
|
clearNewline = false;
|
|
return true;
|
|
}
|
|
clearNewline = false;
|
|
const previousLine = state.before.slice(lf);
|
|
const indent = previousLine.match(/^\s*/)[0];
|
|
if (indent.length) {
|
|
clearNewline = true;
|
|
}
|
|
|
|
editor.undoMgr.setCurrentMode('single');
|
|
state.before += `\n${indent}`;
|
|
state.selection = '';
|
|
return true;
|
|
}),
|
|
|
|
new Keystroke((evt, state, editor) => {
|
|
if (evt.which !== 8 /* backspace */ && evt.which !== 46 /* delete */) {
|
|
return false;
|
|
}
|
|
|
|
editor.undoMgr.setCurrentMode('delete');
|
|
if (!state.selection) {
|
|
const isJump = (cledit.Utils.isMac && evt.altKey) || (!cledit.Utils.isMac && evt.ctrlKey);
|
|
if (isJump) {
|
|
// Custom kill word behavior
|
|
const text = state.before + state.after;
|
|
const offset = getNextWordOffset(text, state.before.length, evt.which === 8);
|
|
if (evt.which === 8) {
|
|
state.before = state.before.slice(0, offset);
|
|
} else {
|
|
state.after = state.after.slice(offset - text.length);
|
|
}
|
|
evt.preventDefault();
|
|
return true;
|
|
} else if (evt.which === 8 && state.before.slice(-1) === '\n') {
|
|
// Special treatment for end of lines
|
|
state.before = state.before.slice(0, -1);
|
|
evt.preventDefault();
|
|
return true;
|
|
} else if (evt.which === 46 && state.after.slice(0, 1) === '\n') {
|
|
state.after = state.after.slice(1);
|
|
evt.preventDefault();
|
|
return true;
|
|
}
|
|
} else {
|
|
state.selection = '';
|
|
evt.preventDefault();
|
|
return true;
|
|
}
|
|
return false;
|
|
}),
|
|
|
|
new Keystroke((evt, state, editor) => {
|
|
if (evt.which !== 37 /* left arrow */ && evt.which !== 39 /* right arrow */) {
|
|
return false;
|
|
}
|
|
const isJump = (cledit.Utils.isMac && evt.altKey) || (!cledit.Utils.isMac && evt.ctrlKey);
|
|
if (!isJump) {
|
|
return false;
|
|
}
|
|
|
|
// Custom jump behavior
|
|
const textContent = editor.getContent();
|
|
const offset = getNextWordOffset(
|
|
textContent,
|
|
editor.selectionMgr.selectionEnd,
|
|
evt.which === 37,
|
|
);
|
|
if (evt.shiftKey) {
|
|
// rebuild the state completely
|
|
const min = Math.min(editor.selectionMgr.selectionStart, offset);
|
|
const max = Math.max(editor.selectionMgr.selectionStart, offset);
|
|
state.before = textContent.slice(0, min);
|
|
state.after = textContent.slice(max);
|
|
state.selection = textContent.slice(min, max);
|
|
state.isBackwardSelection = editor.selectionMgr.selectionStart > offset;
|
|
} else {
|
|
state.before = textContent.slice(0, offset);
|
|
state.after = textContent.slice(offset);
|
|
state.selection = '';
|
|
}
|
|
evt.preventDefault();
|
|
return true;
|
|
}),
|
|
];
|