1 /*
2 * Copyright (c) 2002-2012, the original author or authors.
3 *
4 * This software is distributable under the BSD license. See the terms of the
5 * BSD license in the documentation provided with this software.
6 *
7 * http://www.opensource.org/licenses/bsd-license.php
8 */
9 package jline.console;
10
11 import java.awt.*;
12 import java.awt.datatransfer.Clipboard;
13 import java.awt.datatransfer.DataFlavor;
14 import java.awt.datatransfer.Transferable;
15 import java.awt.datatransfer.UnsupportedFlavorException;
16 import java.awt.event.ActionListener;
17 import java.io.BufferedReader;
18 import java.io.ByteArrayInputStream;
19 import java.io.ByteArrayOutputStream;
20 import java.io.File;
21 import java.io.FileDescriptor;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.io.OutputStreamWriter;
27 import java.io.Reader;
28 import java.io.Writer;
29 import java.net.URL;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.ListIterator;
37 import java.util.Map;
38 import java.util.ResourceBundle;
39 import java.util.Stack;
40
41 import jline.Terminal;
42 import jline.TerminalFactory;
43 import jline.UnixTerminal;
44 import jline.console.completer.CandidateListCompletionHandler;
45 import jline.console.completer.Completer;
46 import jline.console.completer.CompletionHandler;
47 import jline.console.history.History;
48 import jline.console.history.MemoryHistory;
49 import jline.internal.Configuration;
50 import jline.internal.InputStreamReader;
51 import jline.internal.Log;
52 import jline.internal.NonBlockingInputStream;
53 import jline.internal.Nullable;
54 import jline.internal.Urls;
55 import org.fusesource.jansi.AnsiOutputStream;
56
57 import static jline.internal.Preconditions.checkNotNull;
58
59 /**
60 * A reader for console applications. It supports custom tab-completion,
61 * saveable command history, and command line editing. On some platforms,
62 * platform-specific commands will need to be issued before the reader will
63 * function properly. See {@link jline.Terminal#init} for convenience
64 * methods for issuing platform-specific setup commands.
65 *
66 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
67 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
68 * @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
69 */
70 public class ConsoleReader
71 {
72 public static final String JLINE_NOBELL = "jline.nobell";
73
74 public static final String JLINE_ESC_TIMEOUT = "jline.esc.timeout";
75
76 public static final String JLINE_INPUTRC = "jline.inputrc";
77
498 // FIXME: does not handle anything but a line with a prompt absolute position
499 return promptLen + buf.cursor;
500 }
501
502 /**
503 * Returns the text after the last '\n'.
504 * prompt is returned if no '\n' characters are present.
505 * null is returned if prompt is null.
506 */
507 private String lastLine(String str) {
508 if (str == null) return "";
509 int last = str.lastIndexOf("\n");
510
511 if (last >= 0) {
512 return str.substring(last + 1, str.length());
513 }
514
515 return str;
516 }
517
518 private String stripAnsi(String str) {
519 if (str == null) return "";
520 try {
521 ByteArrayOutputStream baos = new ByteArrayOutputStream();
522 AnsiOutputStream aos = new AnsiOutputStream(baos);
523 aos.write(str.getBytes());
524 aos.flush();
525 return baos.toString();
526 } catch (IOException e) {
527 return str;
528 }
529 }
530
531 /**
532 * Move the cursor position to the specified absolute index.
533 */
534 public final boolean setCursorPosition(final int position) throws IOException {
535 if (position == buf.cursor) {
536 return true;
537 }
538
539 return moveCursor(position - buf.cursor) != 0;
540 }
541
542 /**
543 * Set the current buffer's content to the specified {@link String}. The
544 * visual console will be modified to show the current buffer.
545 *
546 * @param buffer the new contents of the buffer.
547 */
548 private void setBuffer(final String buffer) throws IOException {
549 // don't bother modifying it if it is unchanged
648 if (mask == null && isHistoryEnabled()) {
649 history.add(historyLine);
650 }
651 else {
652 mask = null;
653 }
654 }
655
656 history.moveToEnd();
657
658 buf.buffer.setLength(0);
659 buf.cursor = 0;
660
661 return str;
662 }
663
664 /**
665 * Expand event designator such as !!, !#, !3, etc...
666 * See http://www.gnu.org/software/bash/manual/html_node/Event-Designators.html
667 */
668 protected String expandEvents(String str) throws IOException {
669 StringBuilder sb = new StringBuilder();
670 for (int i = 0; i < str.length(); i++) {
671 char c = str.charAt(i);
672 switch (c) {
673 case '\\':
674 // any '\!' should be considered an expansion escape, so skip expansion and strip the escape character
675 // a leading '\^' should be considered an expansion escape, so skip expansion and strip the escape character
676 // otherwise, add the escape
677 if (i + 1 < str.length()) {
678 char nextChar = str.charAt(i+1);
679 if (nextChar == '!' || (nextChar == '^' && i == 0)) {
680 c = nextChar;
681 i++;
682 }
683 }
684 sb.append(c);
685 break;
686 case '!':
687 if (i + 1 < str.length()) {
1350 }
1351
1352 searchChar = charSearchChar;
1353 }
1354 else {
1355 charSearchChar = searchChar;
1356 charSearchFirstInvokeChar = (char) invokeChar;
1357 }
1358
1359 charSearchLastInvokeChar = (char)invokeChar;
1360
1361 isForward = Character.isLowerCase(charSearchFirstInvokeChar);
1362 stopBefore = (Character.toLowerCase(charSearchFirstInvokeChar) == 't');
1363
1364 boolean ok = false;
1365
1366 if (isForward) {
1367 while (count-- > 0) {
1368 int pos = buf.cursor + 1;
1369 while (pos < buf.buffer.length()) {
1370 if (buf.buffer.charAt(pos) == (char) searchChar) {
1371 setCursorPosition(pos);
1372 ok = true;
1373 break;
1374 }
1375 ++pos;
1376 }
1377 }
1378
1379 if (ok) {
1380 if (stopBefore)
1381 moveCursor(-1);
1382
1383 /*
1384 * When in yank-to, move-to, del-to state we actually want to
1385 * go to the character after the one we landed on to make sure
1386 * that the character we ended up on is included in the
1387 * operation
1388 */
1389 if (isInViMoveOperationState()) {
1390 moveCursor(1);
1391 }
1392 }
1393 }
1394 else {
1395 while (count-- > 0) {
1396 int pos = buf.cursor - 1;
1397 while (pos >= 0) {
1398 if (buf.buffer.charAt(pos) == (char) searchChar) {
1399 setCursorPosition(pos);
1400 ok = true;
1401 break;
1402 }
1403 --pos;
1404 }
1405 }
1406
1407 if (ok && stopBefore)
1408 moveCursor(1);
1409 }
1410
1411 return ok;
1412 }
1413
1414 private char switchCase(char ch) {
1415 if (Character.isUpperCase(ch)) {
1416 return Character.toLowerCase(ch);
1417 }
1418 return Character.toUpperCase(ch);
1593 private boolean insert(int count, final CharSequence str) throws IOException {
1594 for (int i = 0; i < count; i++) {
1595 buf.write(str);
1596 if (mask == null) {
1597 // no masking
1598 print(str);
1599 } else if (mask == NULL_MASK) {
1600 // don't print anything
1601 } else {
1602 print(mask, str.length());
1603 }
1604 }
1605 drawBuffer();
1606 return true;
1607 }
1608
1609 /**
1610 * Implements vi search ("/" or "?").
1611 * @throws IOException
1612 */
1613 private int viSearch(char searchChar) throws IOException {
1614 boolean isForward = (searchChar == '/');
1615
1616 /*
1617 * This is a little gross, I'm sure there is a more appropriate way
1618 * of saving and restoring state.
1619 */
1620 CursorBuffer origBuffer = buf.copy();
1621
1622 // Clear the contents of the current line and
1623 setCursorPosition (0);
1624 killLine();
1625
1626 // Our new "prompt" is the character that got us into search mode.
1627 putString(Character.toString(searchChar));
1628 flush();
1629
1630 boolean isAborted = false;
1631 boolean isComplete = false;
1632
2892
2893 case CALL_LAST_KBD_MACRO:
2894 for (int i = 0; i < macro.length(); i++) {
2895 pushBackChar.push(macro.charAt(macro.length() - 1 - i));
2896 }
2897 sb.setLength( 0 );
2898 break;
2899
2900 case VI_EDITING_MODE:
2901 consoleKeys.setKeyMap(KeyMap.VI_INSERT);
2902 break;
2903
2904 case VI_MOVEMENT_MODE:
2905 /*
2906 * If we are re-entering move mode from an
2907 * aborted yank-to, delete-to, change-to then
2908 * don't move the cursor back. The cursor is
2909 * only move on an expclit entry to movement
2910 * mode.
2911 */
2912 if (state == state.NORMAL) {
2913 moveCursor(-1);
2914 }
2915 consoleKeys.setKeyMap(KeyMap.VI_MOVE);
2916 break;
2917
2918 case VI_INSERTION_MODE:
2919 consoleKeys.setKeyMap(KeyMap.VI_INSERT);
2920 break;
2921
2922 case VI_APPEND_MODE:
2923 moveCursor(1);
2924 consoleKeys.setKeyMap(KeyMap.VI_INSERT);
2925 break;
2926
2927 case VI_APPEND_EOL:
2928 success = moveToEnd();
2929 consoleKeys.setKeyMap(KeyMap.VI_INSERT);
2930 break;
2931
2932 /*
3583 public boolean paste() throws IOException {
3584 Clipboard clipboard;
3585 try { // May throw ugly exception on system without X
3586 clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
3587 }
3588 catch (Exception e) {
3589 return false;
3590 }
3591
3592 if (clipboard == null) {
3593 return false;
3594 }
3595
3596 Transferable transferable = clipboard.getContents(null);
3597
3598 if (transferable == null) {
3599 return false;
3600 }
3601
3602 try {
3603 Object content = transferable.getTransferData(DataFlavor.plainTextFlavor);
3604
3605 // This fix was suggested in bug #1060649 at
3606 // http://sourceforge.net/tracker/index.php?func=detail&aid=1060649&group_id=64033&atid=506056
3607 // to get around the deprecated DataFlavor.plainTextFlavor, but it
3608 // raises a UnsupportedFlavorException on Mac OS X
3609
3610 if (content == null) {
3611 try {
3612 content = new DataFlavor().getReaderForText(transferable);
3613 }
3614 catch (Exception e) {
3615 // ignore
3616 }
3617 }
3618
3619 if (content == null) {
3620 return false;
3621 }
3622
|
1 /*
2 * Copyright (c) 2002-2012, the original author or authors.
3 *
4 * This software is distributable under the BSD license. See the terms of the
5 * BSD license in the documentation provided with this software.
6 *
7 * http://www.opensource.org/licenses/bsd-license.php
8 */
9 package jdk.internal.jline.console;
10
11 import java.awt.*;
12 import java.awt.datatransfer.Clipboard;
13 import java.awt.datatransfer.DataFlavor;
14 import java.awt.datatransfer.Transferable;
15 import java.awt.datatransfer.UnsupportedFlavorException;
16 import java.awt.event.ActionListener;
17 import java.io.BufferedReader;
18 import java.io.ByteArrayInputStream;
19 import java.io.ByteArrayOutputStream;
20 import java.io.File;
21 import java.io.FileDescriptor;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.io.OutputStreamWriter;
27 import java.io.Reader;
28 import java.io.Writer;
29 import java.net.URL;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.ListIterator;
37 import java.util.Map;
38 import java.util.ResourceBundle;
39 import java.util.Stack;
40 import java.util.regex.Pattern;
41
42 import jdk.internal.jline.Terminal;
43 import jdk.internal.jline.TerminalFactory;
44 import jdk.internal.jline.UnixTerminal;
45 import jdk.internal.jline.console.completer.CandidateListCompletionHandler;
46 import jdk.internal.jline.console.completer.Completer;
47 import jdk.internal.jline.console.completer.CompletionHandler;
48 import jdk.internal.jline.console.history.History;
49 import jdk.internal.jline.console.history.MemoryHistory;
50 import jdk.internal.jline.internal.Configuration;
51 import jdk.internal.jline.internal.InputStreamReader;
52 import jdk.internal.jline.internal.Log;
53 import jdk.internal.jline.internal.NonBlockingInputStream;
54 import jdk.internal.jline.internal.Nullable;
55 import jdk.internal.jline.internal.Urls;
56 //import org.fusesource.jansi.AnsiOutputStream;
57
58 import static jdk.internal.jline.internal.Preconditions.checkNotNull;
59
60 /**
61 * A reader for console applications. It supports custom tab-completion,
62 * saveable command history, and command line editing. On some platforms,
63 * platform-specific commands will need to be issued before the reader will
64 * function properly. See {@link jline.Terminal#init} for convenience
65 * methods for issuing platform-specific setup commands.
66 *
67 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
68 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
69 * @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
70 */
71 public class ConsoleReader
72 {
73 public static final String JLINE_NOBELL = "jline.nobell";
74
75 public static final String JLINE_ESC_TIMEOUT = "jline.esc.timeout";
76
77 public static final String JLINE_INPUTRC = "jline.inputrc";
78
499 // FIXME: does not handle anything but a line with a prompt absolute position
500 return promptLen + buf.cursor;
501 }
502
503 /**
504 * Returns the text after the last '\n'.
505 * prompt is returned if no '\n' characters are present.
506 * null is returned if prompt is null.
507 */
508 private String lastLine(String str) {
509 if (str == null) return "";
510 int last = str.lastIndexOf("\n");
511
512 if (last >= 0) {
513 return str.substring(last + 1, str.length());
514 }
515
516 return str;
517 }
518
519 String stripAnsi(String str) {
520 if (str == null) return "";
521 return ANSI_CODE_PATTERN.matcher(str).replaceAll("");
522 // try {
523 // ByteArrayOutputStream baos = new ByteArrayOutputStream();
524 // AnsiOutputStream aos = new AnsiOutputStream(baos);
525 // aos.write(str.getBytes());
526 // aos.flush();
527 // return baos.toString();
528 // } catch (IOException e) {
529 // return str;
530 // }
531 }
532 //where:
533 private static final Pattern ANSI_CODE_PATTERN = Pattern.compile("\033\\[[^@-~]*[@-~]");
534
535 /**
536 * Move the cursor position to the specified absolute index.
537 */
538 public final boolean setCursorPosition(final int position) throws IOException {
539 if (position == buf.cursor) {
540 return true;
541 }
542
543 return moveCursor(position - buf.cursor) != 0;
544 }
545
546 /**
547 * Set the current buffer's content to the specified {@link String}. The
548 * visual console will be modified to show the current buffer.
549 *
550 * @param buffer the new contents of the buffer.
551 */
552 private void setBuffer(final String buffer) throws IOException {
553 // don't bother modifying it if it is unchanged
652 if (mask == null && isHistoryEnabled()) {
653 history.add(historyLine);
654 }
655 else {
656 mask = null;
657 }
658 }
659
660 history.moveToEnd();
661
662 buf.buffer.setLength(0);
663 buf.cursor = 0;
664
665 return str;
666 }
667
668 /**
669 * Expand event designator such as !!, !#, !3, etc...
670 * See http://www.gnu.org/software/bash/manual/html_node/Event-Designators.html
671 */
672 @SuppressWarnings("fallthrough")
673 protected String expandEvents(String str) throws IOException {
674 StringBuilder sb = new StringBuilder();
675 for (int i = 0; i < str.length(); i++) {
676 char c = str.charAt(i);
677 switch (c) {
678 case '\\':
679 // any '\!' should be considered an expansion escape, so skip expansion and strip the escape character
680 // a leading '\^' should be considered an expansion escape, so skip expansion and strip the escape character
681 // otherwise, add the escape
682 if (i + 1 < str.length()) {
683 char nextChar = str.charAt(i+1);
684 if (nextChar == '!' || (nextChar == '^' && i == 0)) {
685 c = nextChar;
686 i++;
687 }
688 }
689 sb.append(c);
690 break;
691 case '!':
692 if (i + 1 < str.length()) {
1355 }
1356
1357 searchChar = charSearchChar;
1358 }
1359 else {
1360 charSearchChar = searchChar;
1361 charSearchFirstInvokeChar = (char) invokeChar;
1362 }
1363
1364 charSearchLastInvokeChar = (char)invokeChar;
1365
1366 isForward = Character.isLowerCase(charSearchFirstInvokeChar);
1367 stopBefore = (Character.toLowerCase(charSearchFirstInvokeChar) == 't');
1368
1369 boolean ok = false;
1370
1371 if (isForward) {
1372 while (count-- > 0) {
1373 int pos = buf.cursor + 1;
1374 while (pos < buf.buffer.length()) {
1375 if (buf.buffer.charAt(pos) == searchChar) {
1376 setCursorPosition(pos);
1377 ok = true;
1378 break;
1379 }
1380 ++pos;
1381 }
1382 }
1383
1384 if (ok) {
1385 if (stopBefore)
1386 moveCursor(-1);
1387
1388 /*
1389 * When in yank-to, move-to, del-to state we actually want to
1390 * go to the character after the one we landed on to make sure
1391 * that the character we ended up on is included in the
1392 * operation
1393 */
1394 if (isInViMoveOperationState()) {
1395 moveCursor(1);
1396 }
1397 }
1398 }
1399 else {
1400 while (count-- > 0) {
1401 int pos = buf.cursor - 1;
1402 while (pos >= 0) {
1403 if (buf.buffer.charAt(pos) == searchChar) {
1404 setCursorPosition(pos);
1405 ok = true;
1406 break;
1407 }
1408 --pos;
1409 }
1410 }
1411
1412 if (ok && stopBefore)
1413 moveCursor(1);
1414 }
1415
1416 return ok;
1417 }
1418
1419 private char switchCase(char ch) {
1420 if (Character.isUpperCase(ch)) {
1421 return Character.toLowerCase(ch);
1422 }
1423 return Character.toUpperCase(ch);
1598 private boolean insert(int count, final CharSequence str) throws IOException {
1599 for (int i = 0; i < count; i++) {
1600 buf.write(str);
1601 if (mask == null) {
1602 // no masking
1603 print(str);
1604 } else if (mask == NULL_MASK) {
1605 // don't print anything
1606 } else {
1607 print(mask, str.length());
1608 }
1609 }
1610 drawBuffer();
1611 return true;
1612 }
1613
1614 /**
1615 * Implements vi search ("/" or "?").
1616 * @throws IOException
1617 */
1618 @SuppressWarnings("fallthrough")
1619 private int viSearch(char searchChar) throws IOException {
1620 boolean isForward = (searchChar == '/');
1621
1622 /*
1623 * This is a little gross, I'm sure there is a more appropriate way
1624 * of saving and restoring state.
1625 */
1626 CursorBuffer origBuffer = buf.copy();
1627
1628 // Clear the contents of the current line and
1629 setCursorPosition (0);
1630 killLine();
1631
1632 // Our new "prompt" is the character that got us into search mode.
1633 putString(Character.toString(searchChar));
1634 flush();
1635
1636 boolean isAborted = false;
1637 boolean isComplete = false;
1638
2898
2899 case CALL_LAST_KBD_MACRO:
2900 for (int i = 0; i < macro.length(); i++) {
2901 pushBackChar.push(macro.charAt(macro.length() - 1 - i));
2902 }
2903 sb.setLength( 0 );
2904 break;
2905
2906 case VI_EDITING_MODE:
2907 consoleKeys.setKeyMap(KeyMap.VI_INSERT);
2908 break;
2909
2910 case VI_MOVEMENT_MODE:
2911 /*
2912 * If we are re-entering move mode from an
2913 * aborted yank-to, delete-to, change-to then
2914 * don't move the cursor back. The cursor is
2915 * only move on an expclit entry to movement
2916 * mode.
2917 */
2918 if (state == State.NORMAL) {
2919 moveCursor(-1);
2920 }
2921 consoleKeys.setKeyMap(KeyMap.VI_MOVE);
2922 break;
2923
2924 case VI_INSERTION_MODE:
2925 consoleKeys.setKeyMap(KeyMap.VI_INSERT);
2926 break;
2927
2928 case VI_APPEND_MODE:
2929 moveCursor(1);
2930 consoleKeys.setKeyMap(KeyMap.VI_INSERT);
2931 break;
2932
2933 case VI_APPEND_EOL:
2934 success = moveToEnd();
2935 consoleKeys.setKeyMap(KeyMap.VI_INSERT);
2936 break;
2937
2938 /*
3589 public boolean paste() throws IOException {
3590 Clipboard clipboard;
3591 try { // May throw ugly exception on system without X
3592 clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
3593 }
3594 catch (Exception e) {
3595 return false;
3596 }
3597
3598 if (clipboard == null) {
3599 return false;
3600 }
3601
3602 Transferable transferable = clipboard.getContents(null);
3603
3604 if (transferable == null) {
3605 return false;
3606 }
3607
3608 try {
3609 @SuppressWarnings("deprecation")
3610 Object content = transferable.getTransferData(DataFlavor.plainTextFlavor);
3611
3612 // This fix was suggested in bug #1060649 at
3613 // http://sourceforge.net/tracker/index.php?func=detail&aid=1060649&group_id=64033&atid=506056
3614 // to get around the deprecated DataFlavor.plainTextFlavor, but it
3615 // raises a UnsupportedFlavorException on Mac OS X
3616
3617 if (content == null) {
3618 try {
3619 content = new DataFlavor().getReaderForText(transferable);
3620 }
3621 catch (Exception e) {
3622 // ignore
3623 }
3624 }
3625
3626 if (content == null) {
3627 return false;
3628 }
3629
|