Skip to content

Commit f0028e7

Browse files
committed
Fix scrolling when RETURN pressed in indent plugin; clean up some code
1 parent 25ff82f commit f0028e7

File tree

8 files changed

+76
-117
lines changed

8 files changed

+76
-117
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
---
1010
# Ways you could contribute:
1111
## 1. I've found a bug but don't know how / don't have time to fix it.
12-
If you think you've found a bug, please [submit an issue](https://github.com/WebCoder49/code-input/issues) with screenshots, how you found the bug, and copies of the console's logs if an error is in them. Please also mention the template and plugins you used (your `codeInput.registerTemplate(...)` snippet). We'd be more than happy to help you fix it.
12+
If you think you've found a bug, please [submit an issue](https://github.com/WebCoder49/code-input/issues) with screenshots, how you found the bug, and copies of the console's logs if an error is in them. Please also mention the template and plugins you used (your `codeInput.registerTemplate(...)` snippet). We'd be more than happy to help you fix it. A demo using [CodePen](https://codepen.io/) would be incredibly useful.
1313

1414
## 2. I have implemented a feature / have thought of a potential feature for the library and think it could be useful for others.
1515
The best way to implement a feature is as a plugin like those in the `plugins` folder of `code-input`. If you do this, you could contribute it as in point 3 below.

code-input.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,16 @@ code-input:not(.code-input_loaded)::after {
8383
color: #ccc;
8484
}
8585

86-
/* Make textarea almost completely transparent */
86+
/* Make textarea almost completely transparent, except for caret and placeholder */
8787

8888
code-input textarea {
8989
color: transparent;
9090
background: transparent;
9191
caret-color: inherit!important; /* Or choose your favourite color */
9292
}
93+
code-input textarea::placeholder {
94+
color: lightgrey;
95+
}
9396

9497
/* Can be scrolled */
9598
code-input textarea, code-input pre {

code-input.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -774,15 +774,13 @@ var codeInput = {
774774
oldValue = oldValue.toLowerCase();
775775

776776
// Remove old language class and add new
777-
console.log("code-input: Language: REMOVE", "language-" + oldValue);
778777
code.classList.remove("language-" + oldValue); // From codeElement
779778
code.parentElement.classList.remove("language-" + oldValue); // From preElement
780779
code.classList.remove("language-none"); // Prism
781780
code.parentElement.classList.remove("language-none"); // Prism
782781

783782
if (newValue != undefined && newValue != "") {
784783
code.classList.add("language-" + newValue);
785-
console.log("code-input: Language: ADD", "language-" + newValue);
786784
}
787785

788786
if (mainTextarea.placeholder == oldValue) mainTextarea.placeholder = newValue;
@@ -792,8 +790,7 @@ var codeInput = {
792790
break;
793791
default:
794792
if (codeInput.textareaSyncAttributes.includes(name)) {
795-
console.log("sync", name, oldValue, newValue);
796-
if(newValue == null || newValue == undefined) { // TODO: Console.Log to check reaches here with disabled attribute; Fix for disabled attr removal
793+
if(newValue == null || newValue == undefined) {
797794
this.textareaElement.removeAttribute(name);
798795
} else {
799796
this.textareaElement.setAttribute(name, newValue);

plugins/autocomplete.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ codeInput.plugins.Autocomplete = class extends codeInput.Plugin {
1313
}
1414
/* When a key is pressed, or scrolling occurs, update the autocomplete */
1515
updatePopup(codeInput, onlyScrolled) {
16-
let textarea = codeInput.querySelector("textarea");
16+
let textarea = codeInput.textareaElement;
1717
let caretCoords = this.getCaretCoordinates(codeInput, textarea, textarea.selectionEnd, onlyScrolled);
1818
let popupElem = codeInput.querySelector(".code-input_autocomplete_popup");
1919
popupElem.style.top = caretCoords.top + "px";
@@ -33,7 +33,7 @@ codeInput.plugins.Autocomplete = class extends codeInput.Plugin {
3333
testPosElem.classList.add("code-input_autocomplete_testpos");
3434
codeInput.appendChild(testPosElem); // Styled like first pre, but first pre found to update
3535

36-
let textarea = codeInput.querySelector("textarea");
36+
let textarea = codeInput.textareaElement;
3737
textarea.addEventListener("keyup", this.updatePopup.bind(this, codeInput, false)); // Override this+args in bind - not just scrolling
3838
textarea.addEventListener("click", this.updatePopup.bind(this, codeInput, false)); // Override this+args in bind - not just scrolling
3939
textarea.addEventListener("scroll", this.updatePopup.bind(this, codeInput, true)); // Override this+args in bind - just scrolling

plugins/autodetect.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ codeInput.plugins.Autodetect = class extends codeInput.Plugin {
99
}
1010
/* Remove previous language class */
1111
beforeHighlight(codeInput) {
12-
let result_element = codeInput.querySelector("pre code");
13-
result_element.className = ""; // CODE
14-
result_element.parentElement.className = ""; // PRE
12+
let resultElement = codeInput.codeElement;
13+
resultElement.className = ""; // CODE
14+
resultElement.parentElement.className = ""; // PRE
1515
}
1616
/* Get new language class and set `lang` attribute */
1717
afterHighlight(codeInput) {
18-
let result_element = codeInput.querySelector("pre code");
19-
let lang_class = result_element.className || result_element.parentElement.className;
20-
let lang = lang_class.match(/lang(\w|-)*/i)[0]; // Get word starting with lang...; Get outer bracket
18+
let resultElement = codeInput.codeElement;
19+
let langClass = resultElement.className || resultElement.parentElement.className;
20+
let lang = langClass.match(/lang(\w|-)*/i)[0]; // Get word starting with lang...; Get outer bracket
2121
lang = lang.split("-")[1];
2222
if(lang == "undefined") {
2323
codeInput.removeAttribute("lang");

plugins/debounce-update.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ codeInput.plugins.DebounceUpdate = class extends codeInput.Plugin {
1414
}
1515
/* Runs before elements are added into a `code-input`; Params: codeInput element) */
1616
beforeElementsAdded(codeInput) {
17-
console.log(codeInput, "before elements added");
1817
this.update = codeInput.update.bind(codeInput); // Save previous update func
1918
codeInput.update = this.updateDebounced.bind(this, codeInput);
2019
}

plugins/indent.js

Lines changed: 62 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,145 +10,135 @@ codeInput.plugins.Indent = class extends codeInput.Plugin {
1010

1111
/* Add keystroke events */
1212
afterElementsAdded(codeInput) {
13-
let textarea = codeInput.querySelector("textarea");
14-
textarea.addEventListener('keydown', (event) => { this.check_tab(codeInput, event); this.check_enter(codeInput, event); });
13+
let textarea = codeInput.textareaElement;
14+
textarea.addEventListener('keydown', (event) => { this.checkTab(codeInput, event); this.checkEnter(codeInput, event); });
1515
}
1616

1717
/* Event handlers */
18-
check_tab(codeInput, event) {
18+
checkTab(codeInput, event) {
1919
if(event.key != "Tab") {
2020
return;
2121
}
22-
let input_element = codeInput.querySelector("textarea");
23-
let code = input_element.value;
22+
let inputElement = codeInput.textareaElement;
2423
event.preventDefault(); // stop normal
2524

26-
if(!event.shiftKey && input_element.selectionStart == input_element.selectionEnd) {
25+
if(!event.shiftKey && inputElement.selectionStart == inputElement.selectionEnd) {
2726
// Just place a tab here.
2827
document.execCommand("insertText", false, "\t");
2928

3029
} else {
31-
let lines = input_element.value.split("\n");
32-
let letter_i = 0;
30+
let lines = inputElement.value.split("\n");
31+
let letterI = 0;
3332

34-
let selection_start = input_element.selectionStart; // where cursor moves after tab - moving forward by 1 indent
35-
let selection_end = input_element.selectionEnd; // where cursor moves after tab - moving forward by 1 indent
33+
let selectionStartI = inputElement.selectionStart; // where cursor moves after tab - moving forward by 1 indent
34+
let selectionEndI = inputElement.selectionEnd; // where cursor moves after tab - moving forward by 1 indent
3635

37-
let number_indents = 0;
38-
let first_line_indents = 0;
39-
40-
for (let i = 0; i < lines.length; i++) {
41-
// console.log(lines[i], ": start", selection_start, letter_i + lines[i].length + 1, "&& end", selection_end , letter_i + 1)
42-
if((selection_start <= letter_i+lines[i].length && selection_end >= letter_i + 1)
43-
|| (selection_start == selection_end && selection_start <= letter_i+lines[i].length+1 && selection_end >= letter_i)) { // + 1 so newlines counted
36+
for (let i = 0; i < lines.length; i++) {
37+
if((selectionStartI <= letterI+lines[i].length && selectionEndI >= letterI + 1)
38+
|| (selectionStartI == selectionEndI && selectionStartI <= letterI+lines[i].length+1 && selectionEndI >= letterI)) { // + 1 so newlines counted
4439
// Starts before or at last char and ends after or at first char
4540
if(event.shiftKey) {
4641
if(lines[i][0] == "\t") {
4742
// Remove first tab
48-
input_element.selectionStart = letter_i;
49-
input_element.selectionEnd = letter_i+1;
43+
inputElement.selectionStart = letterI;
44+
inputElement.selectionEnd = letterI+1;
5045
document.execCommand("delete", false, "");
5146

5247
// Change selection
53-
if(selection_start > letter_i) { // Indented outside selection
54-
selection_start--;
48+
if(selectionStartI > letterI) { // Indented outside selection
49+
selectionStartI--;
5550
}
56-
selection_end--;
57-
letter_i--;
51+
selectionEndI--;
52+
letterI--;
5853
}
5954
} else {
6055
// Add tab at start
61-
input_element.selectionStart = letter_i;
62-
input_element.selectionEnd = letter_i;
56+
inputElement.selectionStart = letterI;
57+
inputElement.selectionEnd = letterI;
6358
document.execCommand("insertText", false, "\t");
6459

6560
// Change selection
66-
if(selection_start > letter_i) { // Indented outside selection
67-
selection_start++;
61+
if(selectionStartI > letterI) { // Indented outside selection
62+
selectionStartI++;
6863
}
69-
selection_end++;
70-
letter_i++;
64+
selectionEndI++;
65+
letterI++;
7166
}
7267
}
7368

74-
letter_i += lines[i].length+1; // newline counted
69+
letterI += lines[i].length+1; // newline counted
7570
}
76-
// input_element.value = lines.join("\n");
7771

7872
// move cursor
79-
input_element.selectionStart = selection_start;
80-
input_element.selectionEnd = selection_end;
73+
inputElement.selectionStart = selectionStartI;
74+
inputElement.selectionEnd = selectionEndI;
8175
}
8276

83-
codeInput.update(input_element.value);
77+
codeInput.update(inputElement.value);
8478
}
8579

86-
check_enter(codeInput, event) {
80+
checkEnter(codeInput, event) {
8781
if(event.key != "Enter") {
8882
return;
8983
}
90-
event.preventDefault(); // stop normal
84+
event.preventDefault(); // Stop normal \n only
9185

92-
let input_element = codeInput.querySelector("textarea");
93-
let lines = input_element.value.split("\n");
94-
let letter_i = 0;
95-
let current_line = lines.length - 1;
96-
let new_line = "";
97-
let number_indents = 0;
86+
let inputElement = codeInput.querySelector("textarea");
87+
let lines = inputElement.value.split("\n");
88+
let letterI = 0;
89+
let currentLineI = lines.length - 1;
90+
let newLine = "";
91+
let numberIndents = 0;
9892

9993
// find the index of the line our cursor is currently on
10094
for (let i = 0; i < lines.length; i++) {
101-
letter_i += lines[i].length + 1;
102-
if(input_element.selectionEnd <= letter_i) {
103-
current_line = i;
95+
letterI += lines[i].length + 1;
96+
if(inputElement.selectionEnd <= letterI) {
97+
currentLineI = i;
10498
break;
10599
}
106100
}
107101

108102
// count the number of indents the current line starts with (up to our cursor position in the line)
109-
let cursor_pos_in_line = lines[current_line].length - (letter_i - input_element.selectionEnd) + 1;
110-
for (let i = 0; i < cursor_pos_in_line; i++) {
111-
if (lines[current_line][i] == "\t") {
112-
number_indents++;
103+
let cursorPosInLine = lines[currentLineI].length - (letterI - inputElement.selectionEnd) + 1;
104+
for (let i = 0; i < cursorPosInLine; i++) {
105+
if (lines[currentLineI][i] == "\t") {
106+
numberIndents++;
113107
} else {
114108
break;
115109
}
116110
}
117111

118112
// determine the text before and after the cursor and chop the current line at the new line break
119-
let text_after_cursor = "";
120-
if (cursor_pos_in_line != lines[current_line].length) {
121-
text_after_cursor = lines[current_line].substring(cursor_pos_in_line);
122-
lines[current_line] = lines[current_line].substring(0, cursor_pos_in_line);
113+
let textAfterCursor = "";
114+
if (cursorPosInLine != lines[currentLineI].length) {
115+
textAfterCursor = lines[currentLineI].substring(cursorPosInLine);
116+
lines[currentLineI] = lines[currentLineI].substring(0, cursorPosInLine);
123117
}
124118

125119
// insert our indents and any text from the previous line that might have been after the line break
126-
for (let i = 0; i < number_indents; i++) {
127-
new_line += "\t";
120+
for (let i = 0; i < numberIndents; i++) {
121+
newLine += "\t";
128122
}
129123

130124
// save the current cursor position
131-
let selection_start = input_element.selectionStart;
132-
let selection_end = input_element.selectionEnd;
125+
let selectionStartI = inputElement.selectionStart;
133126

134-
document.execCommand("insertText", false, "\n" + new_line); // Write new line, including auto-indentation
127+
document.execCommand("insertText", false, "\n" + newLine); // Write new line, including auto-indentation
135128

136129
// move cursor to new position
137-
input_element.selectionStart = selection_start + number_indents + 1; // count the indent level and the newline character
138-
input_element.selectionEnd = selection_start + number_indents + 1;
139-
140-
codeInput.update(input_element.value);
141-
130+
inputElement.selectionStart = selectionStartI + numberIndents + 1; // count the indent level and the newline character
131+
inputElement.selectionEnd = inputElement.selectionStart;
142132

143-
// Update scrolls
144-
input_element.scrollLeft = 0;
145-
// Move down 1 line
146-
let lineHeight = Number(getComputedStyle(input_element).lineHeight.split(0, -2));
147-
// console.log(getComputedStyle(input_element).lineHeight);
148-
if(lineHeight == NaN && getComputedStyle(input_element).lineHeight.split(-2) == "px") {
149-
input_element.scrollTop += lineHeight;
150-
} else {
151-
input_element.scrollTop += 20; // px
133+
134+
// Scroll down to cursor if necessary
135+
let paddingTop = Number(getComputedStyle(inputElement).paddingTop.replace("px", ""));
136+
let lineHeight = Number(getComputedStyle(inputElement).lineHeight.replace("px", ""));
137+
let inputHeight = Number(getComputedStyle(inputElement).height.replace("px", ""));
138+
if(currentLineI*lineHeight + lineHeight*2 + paddingTop >= inputElement.scrollTop + inputHeight) { // Cursor too far down
139+
inputElement.scrollBy(0, Number(getComputedStyle(inputElement).lineHeight.replace("px", "")))
152140
}
141+
142+
codeInput.update(inputElement.value);
153143
}
154144
}

plugins/special-chars.js

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,6 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
143143
// Calculate darkness
144144
text_color = color_brightness < 128 ? "white" : "black";
145145

146-
// console.log(background_color, color_brightness, text_color);
147-
148146
this.cachedColors[ascii_code] = [background_color, text_color];
149147
return [background_color, text_color];
150148
} else {
@@ -172,7 +170,6 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
172170
}
173171

174172
// Ensure font the same
175-
// console.log(font);
176173
this.canvasContext.font = font;
177174

178175
// Try to get width from canvas
@@ -186,33 +183,6 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
186183

187184
this.cachedWidths[font][char] = width;
188185

189-
// console.log(this.cachedWidths);
190186
return width;
191187
}
192-
193-
// getCharacterWidth(char) { // Doesn't work for now - from StackOverflow suggestion https://stackoverflow.com/a/76146120/21785620
194-
// let textarea = codeInput.pluginData.specialChars.textarea;
195-
196-
// // Create a temporary element to measure the width of the character
197-
// const span = document.createElement('span');
198-
// span.textContent = char;
199-
200-
// // Copy the textarea's font to the temporary element
201-
// span.style.fontSize = window.getComputedStyle(textarea).fontSize;
202-
// span.style.fontFamily = window.getComputedStyle(textarea).fontFamily;
203-
// span.style.fontWeight = window.getComputedStyle(textarea).fontWeight;
204-
// span.style.visibility = 'hidden';
205-
// span.style.position = 'absolute';
206-
207-
// // Add the temporary element to the document so we can measure its width
208-
// document.body.appendChild(span);
209-
210-
// // Get the width of the character in pixels
211-
// const width = span.offsetWidth;
212-
213-
// // Remove the temporary element from the document
214-
// document.body.removeChild(span);
215-
216-
// return width;
217-
// }
218188
}

0 commit comments

Comments
 (0)