< prev index next >

jdk/src/jdk.jline/share/classes/jdk/internal/jline/console/ConsoleReader.java

Print this page


   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 


< prev index next >