1
+ package simplejavatexteditor ;
2
+
3
+ import java .awt .event .ActionEvent ;
4
+ import java .awt .event .KeyEvent ;
5
+ import java .awt .event .KeyListener ;
6
+ import java .util .ArrayList ;
7
+ import java .util .Collections ;
8
+ import javax .swing .AbstractAction ;
9
+ import javax .swing .ActionMap ;
10
+ import javax .swing .InputMap ;
11
+ import javax .swing .JTextArea ;
12
+ import javax .swing .KeyStroke ;
13
+ import javax .swing .SwingUtilities ;
14
+ import javax .swing .event .DocumentEvent ;
15
+ import javax .swing .event .DocumentListener ;
16
+ import javax .swing .text .BadLocationException ;
17
+
18
+ /**
19
+ * <h1>Auto complete functionality multiple programming languages, including brackets and
20
+ * parentheses</h1>
21
+ *
22
+ * <p>
23
+ * An ArrayList is created for the keywords and the brackets.
24
+ * Logic for setting the content of the ArrayList is
25
+ * found in UI.java. If the word currently being typed
26
+ * matches a word in the list, a Runnable inner class is
27
+ * implemented to handle the word completion.
28
+ *
29
+ * Two other inner classes are also used. The second one handles when the enter
30
+ * key is pressed in response to an auto complete suggestion. The third one
31
+ * performs additional logic on brackets.
32
+ * </p>
33
+ *
34
+ *
35
+ * @author Patrick Slagle
36
+ * @since 2016-12-03
37
+ */
38
+ public class AutoComplete
39
+ implements DocumentListener {
40
+
41
+ private ArrayList <String > brackets = new ArrayList <>();
42
+ private ArrayList <String > bracketCompletions = new ArrayList <>();
43
+
44
+ private ArrayList <String > words = new ArrayList <>();
45
+
46
+ SupportedKeywords kw ;
47
+
48
+ //Keep track of when code completion
49
+ //has been activated
50
+ private enum Mode {
51
+
52
+ INSERT , COMPLETION
53
+ };
54
+
55
+ private final UI ui ;
56
+ private Mode mode = Mode .INSERT ;
57
+ private final JTextArea textArea ;
58
+ private static final String COMMIT_ACTION = "commit" ;
59
+ private boolean isKeyword ;
60
+ private int pos ;
61
+ private String content ;
62
+
63
+ public AutoComplete (UI ui , ArrayList <String > al ) {
64
+ //Set the keywords
65
+ words = al ;
66
+ kw = new SupportedKeywords ();
67
+ brackets = kw .getbrackets ();
68
+ bracketCompletions = kw .getbracketCompletions ();
69
+
70
+ //Access the editor
71
+ this .ui = ui ;
72
+ textArea = ui .getEditor ();
73
+
74
+ //Set the handler for the enter key
75
+ InputMap im = textArea .getInputMap ();
76
+ ActionMap am = textArea .getActionMap ();
77
+ im .put (KeyStroke .getKeyStroke ("ENTER " ), COMMIT_ACTION );
78
+ am .put (COMMIT_ACTION , new CommitAction ());
79
+
80
+ Collections .sort (words );
81
+ }
82
+
83
+ /**
84
+ * A character has been typed into the document.
85
+ * This method performs the primary
86
+ * check to find a keyword completion.
87
+ *
88
+ * @param e
89
+ */
90
+ @ Override
91
+ public void insertUpdate (DocumentEvent e ) {
92
+ pos = e .getOffset ();
93
+ content = null ;
94
+
95
+ try {
96
+ content = textArea .getText (0 , pos + 1 );
97
+ } catch (BadLocationException ex ) {
98
+ ex .printStackTrace ();
99
+ }
100
+
101
+ if (e .getLength () != 1 ) {
102
+ return ;
103
+ }
104
+
105
+ //Before checking for a keyword
106
+ checkForBracket ();
107
+
108
+ //Get the beginning of the word being typed
109
+ int start ;
110
+ for (start = pos ; start >= 0 ; start --) {
111
+ if (!Character .isLetter (content .charAt (start ))) {
112
+ break ;
113
+ }
114
+ }
115
+
116
+ //Auto complete will start
117
+ //after two characters are typed
118
+ if (pos - start < 2 ) {
119
+ return ;
120
+ }
121
+
122
+ //Search for a match on the word being typed
123
+ //in the keywords ArrayList
124
+ String prefix = content .substring (start + 1 );
125
+ int n = Collections .binarySearch (words , prefix );
126
+
127
+ if (n < 0 && -n < words .size ()) {
128
+ String match = words .get (-n - 1 );
129
+
130
+ if (match .startsWith (prefix )) {
131
+ String completion = match .substring (pos - start );
132
+ isKeyword = true ;
133
+ SwingUtilities .invokeLater (
134
+ new CompletionTask (completion , pos + 1 ));
135
+ } else {
136
+ mode = Mode .INSERT ;
137
+ }
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Performs a check to see if the last
143
+ * key typed was one of the supported
144
+ * bracket characters
145
+ */
146
+ private void checkForBracket () {
147
+ //String of the last typed character
148
+ char c = content .charAt (pos );
149
+ String s = String .valueOf (c );
150
+
151
+ for (int i = 0 ; i < brackets .size (); i ++) {
152
+ if (brackets .get (i ).equals (s )) {
153
+ isKeyword = false ;
154
+ SwingUtilities .invokeLater (
155
+ new CompletionTask (bracketCompletions .get (i ), pos + 1 ));
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * So that future classes can view the keyword list in the future.
162
+ *
163
+ * @return the keywords
164
+ */
165
+ private ArrayList <String > getKeywords () {
166
+ return words ;
167
+ }
168
+
169
+ /**
170
+ * So that these keywords can be modified or added to in the future.
171
+ *
172
+ * @param keyword the keyword to set
173
+ */
174
+ private void setKeywords (String keyword ) {
175
+ words .add (keyword );
176
+ }
177
+
178
+ /**
179
+ * Handles the auto complete suggestion
180
+ * generated when the user is typing a
181
+ * word that matches a keyword.
182
+ */
183
+ private class CompletionTask
184
+ implements Runnable {
185
+
186
+ private final String completion ;
187
+ private final int position ;
188
+
189
+ public CompletionTask (String completion , int position ) {
190
+ this .completion = completion ;
191
+ this .position = position ;
192
+ }
193
+
194
+ @ Override
195
+ public void run () {
196
+ textArea .insert (completion , position );
197
+
198
+ textArea .setCaretPosition (position + completion .length ());
199
+ textArea .moveCaretPosition (position );
200
+ mode = Mode .COMPLETION ;
201
+ if (!isKeyword ) {
202
+ textArea .addKeyListener (new HandleBracketEvent ());
203
+ }
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Enter key is pressed in response to an
209
+ * auto complete suggestion. Respond
210
+ * appropriately.
211
+ */
212
+ private class CommitAction
213
+ extends AbstractAction {
214
+
215
+ @ Override
216
+ public void actionPerformed (ActionEvent e ) {
217
+
218
+ if (mode == Mode .COMPLETION ) {
219
+ int pos = textArea .getSelectionEnd ();
220
+
221
+ if (isKeyword ) {
222
+ textArea .insert (" " , pos );
223
+ textArea .setCaretPosition (pos + 1 );
224
+ mode = Mode .INSERT ;
225
+ }
226
+ } else {
227
+ mode = Mode .INSERT ;
228
+ textArea .replaceSelection ("\n " );
229
+ }
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Additional logic for bracket auto complete
235
+ */
236
+ private class HandleBracketEvent
237
+ implements KeyListener {
238
+
239
+ @ Override
240
+ public void keyTyped (KeyEvent e ) {
241
+ //Bracket auto complete needs special attention.
242
+ //Multiple possible responses are needed.
243
+ String keyEvent = String .valueOf (e .getKeyChar ());
244
+ for (String bracketCompletion : bracketCompletions ) {
245
+ if (keyEvent .equals (bracketCompletion )) {
246
+ textArea .replaceRange ("" , pos , pos + 1 );
247
+ mode = Mode .INSERT ;
248
+ textArea .removeKeyListener (this );
249
+ }
250
+ }
251
+ int currentPosition = textArea .getCaretPosition ();
252
+ switch (e .getKeyChar ()) {
253
+ case '\n' :
254
+ textArea .insert ("\n \n " , currentPosition );
255
+ textArea .setCaretPosition (currentPosition + 1 );
256
+ mode = Mode .INSERT ;
257
+ textArea .removeKeyListener (this );
258
+ break ;
259
+ default :
260
+ textArea .setCaretPosition (pos );
261
+ mode = Mode .INSERT ;
262
+ textArea .removeKeyListener (this );
263
+ break ;
264
+ }
265
+ }
266
+
267
+ @ Override
268
+ public void keyPressed (KeyEvent e ) {
269
+ }
270
+
271
+ @ Override
272
+ public void keyReleased (KeyEvent e ) {
273
+ }
274
+ }
275
+
276
+ @ Override
277
+ public void removeUpdate (DocumentEvent e ) {
278
+ }
279
+
280
+ @ Override
281
+ public void changedUpdate (DocumentEvent e ) {
282
+ }
283
+ }
0 commit comments