15
15
*/
16
16
package org .beryx .textio .jline ;
17
17
18
- import java .awt .Color ;
19
- import jline .console .ConsoleReader ;
20
- import jline .console .CursorBuffer ;
21
- import jline .console .UserInterruptException ;
22
- import org .beryx .awt .color .ColorFactory ;
23
- import org .beryx .textio .*;
24
- import org .slf4j .Logger ;
25
- import org .slf4j .LoggerFactory ;
18
+ import static org .beryx .textio .PropertiesConstants .PROP_ANSI_COLOR_MODE ;
19
+ import static org .beryx .textio .PropertiesConstants .PROP_INPUT_BGCOLOR ;
20
+ import static org .beryx .textio .PropertiesConstants .PROP_INPUT_BOLD ;
21
+ import static org .beryx .textio .PropertiesConstants .PROP_INPUT_COLOR ;
22
+ import static org .beryx .textio .PropertiesConstants .PROP_INPUT_ITALIC ;
23
+ import static org .beryx .textio .PropertiesConstants .PROP_INPUT_UNDERLINE ;
24
+ import static org .beryx .textio .PropertiesConstants .PROP_PROMPT_BGCOLOR ;
25
+ import static org .beryx .textio .PropertiesConstants .PROP_PROMPT_BOLD ;
26
+ import static org .beryx .textio .PropertiesConstants .PROP_PROMPT_COLOR ;
27
+ import static org .beryx .textio .PropertiesConstants .PROP_PROMPT_ITALIC ;
28
+ import static org .beryx .textio .PropertiesConstants .PROP_PROMPT_UNDERLINE ;
29
+ import static org .beryx .textio .ReadInterruptionStrategy .Action .CONTINUE ;
30
+ import static org .beryx .textio .ReadInterruptionStrategy .Action .RESTART ;
26
31
32
+ import java .awt .Color ;
27
33
import java .awt .event .ActionEvent ;
28
34
import java .awt .event .ActionListener ;
29
35
import java .io .IOException ;
33
39
import java .util .function .Consumer ;
34
40
import java .util .function .Function ;
35
41
36
- import static org .beryx .textio .PropertiesConstants .*;
37
- import static org .beryx .textio .ReadInterruptionStrategy .Action .*;
42
+ import org .beryx .awt .color .ColorFactory ;
43
+ import org .beryx .textio .AbstractTextTerminal ;
44
+ import org .beryx .textio .KeyCombination ;
45
+ import org .beryx .textio .PropertiesPrefixes ;
46
+ import org .beryx .textio .ReadHandlerData ;
47
+ import org .beryx .textio .ReadInterruptionData ;
48
+ import org .beryx .textio .ReadInterruptionException ;
49
+ import org .beryx .textio .ReadInterruptionStrategy ;
50
+ import org .beryx .textio .TerminalProperties ;
51
+ import org .beryx .textio .TextTerminal ;
52
+ import org .jline .reader .Binding ;
53
+ import org .jline .reader .Buffer ;
54
+ import org .jline .reader .LineReaderBuilder ;
55
+ import org .jline .reader .UserInterruptException ;
56
+ import org .jline .reader .impl .LineReaderImpl ;
57
+ import org .jline .terminal .TerminalBuilder ;
58
+ import org .slf4j .Logger ;
59
+ import org .slf4j .LoggerFactory ;
38
60
39
61
/**
40
62
* A JLine-based {@link TextTerminal}.
41
63
*/
42
- @ PropertiesPrefixes ({"jline" })
64
+ @ PropertiesPrefixes ({ "jline" })
43
65
public class JLineTextTerminal extends AbstractTextTerminal <JLineTextTerminal > {
44
- private static final Logger logger = LoggerFactory .getLogger (JLineTextTerminal .class );
66
+ private static final Logger logger = LoggerFactory .getLogger (JLineTextTerminal .class );
45
67
46
68
private static final Consumer <JLineTextTerminal > DEFAULT_USER_INTERRUPT_HANDLER = textTerm -> System .exit (-1 );
47
69
@@ -74,8 +96,8 @@ public class JLineTextTerminal extends AbstractTextTerminal<JLineTextTerminal> {
74
96
Color .WHITE
75
97
};
76
98
77
- private final ConsoleReader reader ;
78
- private Consumer <JLineTextTerminal >userInterruptHandler = DEFAULT_USER_INTERRUPT_HANDLER ;
99
+ private final LineReaderImpl reader ;
100
+ private Consumer <JLineTextTerminal > userInterruptHandler = DEFAULT_USER_INTERRUPT_HANDLER ;
79
101
private boolean abortRead = true ;
80
102
81
103
private AnsiColorMode ansiColorMode = AnsiColorMode .STANDARD ;
@@ -113,9 +135,9 @@ String getAnsiColorCode(Color color) {
113
135
private static String getStandardColorCode (Color color ) {
114
136
double bestDist = Double .MAX_VALUE ;
115
137
int bestIndex = -1 ;
116
- for (int i = 0 ; i < STANDARD_COLORS .length ; i ++) {
117
- double dist = getColorDistance (color , STANDARD_COLORS [i ]);
118
- if (dist < bestDist ) {
138
+ for (int i = 0 ; i < STANDARD_COLORS .length ; i ++) {
139
+ double dist = getColorDistance (color , STANDARD_COLORS [i ]);
140
+ if (dist < bestDist ) {
119
141
bestDist = dist ;
120
142
bestIndex = i ;
121
143
}
@@ -159,10 +181,12 @@ public static int getStandardColorCode(String colorName) {
159
181
}
160
182
161
183
public Optional <String > getColorCode (String colorName ) {
162
- if (colorName == null || colorName .isEmpty ()) return Optional .empty ();
184
+ if (colorName == null || colorName .isEmpty ()) {
185
+ return Optional .empty ();
186
+ }
163
187
try {
164
188
int code = getStandardColorCode (colorName );
165
- if (code >= 0 ) {
189
+ if (code >= 0 ) {
166
190
return Optional .of ("" + code );
167
191
}
168
192
Color color = ColorFactory .web (colorName );
@@ -175,14 +199,18 @@ public Optional<String> getColorCode(String colorName) {
175
199
}
176
200
177
201
private static int mapTo6 (double val ) {
178
- if (val < 0 ) val = 0 ;
179
- if (val > 255 ) val = 255 ;
180
- return (int )(val * 6.0 / 256.0 );
202
+ if (val < 0 ) {
203
+ val = 0 ;
204
+ }
205
+ if (val > 255 ) {
206
+ val = 255 ;
207
+ }
208
+ return (int ) (val * 6.0 / 256.0 );
181
209
}
182
210
183
211
private String getAnsiColorWithPrefix (int prefix , String colorName ) {
184
212
String ansiCode = getColorCode (colorName ).map (col -> "\u001B [1;" + prefix + col + "m" ).orElse ("" );
185
- logger .debug ("ansiColor({}, {}) = {}" , prefix , colorName , ansiCode );
213
+ logger .debug ("ansiColor({}, {}) = {}" , prefix , colorName , ansiCode );
186
214
return ansiCode ;
187
215
}
188
216
@@ -194,22 +222,24 @@ public String getAnsiBackgroundColor(String colorName) {
194
222
return getAnsiColorWithPrefix (4 , colorName );
195
223
}
196
224
197
- public static ConsoleReader createReader () {
225
+ public static LineReaderImpl createReader () {
198
226
try {
199
- if (System .console () == null ) throw new IllegalArgumentException ("Console not available." );
200
- return new ConsoleReader ();
227
+ return (LineReaderImpl ) LineReaderBuilder .builder ()
228
+ .terminal (TerminalBuilder .builder ().build ())
229
+ .build ();
201
230
} catch (IOException e ) {
202
- throw new IllegalArgumentException ("Cannot create a JLine ConsoleReader ." , e );
231
+ throw new IllegalArgumentException ("Cannot create a JLine Terminal ." , e );
203
232
}
204
233
}
205
234
206
235
public JLineTextTerminal () {
207
236
this (createReader ());
208
237
}
209
238
210
- public JLineTextTerminal (ConsoleReader reader ) {
211
- if (reader == null ) throw new IllegalArgumentException ("reader is null" );
212
- reader .setHandleUserInterrupt (true );
239
+ public JLineTextTerminal (LineReaderImpl reader ) {
240
+ if (reader == null ) {
241
+ throw new IllegalArgumentException ("reader is null" );
242
+ }
213
243
this .reader = reader ;
214
244
215
245
TerminalProperties <JLineTextTerminal > props = getProperties ();
@@ -224,7 +254,8 @@ public JLineTextTerminal(ConsoleReader reader) {
224
254
props .addBooleanListener (PROP_INPUT_ITALIC , false , (term , newVal ) -> setInputItalic (newVal ));
225
255
props .addBooleanListener (PROP_INPUT_UNDERLINE , false , (term , newVal ) -> setInputUnderline (newVal ));
226
256
227
- props .addStringListener (PROP_ANSI_COLOR_MODE , AnsiColorMode .STANDARD .toString (), (term , newVal ) -> setAnsiColorMode (newVal ));
257
+ props .addStringListener (PROP_ANSI_COLOR_MODE , AnsiColorMode .STANDARD .toString (),
258
+ (term , newVal ) -> setAnsiColorMode (newVal ));
228
259
}
229
260
230
261
@ Override
@@ -233,20 +264,19 @@ public String read(boolean masking) {
233
264
try {
234
265
String prefix = "" ;
235
266
Character mask = masking ? '*' : null ;
236
- while (true ) {
267
+ while (true ) {
237
268
try {
238
269
String buffer = initialReadBuffer ;
239
270
initialReadBuffer = null ;
240
271
return prefix + reader .readLine (null , mask , buffer );
241
- } catch (UserInterruptException e ) {
272
+ } catch (UserInterruptException e ) {
242
273
userInterruptHandler .accept (this );
243
274
prefix = prefix + e .getPartialLine ();
244
- if (abortRead ) return prefix ;
275
+ if (abortRead ) {
276
+ return prefix ;
277
+ }
245
278
} catch (ReadInterruptionException e ) {
246
279
throw e ;
247
- } catch (IOException e ) {
248
- logger .error ("read error." , e );
249
- return "" ;
250
280
} catch (Exception e ) {
251
281
logger .error ("read error." , e );
252
282
}
@@ -259,7 +289,7 @@ public String read(boolean masking) {
259
289
@ Override
260
290
public void rawPrint (String message ) {
261
291
String msgPrefix = "" ;
262
- if (moveToLineStartRequired ) {
292
+ if (moveToLineStartRequired ) {
263
293
moveToLineStartRequired = false ;
264
294
msgPrefix = "\r " ;
265
295
}
@@ -269,10 +299,7 @@ public void rawPrint(String message) {
269
299
public void printAnsi (String message ) {
270
300
try {
271
301
reader .setPrompt (message );
272
- reader .drawLine ();
273
- reader .flush ();
274
- } catch (IOException e ) {
275
- logger .error ("print error." , e );
302
+ println ();
276
303
} finally {
277
304
reader .setPrompt (null );
278
305
}
@@ -288,23 +315,13 @@ public String getAnsiPrefix(StyleData styleData) {
288
315
289
316
@ Override
290
317
public void println () {
291
- try {
292
- reader .println ();
293
- reader .flush ();
294
- } catch (IOException e ) {
295
- logger .error ("println error." , e );
296
- }
318
+ reader .getTerminal ().writer ().println ();
319
+ reader .flush ();
297
320
}
298
321
299
322
@ Override
300
323
public boolean resetLine () {
301
- try {
302
- reader .resetPromptLine ("" , "" , 0 );
303
- return true ;
304
- } catch (IOException e ) {
305
- logger .error ("resetLine error." , e );
306
- return false ;
307
- }
324
+ return reader .redrawLine ();
308
325
}
309
326
310
327
@ Override
@@ -320,7 +337,7 @@ public boolean registerUserInterruptHandler(Consumer<JLineTextTerminal> handler,
320
337
return true ;
321
338
}
322
339
323
- private static class UserHandler implements ActionListener {
340
+ private static class UserHandler implements ActionListener , Binding {
324
341
private final JLineTextTerminal textTerminal ;
325
342
private final Function <JLineTextTerminal , ReadHandlerData > handler ;
326
343
@@ -331,16 +348,16 @@ private UserHandler(JLineTextTerminal textTerminal, Function<JLineTextTerminal,
331
348
332
349
@ Override
333
350
public void actionPerformed (ActionEvent e ) {
334
- CursorBuffer buf = textTerminal .reader .getCursorBuffer ();
335
- String partialInput = buf .buffer . toString ( );
351
+ Buffer buf = textTerminal .reader .getBuffer ();
352
+ String partialInput = buf .substring ( 0 );
336
353
buf .clear ();
337
354
338
355
ReadHandlerData handlerData = handler .apply (textTerminal );
339
356
ReadInterruptionStrategy .Action action = handlerData .getAction ();
340
- if (action == CONTINUE ) {
357
+ if (action == CONTINUE ) {
341
358
buf .write (partialInput );
342
359
} else {
343
- if (action == RESTART ) {
360
+ if (action == RESTART ) {
344
361
textTerminal .initialReadBuffer = partialInput ;
345
362
}
346
363
ReadInterruptionData interruptData = ReadInterruptionData .from (handlerData , partialInput );
@@ -352,24 +369,32 @@ public void actionPerformed(ActionEvent e) {
352
369
@ Override
353
370
public boolean registerHandler (String keyStroke , Function <JLineTextTerminal , ReadHandlerData > handler ) {
354
371
String keySeq = getKeySequence (keyStroke );
355
- if (keySeq == null ) return false ;
356
- reader .getKeys ().bind (keySeq , new UserHandler (this , handler ));
372
+ if (keySeq == null ) {
373
+ return false ;
374
+ }
375
+ reader .getKeys ().bind (new UserHandler (this , handler ), keySeq );
357
376
return true ;
358
377
}
359
378
360
379
@ Override
361
380
public void dispose (String resultData ) {
362
381
printAnsi (ANSI_RESET );
363
- reader .close ();
382
+ try {
383
+ reader .getTerminal ().close ();
384
+ } catch (IOException e ) {
385
+ }
364
386
}
365
387
366
388
@ Override
367
389
public void abort () {
368
390
printAnsi (ANSI_RESET );
369
- reader .close ();
391
+ try {
392
+ reader .getTerminal ().close ();
393
+ } catch (IOException e ) {
394
+ }
370
395
}
371
396
372
- public ConsoleReader getReader () {
397
+ public LineReaderImpl getReader () {
373
398
return reader ;
374
399
}
375
400
@@ -414,7 +439,7 @@ public void setInputUnderline(boolean underline) {
414
439
}
415
440
416
441
public void setAnsiColorMode (String mode ) {
417
- if (mode == null || mode .isEmpty ()) {
442
+ if (mode == null || mode .isEmpty ()) {
418
443
ansiColorMode = AnsiColorMode .STANDARD ;
419
444
return ;
420
445
}
@@ -428,18 +453,26 @@ public void setAnsiColorMode(String mode) {
428
453
429
454
public static String getKeySequence (String keyStroke ) {
430
455
KeyCombination kc = KeyCombination .of (keyStroke );
431
- if (kc == null ) return null ;
432
- if (kc .isTyped ()) return String .valueOf (kc .getChar ());
456
+ if (kc == null ) {
457
+ return null ;
458
+ }
459
+ if (kc .isTyped ()) {
460
+ return String .valueOf (kc .getChar ());
461
+ }
433
462
int code = kc .getCode ();
434
- if (code < 'A' || code > 'Z' ) return null ;
435
- if (kc .isCtrlDown ()) {
436
- if (kc .isAltDown ()) return null ;
437
- return String .valueOf ((char )(code + 1 -'A' ));
438
- } else if (kc .isAltDown ()) {
439
- if (!kc .isShiftDown ()) {
463
+ if (code < 'A' || code > 'Z' ) {
464
+ return null ;
465
+ }
466
+ if (kc .isCtrlDown ()) {
467
+ if (kc .isAltDown ()) {
468
+ return null ;
469
+ }
470
+ return String .valueOf ((char ) (code + 1 - 'A' ));
471
+ } else if (kc .isAltDown ()) {
472
+ if (!kc .isShiftDown ()) {
440
473
code += 32 ;
441
474
}
442
- return String .format ("%c%c" , (char )27 , (char )code );
475
+ return String .format ("%c%c" , (char ) 27 , (char ) code );
443
476
}
444
477
return null ;
445
478
}
0 commit comments