Skip to content

Commit 2f6ed5d

Browse files
authored
Merge pull request #139 from WebCoder49/webkit-exec-command
Fix plugin bugs related to auto-closing brackets
2 parents cb99464 + 4700894 commit 2f6ed5d

File tree

3 files changed

+61
-15
lines changed

3 files changed

+61
-15
lines changed

plugins/auto-close-brackets.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ codeInput.plugins.AutoCloseBrackets = class extends codeInput.Plugin {
1919

2020
/* Add keystroke events */
2121
afterElementsAdded(codeInput) {
22-
codeInput.textareaElement.addEventListener('keydown', (event) => { this.checkBackspace(codeInput, event) });
23-
codeInput.textareaElement.addEventListener('beforeinput', (event) => { this.checkBrackets(codeInput, event); });
22+
codeInput.pluginData.autoCloseBrackets = { automatedKeypresses: false};
23+
codeInput.textareaElement.addEventListener('keydown', (event) => { this.checkBackspace(codeInput, event); });
24+
codeInput.textareaElement.addEventListener('beforeinput', (event) => { this.checkClosingBracket(codeInput, event); });
25+
codeInput.textareaElement.addEventListener('input', (event) => { this.checkOpeningBracket(codeInput, event); });
2426
}
2527

26-
/* Deal with the automatic creation of closing bracket when opening brackets are typed, and the ability to "retype" a closing
27-
bracket where one has already been placed. */
28-
checkBrackets(codeInput, event) {
28+
/* Deal with the ability to "retype" a closing bracket where one has already
29+
been placed. Runs before input so newly typing a closing bracket can be
30+
prevented.*/
31+
checkClosingBracket(codeInput, event) {
32+
if(codeInput.pluginData.autoCloseBrackets.automatedKeypresses) return;
2933
if(event.data == codeInput.textareaElement.value[codeInput.textareaElement.selectionStart]) {
3034
// Check if a closing bracket is typed
3135
for(let openingBracket in this.bracketPairs) {
@@ -37,18 +41,30 @@ codeInput.plugins.AutoCloseBrackets = class extends codeInput.Plugin {
3741
break;
3842
}
3943
}
40-
} else if(event.data in this.bracketPairs) {
44+
}
45+
}
46+
47+
/* Deal with the automatic creation of closing bracket when opening brackets are typed. Runs after input for consistency between browsers. */
48+
checkOpeningBracket(codeInput, event) {
49+
if(codeInput.pluginData.autoCloseBrackets.automatedKeypresses) return;
50+
if(event.data in this.bracketPairs) {
4151
// Opening bracket typed; Create bracket pair
4252
let closingBracket = this.bracketPairs[event.data];
4353
// Insert the closing bracket
54+
// automatedKeypresses property to prevent keypresses being captured
55+
// by this plugin during automated input as some browsers
56+
// (e.g. GNOME Web) do.
57+
codeInput.pluginData.autoCloseBrackets.automatedKeypresses = true;
4458
document.execCommand("insertText", false, closingBracket);
59+
codeInput.pluginData.autoCloseBrackets.automatedKeypresses = false;
4560
// Move caret before the inserted closing bracket
4661
codeInput.textareaElement.selectionStart = codeInput.textareaElement.selectionEnd -= 1;
4762
}
4863
}
4964

5065
/* Deal with cases where a backspace deleting an opening bracket deletes the closing bracket straight after it as well */
5166
checkBackspace(codeInput, event) {
67+
if(codeInput.pluginData.autoCloseBrackets.automatedKeypresses) return;
5268
if(event.key == "Backspace" && codeInput.textareaElement.selectionStart == codeInput.textareaElement.selectionEnd) {
5369
let closingBracket = this.bracketPairs[codeInput.textareaElement.value[codeInput.textareaElement.selectionStart-1]];
5470
if(closingBracket != undefined && codeInput.textareaElement.value[codeInput.textareaElement.selectionStart] == closingBracket) {
@@ -58,4 +74,4 @@ codeInput.plugins.AutoCloseBrackets = class extends codeInput.Plugin {
5874
}
5975
}
6076
}
61-
}
77+
}

plugins/indent.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,12 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {
8181
let indentationWidthPx = testIndentationWidthSpan.offsetWidth;
8282
codeInput.removeChild(testIndentationWidthPre);
8383

84-
codeInput.pluginData.indent = {indentationWidthPx: indentationWidthPx};
84+
codeInput.pluginData.indent = {automatedKeypresses: false, indentationWidthPx: indentationWidthPx};
8585
}
8686

8787
/* 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). */
8888
checkTab(codeInput, event) {
89+
if(codeInput.pluginData.indent.automatedKeypresses) return;
8990
if(!this.tabIndentationEnabled) return;
9091
if(this.escTabToChangeFocus) {
9192
// Accessibility - allow Tab for keyboard navigation when Esc pressed right before it.
@@ -116,7 +117,12 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {
116117

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

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

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

192203
/* Deal with new lines retaining indentation */
193204
checkEnter(codeInput, event) {
205+
if(codeInput.pluginData.indent.automatedKeypresses) return;
194206
if(event.key != "Enter") {
195207
return;
196208
}
@@ -263,18 +275,27 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {
263275
}
264276

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

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

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

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

295316
/* Deal with one 'tab' of spaces-based-indentation being deleted by each backspace, rather than one space */
296317
checkBackspace(codeInput, event) {
318+
if(codeInput.pluginData.indent.automatedKeypresses) return;
297319
if(event.key != "Backspace" || this.indentationNumChars == 1) {
298320
return; // Normal backspace when indentation of 1
299321
}
@@ -321,7 +343,8 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {
321343
if(codeInput.value.substring(codeInput.textareaElement.selectionStart - this.indentationNumChars, codeInput.textareaElement.selectionStart) == this.indentation) {
322344
// Indentation before cursor = delete it
323345
codeInput.textareaElement.selectionStart -= this.indentationNumChars;
324-
document.execCommand("delete", false, "");
346+
// document.execCommand("delete", false, "");
347+
// event.preventDefault();
325348
}
326349
}
327350
}

tests/tester.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,22 +162,27 @@ function startLoad(codeInputElem, isHLJS) {
162162
}
163163

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

175180
/* 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) */
176181
async function startTests(textarea, isHLJS) {
177182
textarea.focus();
178-
allowInputEvents(textarea);
179183

180184
codeInputElement = textarea.parentElement;
185+
allowInputEvents(textarea, codeInputElement);
181186

182187
/*--- Tests for core functionality ---*/
183188

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

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

527532
testAddingText("Indent-AutoCloseBrackets", textarea, function(textarea) {
528-
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);
529-
}, '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);
533+
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);
534+
move(textarea, 1); // Move after created closing bracket
535+
backspace(textarea); // Remove created closing bracket
536+
}, '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);
530537

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

0 commit comments

Comments
 (0)