Skip to content

Commit 314d4c0

Browse files
committed
Add indent plugin
1 parent e0732bd commit 314d4c0

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

plugins/indent.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* Adds indentation using the `Tab` key, and auto-indents after a newline, as well as making it
3+
* possible to indent/unindent multiple lines using Tab/Shift+Tab
4+
*/
5+
codeInput.plugins.Indent = class extends codeInput.Plugin {
6+
constructor() {
7+
super();
8+
}
9+
10+
/* Add keystroke events */
11+
afterElementsAdded(codeInput) {
12+
codeInput.check_tab = this.check_tab;
13+
codeInput.check_enter = this.check_enter;
14+
codeInput.querySelector("textarea").setAttribute("onkeydown", "this.parentElement.check_tab(event); this.parentElement.check_enter(event);");
15+
}
16+
17+
/* Event handlers */
18+
check_tab(event) {
19+
if(event.key != "Tab") {
20+
return;
21+
}
22+
let input_element = this.querySelector("textarea");
23+
let code = input_element.value;
24+
event.preventDefault(); // stop normal
25+
26+
if(!event.shiftKey && input_element.selectionStart == input_element.selectionEnd) {
27+
// Shift always means dedent - this places a tab here.
28+
let before_selection = code.slice(0, input_element.selectionStart); // text before tab
29+
let after_selection = code.slice(input_element.selectionEnd, input_element.value.length); // text after tab
30+
31+
let cursor_pos = input_element.selectionEnd + 1; // where cursor moves after tab - moving forward by 1 char to after tab
32+
input_element.value = before_selection + "\t" + after_selection; // add tab char
33+
34+
// move cursor
35+
input_element.selectionStart = cursor_pos;
36+
input_element.selectionEnd = cursor_pos;
37+
38+
} else {
39+
let lines = input_element.value.split("\n");
40+
let letter_i = 0;
41+
42+
let selection_start = input_element.selectionStart; // where cursor moves after tab - moving forward by 1 indent
43+
let selection_end = input_element.selectionEnd; // where cursor moves after tab - moving forward by 1 indent
44+
45+
let number_indents = 0;
46+
let first_line_indents = 0;
47+
48+
for (let i = 0; i < lines.length; i++) {
49+
letter_i += lines[i].length+1; // newline counted
50+
51+
console.log(lines[i], ": start", input_element.selectionStart, letter_i, "&& end", input_element.selectionEnd , letter_i - lines[i].length)
52+
if(input_element.selectionStart <= letter_i && input_element.selectionEnd >= letter_i - lines[i].length) {
53+
// Starts before or at last char and ends after or at first char
54+
if(event.shiftKey) {
55+
if(lines[i][0] == "\t") {
56+
// Remove first tab
57+
lines[i] = lines[i].slice(1);
58+
if(number_indents == 0) first_line_indents--;
59+
number_indents--;
60+
}
61+
} else {
62+
lines[i] = "\t" + lines[i];
63+
if(number_indents == 0) first_line_indents++;
64+
number_indents++;
65+
}
66+
67+
}
68+
}
69+
input_element.value = lines.join("\n");
70+
71+
// move cursor
72+
input_element.selectionStart = selection_start + first_line_indents;
73+
input_element.selectionEnd = selection_end + number_indents;
74+
}
75+
76+
this.update(input_element.value);
77+
}
78+
79+
check_enter(event) {
80+
if(event.key != "Enter") {
81+
return;
82+
}
83+
event.preventDefault(); // stop normal
84+
85+
let input_element = this.querySelector("textarea");
86+
let lines = input_element.value.split("\n");
87+
let letter_i = 0;
88+
let current_line = lines.length - 1;
89+
let new_line = "";
90+
let number_indents = 0;
91+
92+
// find the index of the line our cursor is currently on
93+
for (let i = 0; i < lines.length; i++) {
94+
letter_i += lines[i].length + 1;
95+
if(input_element.selectionEnd <= letter_i) {
96+
current_line = i;
97+
break;
98+
}
99+
}
100+
101+
// count the number of indents the current line starts with (up to our cursor position in the line)
102+
let cursor_pos_in_line = lines[current_line].length - (letter_i - input_element.selectionEnd) + 1;
103+
for (let i = 0; i < cursor_pos_in_line; i++) {
104+
if (lines[current_line][i] == "\t") {
105+
number_indents++;
106+
} else {
107+
break;
108+
}
109+
}
110+
111+
// determine the text before and after the cursor and chop the current line at the new line break
112+
let text_after_cursor = "";
113+
if (cursor_pos_in_line != lines[current_line].length) {
114+
text_after_cursor = lines[current_line].substring(cursor_pos_in_line);
115+
lines[current_line] = lines[current_line].substring(0, cursor_pos_in_line);
116+
}
117+
118+
// insert our indents and any text from the previous line that might have been after the line break
119+
for (let i = 0; i < number_indents; i++) {
120+
new_line += "\t";
121+
}
122+
new_line += text_after_cursor;
123+
124+
// save the current cursor position
125+
let selection_start = input_element.selectionStart;
126+
let selection_end = input_element.selectionEnd;
127+
128+
// splice our new line into the list of existing lines and join them all back up
129+
lines.splice(current_line + 1, 0, new_line);
130+
input_element.value = lines.join("\n");
131+
132+
// move cursor to new position
133+
input_element.selectionStart = selection_start + number_indents + 1; // count the indent level and the newline character
134+
input_element.selectionEnd = selection_end + number_indents + 1;
135+
136+
this.update(input_element.value);
137+
}
138+
}

0 commit comments

Comments
 (0)