11package processing.p5js
22
3+ import processing.app.Preferences
34import processing.app.syntax.InputHandler
45import processing.app.syntax.PdeInputHandler
56import java.awt.event.KeyEvent
7+ import java.util.*
68
7- class p5jsInputHandler (editor : p5jsEditor): PdeInputHandler(editor) {
8- init {
9+ class p5jsInputHandler (editor : p5jsEditor) : PdeInputHandler(editor) {
10+ init {
911 this .addKeyBinding(" ENTER" , InputHandler .INSERT_BREAK )
1012 this .addKeyBinding(" TAB" , InputHandler .INSERT_TAB )
1113 }
1214
13- override fun handlePressed (event : KeyEvent ? ): Boolean {
14- val c = event!! .keyChar
15- val code = event.keyCode
15+ /* *
16+ * Intercepts key pressed events for JEditTextArea. Shamelessly stolen from Java Mode
17+ *
18+ *
19+ * Called by JEditTextArea inside processKeyEvent(). Note that this
20+ * won't intercept actual characters, because those are fired on
21+ * keyTyped().
22+ * @return true if the event has been handled (to remove it from the queue)
23+ */
24+ override fun handlePressed (event : KeyEvent ): Boolean {
25+ val c = event.getKeyChar()
26+ val code = event.getKeyCode()
27+
28+ val sketch = editor.getSketch()
29+ val textarea = editor.getTextArea()
30+
31+ if (event.isMetaDown()) {
32+ // if ((event.getModifiers() & InputEvent.META_MASK) != 0) {
33+ // event.consume(); // does nothing
34+ return false
35+ }
1636
1737 if ((code == KeyEvent .VK_BACK_SPACE ) || (code == KeyEvent .VK_TAB ) ||
1838 (code == KeyEvent .VK_ENTER ) || ((c.code >= 32 ) && (c.code < 128 ))
1939 ) {
20- editor.sketch.isModified = true
40+ sketch.setModified(true )
41+ }
42+
43+ if ((code == KeyEvent .VK_UP ) && event.isControlDown()) {
44+ // ((event.getModifiers() & InputEvent.CTRL_MASK) != 0)) {
45+ // back up to the last empty line
46+ val contents = textarea.getText().toCharArray()
47+ // int origIndex = textarea.getCaretPosition() - 1;
48+ val caretIndex = textarea.getCaretPosition()
49+
50+ var index: Int = calcLineStart(caretIndex - 1 , contents)
51+ // System.out.println("line start " + (int) contents[index]);
52+ index - = 2 // step over the newline
53+ // System.out.println((int) contents[index]);
54+ var onlySpaces = true
55+ while (index > 0 ) {
56+ if (contents[index].code == 10 ) {
57+ if (onlySpaces) {
58+ index++
59+ break
60+ } else {
61+ onlySpaces = true // reset
62+ }
63+ } else if (contents[index] != ' ' ) {
64+ onlySpaces = false
65+ }
66+ index--
67+ }
68+ // if the first char, index will be -2
69+ if (index < 0 ) index = 0
70+
71+ // if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
72+ if (event.isShiftDown()) {
73+ textarea.setSelectionStart(caretIndex)
74+ textarea.setSelectionEnd(index)
75+ } else {
76+ textarea.setCaretPosition(index)
77+ }
78+ event.consume()
79+
80+ // return true;
81+ } else if ((code == KeyEvent .VK_DOWN ) && event.isControlDown()) {
82+ // ((event.getModifiers() & InputEvent.CTRL_MASK) != 0)) {
83+ val contents = textarea.getText().toCharArray()
84+ val caretIndex = textarea.getCaretPosition()
85+
86+ var index = caretIndex
87+ var lineStart = 0
88+ var onlySpaces = false // don't count this line
89+ while (index < contents.size) {
90+ if (contents[index].code == 10 ) {
91+ if (onlySpaces) {
92+ index = lineStart // this is it
93+ break
94+ } else {
95+ lineStart = index + 1
96+ onlySpaces = true // reset
97+ }
98+ } else if (contents[index] != ' ' ) {
99+ onlySpaces = false
100+ }
101+ index++
102+ }
103+
104+ // if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
105+ if (event.isShiftDown()) {
106+ textarea.setSelectionStart(caretIndex)
107+ textarea.setSelectionEnd(index)
108+ } else {
109+ textarea.setCaretPosition(index)
110+ }
111+ event.consume()
112+
113+ // return true;
114+ } else if (c.code == 9 ) {
115+ // if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
116+ if (event.isShiftDown()) {
117+ // if shift is down, the user always expects an outdent
118+ // https://github.com/processing/processing/issues/497
119+ editor.handleOutdent()
120+ } else if (textarea.isSelectionActive()) {
121+ editor.handleIndent()
122+ } else if (Preferences .getBoolean(" editor.tabs.expand" )) {
123+ val tabSize = Preferences .getInteger(" editor.tabs.size" )
124+ textarea.setSelectedText(this .spaces(tabSize))
125+ event.consume()
126+ } else { // !Preferences.getBoolean("editor.tabs.expand")
127+ textarea.setSelectedText(" \t " )
128+ event.consume()
129+ }
130+ } else if (code == 10 || code == 13 ) { // auto-indent
131+ if (Preferences .getBoolean(" editor.indent" )) {
132+ val contents = textarea.getText().toCharArray()
133+ val tabSize = Preferences .getInteger(" editor.tabs.size" )
134+
135+ // this is the previous character
136+ // (i.e. when you hit return, it'll be the last character
137+ // just before where the newline will be inserted)
138+ val origIndex = textarea.getCaretPosition() - 1
139+
140+ // if the previous thing is a brace (whether prev line or
141+ // up farther) then the correct indent is the number of spaces
142+ // on that line + 'indent'.
143+ // if the previous line is not a brace, then just use the
144+ // identical indentation to the previous line
145+
146+ // calculate the amount of indent on the previous line
147+ // this will be used *only if the prev line is not an indent*
148+ var spaceCount: Int = calcSpaceCount(origIndex, contents)
149+
150+ // If the last character was a left curly brace, then indent.
151+ // For 0122, walk backwards a bit to make sure that the there isn't a
152+ // curly brace several spaces (or lines) back. Also moved this before
153+ // calculating extraCount, since it'll affect that as well.
154+ var index2 = origIndex
155+ while ((index2 >= 0 ) &&
156+ Character .isWhitespace(contents[index2])
157+ ) {
158+ index2--
159+ }
160+ if (index2 != - 1 ) {
161+ // still won't catch a case where prev stuff is a comment
162+ if (contents[index2] == ' {' ) {
163+ // intermediate lines be damned,
164+ // use the indent for this line instead
165+ spaceCount = calcSpaceCount(index2, contents)
166+ spaceCount + = tabSize
167+ }
168+ }
169+
170+ // now before inserting this many spaces, walk forward from
171+ // the caret position and count the number of spaces,
172+ // so that the number of spaces aren't duplicated again
173+ var index = origIndex + 1
174+ var extraCount = 0
175+ while ((index < contents.size) &&
176+ (contents[index] == ' ' )
177+ ) {
178+ // spaceCount--;
179+ extraCount++
180+ index++
181+ }
182+ var braceCount = 0
183+ while ((index < contents.size) && (contents[index] != ' \n ' )) {
184+ if (contents[index] == ' }' ) {
185+ braceCount++
186+ }
187+ index++
188+ }
189+
190+ // Hitting return on a line with spaces *after* the caret
191+ // can cause trouble. For 0099, was ignoring the case, but this is
192+ // annoying, so in 0122 we're trying to fix that.
193+ spaceCount - = extraCount
194+
195+ if (spaceCount < 0 ) {
196+ // for rev 0122, actually delete extra space
197+ // textarea.setSelectionStart(origIndex + 1);
198+ textarea.setSelectionEnd(textarea.getSelectionStop() - spaceCount)
199+ textarea.setSelectedText(" \n " )
200+ textarea.setCaretPosition(textarea.getCaretPosition() + extraCount + spaceCount)
201+ } else {
202+ val insertion = " \n " + this .spaces(spaceCount)
203+ textarea.setSelectedText(insertion)
204+ textarea.setCaretPosition(textarea.getCaretPosition() + extraCount)
205+ }
206+
207+ // not gonna bother handling more than one brace
208+ if (braceCount > 0 ) {
209+ val sel = textarea.getSelectionStart()
210+ // sel - tabSize will be -1 if start/end parens on the same line
211+ // https://download.processing.org/bugzilla/484.html
212+ if (sel - tabSize >= 0 ) {
213+ textarea.select(sel - tabSize, sel)
214+ val s: String = this .spaces(tabSize)
215+ // if these are spaces that we can delete
216+ if (s == textarea.getSelectedText()) {
217+ textarea.setSelectedText(" " )
218+ } else {
219+ textarea.select(sel, sel)
220+ }
221+ }
222+ }
223+ } else {
224+ // Enter/Return was being consumed by somehow even if false
225+ // was returned, so this is a band-aid to simply fire the event again.
226+ // https://download.processing.org/bugzilla/1073.html
227+ textarea.setSelectedText(c.toString())
228+ }
229+ // mark this event as already handled (all but ignored)
230+ return true
231+
232+ // return true;
233+ } else if (c == ' }' ) {
234+ if (Preferences .getBoolean(" editor.indent" )) {
235+ // first remove anything that was there (in case this multiple
236+ // characters are selected, so that it's not in the way of the
237+ // spaces for the auto-indent
238+ if (textarea.getSelectionStart() != textarea.getSelectionStop()) {
239+ textarea.setSelectedText(" " )
240+ }
241+
242+ // if this brace is the only thing on the line, outdent
243+ val contents = textarea.getText().toCharArray()
244+ // index to the character to the left of the caret
245+ val prevCharIndex = textarea.getCaretPosition() - 1
246+
247+ // backup from the current caret position to the last newline,
248+ // checking for anything besides whitespace along the way.
249+ // if there's something besides whitespace, exit without
250+ // messing any sort of indenting.
251+ var index = prevCharIndex
252+ var finished = false
253+ while ((index != - 1 ) && (! finished)) {
254+ if (contents[index].code == 10 ) {
255+ finished = true
256+ index++
257+ } else if (contents[index] != ' ' ) {
258+ // don't do anything, this line has other stuff on it
259+ return false
260+ } else {
261+ index--
262+ }
263+ }
264+ if (! finished) return false // brace with no start
265+
266+ val lineStartIndex = index
267+
268+ val pairedSpaceCount: Int = calcBraceIndent(prevCharIndex, contents) // , 1);
269+ if (pairedSpaceCount == - 1 ) return false
270+
271+ textarea.setSelectionStart(lineStartIndex)
272+ textarea.setSelectedText(this .spaces(pairedSpaceCount))
273+
274+ // mark this event as already handled
275+ event.consume()
276+ return true
277+ }
278+ }
279+ return false
280+ }
281+
282+ /* *
283+ * Return the index for the first character on this line.
284+ */
285+ fun calcLineStart (index : Int , contents : CharArray ): Int {
286+ // backup from the current caret position to the last newline,
287+ // so that we can figure out how far this line was indented
288+ /* int spaceCount = 0;*/
289+ var index = index
290+ var finished = false
291+ while ((index != - 1 ) && (! finished)) {
292+ if ((contents[index].code == 10 ) ||
293+ (contents[index].code == 13 )
294+ ) {
295+ finished = true
296+ // index++; // maybe ?
297+ } else {
298+ index-- // new
299+ }
300+ }
301+ // add one because index is either -1 (the start of the document)
302+ // or it's the newline character for the previous line
303+ return index + 1
304+ }
305+
306+ /* *
307+ * Calculate the number of spaces on this line.
308+ */
309+ protected fun calcSpaceCount (index : Int , contents : CharArray ): Int {
310+ var index = index
311+ index = calcLineStart(index, contents)
312+
313+ var spaceCount = 0
314+ // now walk forward and figure out how many spaces there are
315+ while ((index < contents.size) && (index >= 0 ) &&
316+ (contents[index++ ] == ' ' )
317+ ) {
318+ spaceCount++
319+ }
320+ return spaceCount
321+ }
322+
323+
324+ /* *
325+ * Walk back from 'index' until the brace that seems to be
326+ * the beginning of the current block, and return the number of
327+ * spaces found on that line.
328+ */
329+ protected fun calcBraceIndent (index : Int , contents : CharArray ): Int {
330+ // now that we know things are ok to be indented, walk
331+ // backwards to the last { to see how far its line is indented.
332+ // this isn't perfect cuz it'll pick up commented areas,
333+ // but that's not really a big deal and can be fixed when
334+ // this is all given a more complete (proper) solution.
335+ var index = index
336+ var braceDepth = 1
337+ var finished = false
338+ while ((index != - 1 ) && (! finished)) {
339+ if (contents[index] == ' }' ) {
340+ // aww crap, this means we're one deeper
341+ // and will have to find one more extra {
342+ braceDepth++
343+ // if (braceDepth == 0) {
344+ // finished = true;
345+ // }
346+ index--
347+ } else if (contents[index] == ' {' ) {
348+ braceDepth--
349+ if (braceDepth == 0 ) {
350+ finished = true
351+ }
352+ index--
353+ } else {
354+ index--
355+ }
21356 }
22- return super .handlePressed(event)
357+ // never found a proper brace, be safe and don't do anything
358+ if (! finished) return - 1
359+
360+ // check how many spaces on the line with the matching open brace
361+ // int pairedSpaceCount = calcSpaceCount(index, contents);
362+ return calcSpaceCount(index, contents)
363+ }
364+
365+
366+ private fun spaces (count : Int ): String {
367+ val c = CharArray (count)
368+ Arrays .fill(c, ' ' )
369+ return String (c)
23370 }
24371}
0 commit comments