Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions plugins/auto-close-brackets.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ codeInput.plugins.AutoCloseBrackets = class extends codeInput.Plugin {

/* Add keystroke events */
afterElementsAdded(codeInput) {
codeInput.textareaElement.addEventListener('keydown', (event) => { this.checkBackspace(codeInput, event) });
codeInput.textareaElement.addEventListener('beforeinput', (event) => { this.checkBrackets(codeInput, event); });
codeInput.pluginData.autoCloseBrackets = { automatedKeypresses: false};
codeInput.textareaElement.addEventListener('keydown', (event) => { this.checkBackspace(codeInput, event); });
codeInput.textareaElement.addEventListener('beforeinput', (event) => { this.checkClosingBracket(codeInput, event); });
codeInput.textareaElement.addEventListener('input', (event) => { this.checkOpeningBracket(codeInput, event); });
}

/* Deal with the automatic creation of closing bracket when opening brackets are typed, and the ability to "retype" a closing
bracket where one has already been placed. */
checkBrackets(codeInput, event) {
/* Deal with the ability to "retype" a closing bracket where one has already
been placed. Runs before input so newly typing a closing bracket can be
prevented.*/
checkClosingBracket(codeInput, event) {
if(codeInput.pluginData.autoCloseBrackets.automatedKeypresses) return;
if(event.data == codeInput.textareaElement.value[codeInput.textareaElement.selectionStart]) {
// Check if a closing bracket is typed
for(let openingBracket in this.bracketPairs) {
Expand All @@ -37,18 +41,30 @@ codeInput.plugins.AutoCloseBrackets = class extends codeInput.Plugin {
break;
}
}
} else if(event.data in this.bracketPairs) {
}
}

/* Deal with the automatic creation of closing bracket when opening brackets are typed. Runs after input for consistency between browsers. */
checkOpeningBracket(codeInput, event) {
if(codeInput.pluginData.autoCloseBrackets.automatedKeypresses) return;
if(event.data in this.bracketPairs) {
// Opening bracket typed; Create bracket pair
let closingBracket = this.bracketPairs[event.data];
// Insert the closing bracket
// automatedKeypresses property to prevent keypresses being captured
// by this plugin during automated input as some browsers
// (e.g. GNOME Web) do.
codeInput.pluginData.autoCloseBrackets.automatedKeypresses = true;
document.execCommand("insertText", false, closingBracket);
codeInput.pluginData.autoCloseBrackets.automatedKeypresses = false;
// Move caret before the inserted closing bracket
codeInput.textareaElement.selectionStart = codeInput.textareaElement.selectionEnd -= 1;
}
}

/* Deal with cases where a backspace deleting an opening bracket deletes the closing bracket straight after it as well */
checkBackspace(codeInput, event) {
if(codeInput.pluginData.autoCloseBrackets.automatedKeypresses) return;
if(event.key == "Backspace" && codeInput.textareaElement.selectionStart == codeInput.textareaElement.selectionEnd) {
let closingBracket = this.bracketPairs[codeInput.textareaElement.value[codeInput.textareaElement.selectionStart-1]];
if(closingBracket != undefined && codeInput.textareaElement.value[codeInput.textareaElement.selectionStart] == closingBracket) {
Expand All @@ -58,4 +74,4 @@ codeInput.plugins.AutoCloseBrackets = class extends codeInput.Plugin {
}
}
}
}
}
27 changes: 25 additions & 2 deletions plugins/indent.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,12 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {
let indentationWidthPx = testIndentationWidthSpan.offsetWidth;
codeInput.removeChild(testIndentationWidthPre);

codeInput.pluginData.indent = {indentationWidthPx: indentationWidthPx};
codeInput.pluginData.indent = {automatedKeypresses: false, indentationWidthPx: indentationWidthPx};
}

/* Deal with the Tab key causing indentation, and Tab+Selection indenting / Shift+Tab+Selection unindenting lines, and the mechanism through which Tab can be used to switch focus instead (accessibility). */
checkTab(codeInput, event) {
if(codeInput.pluginData.indent.automatedKeypresses) return;
if(!this.tabIndentationEnabled) return;
if(this.escTabToChangeFocus) {
// Accessibility - allow Tab for keyboard navigation when Esc pressed right before it.
Expand Down Expand Up @@ -116,7 +117,12 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {

if(!event.shiftKey && inputElement.selectionStart == inputElement.selectionEnd) {
// Just place a tab/spaces here.
// automatedKeypresses property to prevent keypresses being captured
// by this plugin during automated input as some browsers
// (e.g. GNOME Web) do.
codeInput.pluginData.indent.automatedKeypresses = true;
document.execCommand("insertText", false, this.indentation);
codeInput.pluginData.indent.automatedKeypresses = false;

} else {
let lines = inputElement.value.split("\n");
Expand Down Expand Up @@ -147,7 +153,12 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {
// Add tab at start
inputElement.selectionStart = letterI;
inputElement.selectionEnd = letterI;
// automatedKeypresses property to prevent keypresses being captured
// by this plugin during automated input as some browsers
// (e.g. GNOME Web) do.
codeInput.pluginData.indent.f = true;
document.execCommand("insertText", false, this.indentation);
codeInput.pluginData.indent.automatedKeypresses = false;

// Change selection
if(selectionStartI > letterI) { // Indented outside selection
Expand Down Expand Up @@ -191,6 +202,7 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {

/* Deal with new lines retaining indentation */
checkEnter(codeInput, event) {
if(codeInput.pluginData.indent.automatedKeypresses) return;
if(event.key != "Enter") {
return;
}
Expand Down Expand Up @@ -263,18 +275,27 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {
}

// insert our indents and any text from the previous line that might have been after the line break
// negative indents shouldn't exist and would only break future calculations.
if(numberIndents < 0) {
numberIndents = 0;
}
for (let i = 0; i < numberIndents; i++) {
newLine += this.indentation;
}

// save the current cursor position
let selectionStartI = inputElement.selectionStart;

// automatedKeypresses property to prevent keypresses being captured
// by this plugin during automated input as some browsers
// (e.g. GNOME Web) do.
codeInput.pluginData.indent.automatedKeypresses = true;
if(bracketThreeLinesTriggered) {
document.execCommand("insertText", false, "\n" + furtherIndentation); // Write indented line
numberIndents += 1; // Reflects the new indent
}
document.execCommand("insertText", false, "\n" + newLine); // Write new line, including auto-indentation
codeInput.pluginData.indent.automatedKeypresses = false;

// move cursor to new position
inputElement.selectionStart = selectionStartI + numberIndents*this.indentationNumChars + 1; // count the indent level and the newline character
Expand All @@ -294,6 +315,7 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {

/* Deal with one 'tab' of spaces-based-indentation being deleted by each backspace, rather than one space */
checkBackspace(codeInput, event) {
if(codeInput.pluginData.indent.automatedKeypresses) return;
if(event.key != "Backspace" || this.indentationNumChars == 1) {
return; // Normal backspace when indentation of 1
}
Expand Down Expand Up @@ -321,7 +343,8 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {
if(codeInput.value.substring(codeInput.textareaElement.selectionStart - this.indentationNumChars, codeInput.textareaElement.selectionStart) == this.indentation) {
// Indentation before cursor = delete it
codeInput.textareaElement.selectionStart -= this.indentationNumChars;
document.execCommand("delete", false, "");
// document.execCommand("delete", false, "");
// event.preventDefault();
}
}
}
Expand Down
19 changes: 13 additions & 6 deletions tests/tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,22 +162,27 @@ function startLoad(codeInputElem, isHLJS) {
}

/* Make input events work and be trusted in the inputElement - thanks for this SO answer: https://stackoverflow.com/a/49519772/21785620 */
function allowInputEvents(inputElement) {
function allowInputEvents(inputElement, codeInputElement=undefined) {
inputElement.addEventListener('input', function(e){
if(!e.isTrusted){
e.preventDefault();
// Manually trigger
// Prevent auto-close-brackets plugin recapturing the event
// Needed because this interception is hacky.
// TODO: Potentially plugin-agnostic way, probably automatedKeypresses var in core, won't be needed much but may be helpful extra feature.
if(codeInputElement !== undefined) codeInputElement.pluginData.autoCloseBrackets.automatedKeypresses = true;
document.execCommand("insertText", false, e.data);
if(codeInputElement !== undefined) codeInputElement.pluginData.autoCloseBrackets.automatedKeypresses = false;
}
}, false);
}

/* Start the tests using the textarea inside the code-input element and whether highlight.js is being used (as the Autodetect plugin only works with highlight.js, for example) */
async function startTests(textarea, isHLJS) {
textarea.focus();
allowInputEvents(textarea);

codeInputElement = textarea.parentElement;
allowInputEvents(textarea, codeInputElement);

/*--- Tests for core functionality ---*/

Expand Down Expand Up @@ -438,7 +443,7 @@ console.log("I've got another line!", 2 &lt; 3, "should be true.");
findInput.focus();
allowInputEvents(findInput);
addText(findInput, "hello");
await waitAsync(150); // Wait for highlighting so matches update
await waitAsync(200); // Wait for highlighting so matches update

replaceInput.value = "hi";
replaceAllButton.click();
Expand Down Expand Up @@ -525,8 +530,10 @@ console.log("I've got another line!", 2 &lt; 3, "should be true.");
backspace(textarea);

testAddingText("Indent-AutoCloseBrackets", textarea, function(textarea) {
addText(textarea, `function printTriples(max) {\nfor(let i = 0; i < max-2; i++) {\nfor(let j = 0; j < max-1; j++) {\nfor(let k = 0; k < max; k++) {\nconsole.log(i,j,k);\n}\n//Hmmm...`, true);
}, 'function printTriples(max) {\n for(let i = 0; i < max-2; i++) {\n for(let j = 0; j < max-1; j++) {\n for(let k = 0; k < max; k++) {\n console.log(i,j,k);\n }\n //Hmmm...\n }\n }\n }\n}', 189, 189);
addText(textarea, `function printTriples(max) {\nfor(let i = 0; i < max-2; i++) {\nfor(let j = 0; j < max-1; j++) {\nfor(let k = 0; k < max; k++) {\nconsole.log(i,j,k);\n}\n//Hmmm...\n}//Test auto-unindent\n{`, true);
move(textarea, 1); // Move after created closing bracket
backspace(textarea); // Remove created closing bracket
}, 'function printTriples(max) {\n for(let i = 0; i < max-2; i++) {\n for(let j = 0; j < max-1; j++) {\n for(let k = 0; k < max; k++) {\n console.log(i,j,k);\n }\n //Hmmm...\n }//Test auto-unindent\n {\n }\n }\n }\n}', 221, 211);

// SelectTokenCallbacks
if(isHLJS) {
Expand Down Expand Up @@ -590,4 +597,4 @@ console.log("I've got another line!", 2 &lt; 3, "should be true.");
document.querySelector("h2").style.backgroundColor = "lightgreen";
document.querySelector("h2").textContent = "All Tests have Passed.";
}
}
}