src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java
changeset 58903 eeb1c0da2126
parent 52938 5ff7480c9e28
equal deleted inserted replaced
58902:197238c30630 58903:eeb1c0da2126
     1 /*
     1 /*
     2  * Copyright (c) 2002-2018, the original author or authors.
     2  * Copyright (c) 2002-2019, the original author or authors.
     3  *
     3  *
     4  * This software is distributable under the BSD license. See the terms of the
     4  * This software is distributable under the BSD license. See the terms of the
     5  * BSD license in the documentation provided with this software.
     5  * BSD license in the documentation provided with this software.
     6  *
     6  *
     7  * http://www.opensource.org/licenses/bsd-license.php
     7  * https://opensource.org/licenses/BSD-3-Clause
     8  */
     8  */
     9 package jdk.internal.org.jline.reader.impl;
     9 package jdk.internal.org.jline.reader.impl;
    10 
    10 
    11 import java.io.Flushable;
    11 import java.io.Flushable;
    12 import java.io.IOError;
    12 import java.io.IOError;
    15 import java.io.InterruptedIOException;
    15 import java.io.InterruptedIOException;
    16 import java.time.Instant;
    16 import java.time.Instant;
    17 import java.util.*;
    17 import java.util.*;
    18 import java.util.Map.Entry;
    18 import java.util.Map.Entry;
    19 import java.util.concurrent.atomic.AtomicBoolean;
    19 import java.util.concurrent.atomic.AtomicBoolean;
       
    20 import java.util.concurrent.locks.Lock;
       
    21 import java.util.concurrent.locks.ReentrantLock;
    20 import java.util.function.*;
    22 import java.util.function.*;
    21 import java.util.regex.Matcher;
    23 import java.util.regex.Matcher;
    22 import java.util.regex.Pattern;
    24 import java.util.regex.Pattern;
    23 import java.util.stream.Collectors;
    25 import java.util.stream.Collectors;
       
    26 import java.util.stream.Stream;
    24 import java.util.stream.StreamSupport;
    27 import java.util.stream.StreamSupport;
    25 
    28 
    26 import jdk.internal.org.jline.keymap.BindingReader;
    29 import jdk.internal.org.jline.keymap.BindingReader;
    27 import jdk.internal.org.jline.keymap.KeyMap;
    30 import jdk.internal.org.jline.keymap.KeyMap;
    28 import jdk.internal.org.jline.reader.*;
    31 import jdk.internal.org.jline.reader.*;
    30 import jdk.internal.org.jline.reader.impl.history.DefaultHistory;
    33 import jdk.internal.org.jline.reader.impl.history.DefaultHistory;
    31 import jdk.internal.org.jline.terminal.*;
    34 import jdk.internal.org.jline.terminal.*;
    32 import jdk.internal.org.jline.terminal.Attributes.ControlChar;
    35 import jdk.internal.org.jline.terminal.Attributes.ControlChar;
    33 import jdk.internal.org.jline.terminal.Terminal.Signal;
    36 import jdk.internal.org.jline.terminal.Terminal.Signal;
    34 import jdk.internal.org.jline.terminal.Terminal.SignalHandler;
    37 import jdk.internal.org.jline.terminal.Terminal.SignalHandler;
       
    38 import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal;
    35 import jdk.internal.org.jline.utils.AttributedString;
    39 import jdk.internal.org.jline.utils.AttributedString;
    36 import jdk.internal.org.jline.utils.AttributedStringBuilder;
    40 import jdk.internal.org.jline.utils.AttributedStringBuilder;
    37 import jdk.internal.org.jline.utils.AttributedStyle;
    41 import jdk.internal.org.jline.utils.AttributedStyle;
    38 import jdk.internal.org.jline.utils.Curses;
    42 import jdk.internal.org.jline.utils.Curses;
    39 import jdk.internal.org.jline.utils.Display;
    43 import jdk.internal.org.jline.utils.Display;
   157 
   161 
   158     protected final Buffer buf = new BufferImpl();
   162     protected final Buffer buf = new BufferImpl();
   159 
   163 
   160     protected final Size size = new Size();
   164     protected final Size size = new Size();
   161 
   165 
   162     protected AttributedString prompt;
   166     protected AttributedString prompt = AttributedString.EMPTY;
   163     protected AttributedString rightPrompt;
   167     protected AttributedString rightPrompt = AttributedString.EMPTY;
   164 
   168 
   165     protected MaskingCallback maskingCallback;
   169     protected MaskingCallback maskingCallback;
   166 
   170 
   167     protected Map<Integer, String> modifiedHistory = new HashMap<>();
   171     protected Map<Integer, String> modifiedHistory = new HashMap<>();
   168     protected Buffer historyBuffer = null;
   172     protected Buffer historyBuffer = null;
   208     protected KillRing killRing = new KillRing();
   212     protected KillRing killRing = new KillRing();
   209 
   213 
   210     protected UndoTree<Buffer> undo = new UndoTree<>(this::setBuffer);
   214     protected UndoTree<Buffer> undo = new UndoTree<>(this::setBuffer);
   211     protected boolean isUndo;
   215     protected boolean isUndo;
   212 
   216 
       
   217     /**
       
   218      * State lock
       
   219      */
       
   220     protected final ReentrantLock lock = new ReentrantLock();
   213     /*
   221     /*
   214      * Current internal state of the line reader
   222      * Current internal state of the line reader
   215      */
   223      */
   216     protected State   state = State.DONE;
   224     protected State   state = State.DONE;
   217     protected final AtomicBoolean startedReading = new AtomicBoolean();
   225     protected final AtomicBoolean startedReading = new AtomicBoolean();
   237 
   245 
   238     protected String keyMap;
   246     protected String keyMap;
   239 
   247 
   240     protected int smallTerminalOffset = 0;
   248     protected int smallTerminalOffset = 0;
   241 
   249 
       
   250     /*
       
   251      * accept-and-infer-next-history, accept-and-hold & accept-line-and-down-history
       
   252      */
       
   253     protected boolean nextCommandFromHistory = false;
       
   254     protected int nextHistoryId = -1;
   242 
   255 
   243 
   256 
   244     public LineReaderImpl(Terminal terminal) throws IOException {
   257     public LineReaderImpl(Terminal terminal) throws IOException {
   245         this(terminal, null, null);
   258         this(terminal, null, null);
   246     }
   259     }
   264         this.keyMaps = defaultKeyMaps();
   277         this.keyMaps = defaultKeyMaps();
   265 
   278 
   266         builtinWidgets = builtinWidgets();
   279         builtinWidgets = builtinWidgets();
   267         widgets = new HashMap<>(builtinWidgets);
   280         widgets = new HashMap<>(builtinWidgets);
   268         bindingReader = new BindingReader(terminal.reader());
   281         bindingReader = new BindingReader(terminal.reader());
       
   282         doDisplay();
   269     }
   283     }
   270 
   284 
   271     public Terminal getTerminal() {
   285     public Terminal getTerminal() {
   272         return terminal;
   286         return terminal;
   273     }
   287     }
   465         Thread readLineThread = Thread.currentThread();
   479         Thread readLineThread = Thread.currentThread();
   466         SignalHandler previousIntrHandler = null;
   480         SignalHandler previousIntrHandler = null;
   467         SignalHandler previousWinchHandler = null;
   481         SignalHandler previousWinchHandler = null;
   468         SignalHandler previousContHandler = null;
   482         SignalHandler previousContHandler = null;
   469         Attributes originalAttributes = null;
   483         Attributes originalAttributes = null;
   470         boolean dumb = Terminal.TYPE_DUMB.equals(terminal.getType())
   484         boolean dumb = isTerminalDumb();
   471                     || Terminal.TYPE_DUMB_COLOR.equals(terminal.getType());
       
   472         try {
   485         try {
   473 
   486 
   474             this.maskingCallback = maskingCallback;
   487             this.maskingCallback = maskingCallback;
   475 
   488 
   476             /*
   489             /*
   493             setRightPrompt(rightPrompt);
   506             setRightPrompt(rightPrompt);
   494             buf.clear();
   507             buf.clear();
   495             if (buffer != null) {
   508             if (buffer != null) {
   496                 buf.write(buffer);
   509                 buf.write(buffer);
   497             }
   510             }
       
   511             if (nextCommandFromHistory && nextHistoryId > 0) {
       
   512                 if (history.size() > nextHistoryId) {
       
   513                     history.moveTo(nextHistoryId);
       
   514                 } else {
       
   515                     history.moveTo(history.last());
       
   516                 }
       
   517                 buf.write(history.current());
       
   518             } else {
       
   519                 nextHistoryId = -1;
       
   520             }
       
   521             nextCommandFromHistory = false;
   498             undo.clear();
   522             undo.clear();
   499             parsedLine = null;
   523             parsedLine = null;
   500             keyMap = MAIN;
   524             keyMap = MAIN;
   501 
   525 
   502             if (history != null) {
   526             if (history != null) {
   503                 history.attach(this);
   527                 history.attach(this);
   504             }
   528             }
   505 
   529 
   506             synchronized (this) {
   530             try {
       
   531                 lock.lock();
       
   532 
   507                 this.reading = true;
   533                 this.reading = true;
   508 
   534 
   509                 previousIntrHandler = terminal.handle(Signal.INT, signal -> readLineThread.interrupt());
   535                 previousIntrHandler = terminal.handle(Signal.INT, signal -> readLineThread.interrupt());
   510                 previousWinchHandler = terminal.handle(Signal.WINCH, this::handleSignal);
   536                 previousWinchHandler = terminal.handle(Signal.WINCH, this::handleSignal);
   511                 previousContHandler = terminal.handle(Signal.CONT, this::handleSignal);
   537                 previousContHandler = terminal.handle(Signal.CONT, this::handleSignal);
   512                 originalAttributes = terminal.enterRawMode();
   538                 originalAttributes = terminal.enterRawMode();
   513 
   539 
   514                 // Cache terminal size for the duration of the call to readLine()
   540                 doDisplay();
   515                 // It will eventually be updated with WINCH signals
       
   516                 size.copy(terminal.getSize());
       
   517 
       
   518                 display = new Display(terminal, false);
       
   519                 if (size.getRows() == 0 || size.getColumns() == 0) {
       
   520                     display.resize(1, Integer.MAX_VALUE);
       
   521                 } else {
       
   522                     display.resize(size.getRows(), size.getColumns());
       
   523                 }
       
   524                 if (isSet(Option.DELAY_LINE_WRAP))
       
   525                     display.setDelayLineWrap(true);
       
   526 
   541 
   527                 // Move into application mode
   542                 // Move into application mode
   528                 if (!dumb) {
   543                 if (!dumb) {
   529                     terminal.puts(Capability.keypad_xmit);
   544                     terminal.puts(Capability.keypad_xmit);
   530                     if (isSet(Option.AUTO_FRESH_LINE))
   545                     if (isSet(Option.AUTO_FRESH_LINE))
   545                 undo.newState(buf.copy());
   560                 undo.newState(buf.copy());
   546 
   561 
   547                 // Draw initial prompt
   562                 // Draw initial prompt
   548                 redrawLine();
   563                 redrawLine();
   549                 redisplay();
   564                 redisplay();
       
   565             } finally {
       
   566                 lock.unlock();
   550             }
   567             }
   551 
   568 
   552             while (true) {
   569             while (true) {
   553 
   570 
   554                 KeyMap<Binding> local = null;
   571                 KeyMap<Binding> local = null;
   576                 // Reset region after a paste
   593                 // Reset region after a paste
   577                 if (regionActive == RegionType.PASTE) {
   594                 if (regionActive == RegionType.PASTE) {
   578                     regionActive = RegionType.NONE;
   595                     regionActive = RegionType.NONE;
   579                 }
   596                 }
   580 
   597 
   581                 synchronized (this) {
   598                 try {
       
   599                     lock.lock();
   582                     // Get executable widget
   600                     // Get executable widget
   583                     Buffer copy = buf.copy();
   601                     Buffer copy = buf.copy();
   584                     Widget w = getWidget(o);
   602                     Widget w = getWidget(o);
   585                     if (!w.apply()) {
   603                     if (!w.apply()) {
   586                         beep();
   604                         beep();
   608                     }
   626                     }
   609 
   627 
   610                     if (!dumb) {
   628                     if (!dumb) {
   611                         redisplay();
   629                         redisplay();
   612                     }
   630                     }
       
   631                 } finally {
       
   632                     lock.unlock();
   613                 }
   633                 }
   614             }
   634             }
   615         } catch (IOError e) {
   635         } catch (IOError e) {
   616             if (e.getCause() instanceof InterruptedIOException) {
   636             if (e.getCause() instanceof InterruptedIOException) {
   617                 throw new UserInterruptException(buf.toString());
   637                 throw new UserInterruptException(buf.toString());
   618             } else {
   638             } else {
   619                 throw e;
   639                 throw e;
   620             }
   640             }
   621         }
   641         }
   622         finally {
   642         finally {
   623             synchronized (this) {
   643             try {
       
   644                 lock.lock();
       
   645 
   624                 this.reading = false;
   646                 this.reading = false;
   625 
   647 
   626                 cleanup();
   648                 cleanup();
   627                 if (originalAttributes != null) {
   649                 if (originalAttributes != null) {
   628                     terminal.setAttributes(originalAttributes);
   650                     terminal.setAttributes(originalAttributes);
   634                     terminal.handle(Signal.WINCH, previousWinchHandler);
   656                     terminal.handle(Signal.WINCH, previousWinchHandler);
   635                 }
   657                 }
   636                 if (previousContHandler != null) {
   658                 if (previousContHandler != null) {
   637                     terminal.handle(Signal.CONT, previousContHandler);
   659                     terminal.handle(Signal.CONT, previousContHandler);
   638                 }
   660                 }
       
   661             } finally {
       
   662                 lock.unlock();
   639             }
   663             }
   640             startedReading.set(false);
   664             startedReading.set(false);
   641         }
   665         }
   642     }
   666     }
   643 
   667 
       
   668     private boolean isTerminalDumb(){
       
   669         return Terminal.TYPE_DUMB.equals(terminal.getType())
       
   670                 || Terminal.TYPE_DUMB_COLOR.equals(terminal.getType());
       
   671     }
       
   672 
       
   673     private void doDisplay(){
       
   674         // Cache terminal size for the duration of the call to readLine()
       
   675         // It will eventually be updated with WINCH signals
       
   676         size.copy(terminal.getBufferSize());
       
   677 
       
   678         display = new Display(terminal, false);
       
   679         if (size.getRows() == 0 || size.getColumns() == 0) {
       
   680             display.resize(1, Integer.MAX_VALUE);
       
   681         } else {
       
   682             display.resize(size.getRows(), size.getColumns());
       
   683         }
       
   684         if (isSet(Option.DELAY_LINE_WRAP))
       
   685             display.setDelayLineWrap(true);
       
   686     }
       
   687 
   644     @Override
   688     @Override
   645     public synchronized void printAbove(String str) {
   689     public void printAbove(String str) {
   646         boolean reading = this.reading;
   690         try {
   647         if (reading) {
   691             lock.lock();
   648             display.update(Collections.emptyList(), 0);
   692 
   649         }
   693             boolean reading = this.reading;
   650         if (str.endsWith("\n")) {
   694             if (reading) {
   651             terminal.writer().print(str);
   695                 display.update(Collections.emptyList(), 0);
   652         } else {
   696             }
   653             terminal.writer().println(str);
   697             if (str.endsWith("\n") || str.endsWith("\n\033[m") || str.endsWith("\n\033[0m")) {
   654         }
   698                 terminal.writer().print(str);
   655         if (reading) {
   699             } else {
   656             redisplay(false);
   700                 terminal.writer().println(str);
   657         }
   701             }
   658         terminal.flush();
   702             if (reading) {
       
   703                 redisplay(false);
       
   704             }
       
   705             terminal.flush();
       
   706         } finally {
       
   707             lock.unlock();
       
   708         }
   659     }
   709     }
   660 
   710 
   661     @Override
   711     @Override
   662     public void printAbove(AttributedString str) {
   712     public void printAbove(AttributedString str) {
   663         printAbove(str.toAnsi(terminal));
   713         printAbove(str.toAnsi(terminal));
   664     }
   714     }
   665 
   715 
   666     @Override
   716     @Override
   667     public synchronized boolean isReading() {
   717     public boolean isReading() {
   668         return reading;
   718         try {
       
   719             lock.lock();
       
   720             return reading;
       
   721         } finally {
       
   722             lock.unlock();
       
   723         }
   669     }
   724     }
   670 
   725 
   671     /* Make sure we position the cursor on column 0 */
   726     /* Make sure we position the cursor on column 0 */
   672     protected boolean freshLine() {
   727     protected boolean freshLine() {
   673         boolean wrapAtEol = terminal.getBooleanCapability(Capability.auto_right_margin);
   728         boolean wrapAtEol = terminal.getBooleanCapability(Capability.auto_right_margin);
   698             }
   753             }
   699             sb.append(KeyMap.key(terminal, Capability.carriage_return));
   754             sb.append(KeyMap.key(terminal, Capability.carriage_return));
   700             sb.append(" ");
   755             sb.append(" ");
   701             sb.append(KeyMap.key(terminal, Capability.carriage_return));
   756             sb.append(KeyMap.key(terminal, Capability.carriage_return));
   702         }
   757         }
   703         print(sb.toAnsi(terminal));
   758         sb.print(terminal);
   704         return true;
   759         return true;
   705     }
   760     }
   706 
   761 
   707     @Override
   762     @Override
   708     public synchronized void callWidget(String name) {
   763     public void callWidget(String name) {
   709         if (!reading) {
       
   710             throw new IllegalStateException("Widgets can only be called during a `readLine` call");
       
   711         }
       
   712         try {
   764         try {
   713             Widget w;
   765             lock.lock();
   714             if (name.startsWith(".")) {
   766             if (!reading) {
   715                 w = builtinWidgets.get(name.substring(1));
   767                 throw new IllegalStateException("Widgets can only be called during a `readLine` call");
   716             } else {
   768             }
   717                 w = widgets.get(name);
   769             try {
   718             }
   770                 Widget w;
   719             if (w != null) {
   771                 if (name.startsWith(".")) {
   720                 w.apply();
   772                     w = builtinWidgets.get(name.substring(1));
   721             }
   773                 } else {
   722         } catch (Throwable t) {
   774                     w = widgets.get(name);
   723             Log.debug("Error executing widget '", name, "'", t);
   775                 }
       
   776                 if (w != null) {
       
   777                     w.apply();
       
   778                 }
       
   779             } catch (Throwable t) {
       
   780                 Log.debug("Error executing widget '", name, "'", t);
       
   781             }
       
   782         } finally {
       
   783             lock.unlock();
   724         }
   784         }
   725     }
   785     }
   726 
   786 
   727     /**
   787     /**
   728      * Clear the line and redraw it.
   788      * Clear the line and redraw it.
   758      * Read a character from the terminal.
   818      * Read a character from the terminal.
   759      *
   819      *
   760      * @return the character, or -1 if an EOF is received.
   820      * @return the character, or -1 if an EOF is received.
   761      */
   821      */
   762     public int readCharacter() {
   822     public int readCharacter() {
   763         return bindingReader.readCharacter();
   823         if (lock.isHeldByCurrentThread()) {
       
   824             try {
       
   825                 lock.unlock();
       
   826                 return bindingReader.readCharacter();
       
   827             } finally {
       
   828                 lock.lock();
       
   829             }
       
   830         } else {
       
   831             return bindingReader.readCharacter();
       
   832         }
   764     }
   833     }
   765 
   834 
   766     public int peekCharacter(long timeout) {
   835     public int peekCharacter(long timeout) {
   767         return bindingReader.peekCharacter(timeout);
   836         return bindingReader.peekCharacter(timeout);
       
   837     }
       
   838 
       
   839     protected <T> T doReadBinding(KeyMap<T> keys, KeyMap<T> local) {
       
   840         if (lock.isHeldByCurrentThread()) {
       
   841             try {
       
   842                 lock.unlock();
       
   843                 return bindingReader.readBinding(keys, local);
       
   844             } finally {
       
   845                 lock.lock();
       
   846             }
       
   847         } else {
       
   848             return bindingReader.readBinding(keys, local);
       
   849         }
   768     }
   850     }
   769 
   851 
   770     /**
   852     /**
   771      * Read from the input stream and decode an operation from the key map.
   853      * Read from the input stream and decode an operation from the key map.
   772      *
   854      *
   781     public Binding readBinding(KeyMap<Binding> keys) {
   863     public Binding readBinding(KeyMap<Binding> keys) {
   782         return readBinding(keys, null);
   864         return readBinding(keys, null);
   783     }
   865     }
   784 
   866 
   785     public Binding readBinding(KeyMap<Binding> keys, KeyMap<Binding> local) {
   867     public Binding readBinding(KeyMap<Binding> keys, KeyMap<Binding> local) {
   786         Binding o = bindingReader.readBinding(keys, local);
   868         Binding o = doReadBinding(keys, local);
   787         /*
   869         /*
   788          * The kill ring keeps record of whether or not the
   870          * The kill ring keeps record of whether or not the
   789          * previous command was a yank or a kill. We reset
   871          * previous command was a yank or a kill. We reset
   790          * that state here if needed.
   872          * that state here if needed.
   791          */
   873          */
   924                 if (escaped) {
  1006                 if (escaped) {
   925                     escaped = false;
  1007                     escaped = false;
   926                     if (ch != '\n') {
  1008                     if (ch != '\n') {
   927                         sb.append(ch);
  1009                         sb.append(ch);
   928                     }
  1010                     }
   929                 } else if (ch == '\\') {
  1011                 } else if (parser.isEscapeChar(ch)) {
   930                     escaped = true;
  1012                     escaped = true;
   931                 } else {
  1013                 } else {
   932                     sb.append(ch);
  1014                     sb.append(ch);
   933                 }
  1015                 }
   934             }
  1016             }
   946         return str;
  1028         return str;
   947     }
  1029     }
   948 
  1030 
   949     protected void handleSignal(Signal signal) {
  1031     protected void handleSignal(Signal signal) {
   950         if (signal == Signal.WINCH) {
  1032         if (signal == Signal.WINCH) {
   951             size.copy(terminal.getSize());
  1033             Status status = Status.getStatus(terminal, false);
       
  1034             if (status != null) {
       
  1035                 status.hardReset();
       
  1036             }
       
  1037             size.copy(terminal.getBufferSize());
   952             display.resize(size.getRows(), size.getColumns());
  1038             display.resize(size.getRows(), size.getColumns());
       
  1039             redrawLine();
   953             redisplay();
  1040             redisplay();
   954         }
  1041         }
   955         else if (signal == Signal.CONT) {
  1042         else if (signal == Signal.CONT) {
   956             terminal.enterRawMode();
  1043             terminal.enterRawMode();
   957             size.copy(terminal.getSize());
  1044             size.copy(terminal.getBufferSize());
   958             display.resize(size.getRows(), size.getColumns());
  1045             display.resize(size.getRows(), size.getColumns());
   959             terminal.puts(Capability.keypad_xmit);
  1046             terminal.puts(Capability.keypad_xmit);
   960             redrawLine();
  1047             redrawLine();
   961             redisplay();
  1048             redisplay();
   962         }
  1049         }
  1901             keyMap = keyMaps.get(SAFE);
  1988             keyMap = keyMaps.get(SAFE);
  1902         }
  1989         }
  1903         while (true) {
  1990         while (true) {
  1904             post = () -> new AttributedString(searchPrompt + searchBuffer.toString() + "_");
  1991             post = () -> new AttributedString(searchPrompt + searchBuffer.toString() + "_");
  1905             redisplay();
  1992             redisplay();
  1906             Binding b = bindingReader.readBinding(keyMap);
  1993             Binding b = doReadBinding(keyMap, null);
  1907             if (b instanceof Reference) {
  1994             if (b instanceof Reference) {
  1908                 String func = ((Reference) b).name();
  1995                 String func = ((Reference) b).name();
  1909                 switch (func) {
  1996                 switch (func) {
  1910                     case SEND_BREAK:
  1997                     case SEND_BREAK:
  1911                         post = null;
  1998                         post = null;
  2298             pos = mark;
  2385             pos = mark;
  2299             v
  2386             v
  2300         } else {
  2387         } else {
  2301             viMoveMode = mode;
  2388             viMoveMode = mode;
  2302             mark = -1;
  2389             mark = -1;
  2303             Binding b = bindingReader.readBinding(getKeys(), keyMaps.get(VIOPP));
  2390             Binding b = doReadBinding(getKeys(), keyMaps.get(VIOPP));
  2304             if (b == null || new Reference(SEND_BREAK).equals(b)) {
  2391             if (b == null || new Reference(SEND_BREAK).equals(b)) {
  2305                 viMoveMode = ViMoveMode.NORMAL;
  2392                 viMoveMode = ViMoveMode.NORMAL;
  2306                 mark = oldMark;
  2393                 mark = oldMark;
  2307                 return -1;
  2394                 return -1;
  2308             }
  2395             }
  2708     protected boolean quit() {
  2795     protected boolean quit() {
  2709         getBuffer().clear();
  2796         getBuffer().clear();
  2710         return acceptLine();
  2797         return acceptLine();
  2711     }
  2798     }
  2712 
  2799 
       
  2800     protected boolean acceptAndHold() {
       
  2801         nextCommandFromHistory = false;
       
  2802         acceptLine();
       
  2803         if (!buf.toString().isEmpty()) {
       
  2804             nextHistoryId = Integer.MAX_VALUE;
       
  2805             nextCommandFromHistory = true;
       
  2806         }
       
  2807         return nextCommandFromHistory;
       
  2808     }
       
  2809 
       
  2810     protected boolean acceptLineAndDownHistory() {
       
  2811         nextCommandFromHistory = false;
       
  2812         acceptLine();
       
  2813         if (nextHistoryId < 0) {
       
  2814             nextHistoryId = history.index();
       
  2815         }
       
  2816         if (history.size() > nextHistoryId + 1) {
       
  2817             nextHistoryId++;
       
  2818             nextCommandFromHistory = true;
       
  2819         }
       
  2820         return nextCommandFromHistory;
       
  2821     }
       
  2822 
       
  2823     protected boolean acceptAndInferNextHistory() {
       
  2824         nextCommandFromHistory = false;
       
  2825         acceptLine();
       
  2826         if (!buf.toString().isEmpty()) {
       
  2827             nextHistoryId = searchBackwards(buf.toString(), history.last());
       
  2828             if (nextHistoryId >= 0 && history.size() > nextHistoryId + 1) {
       
  2829                 nextHistoryId++;
       
  2830                 nextCommandFromHistory = true;
       
  2831             }
       
  2832         }
       
  2833         return nextCommandFromHistory;
       
  2834     }
       
  2835 
  2713     protected boolean acceptLine() {
  2836     protected boolean acceptLine() {
  2714         parsedLine = null;
  2837         parsedLine = null;
  2715         if (!isSet(Option.DISABLE_EVENT_EXPANSION)) {
  2838         if (!isSet(Option.DISABLE_EVENT_EXPANSION)) {
  2716             try {
  2839             try {
  2717                 String str = buf.toString();
  2840                 String str = buf.toString();
  3341         return true;
  3464         return true;
  3342     }
  3465     }
  3343 
  3466 
  3344     protected Map<String, Widget> builtinWidgets() {
  3467     protected Map<String, Widget> builtinWidgets() {
  3345         Map<String, Widget> widgets = new HashMap<>();
  3468         Map<String, Widget> widgets = new HashMap<>();
  3346         widgets.put(ACCEPT_LINE, this::acceptLine);
  3469         addBuiltinWidget(widgets, ACCEPT_AND_INFER_NEXT_HISTORY, this::acceptAndInferNextHistory);
  3347         widgets.put(ARGUMENT_BASE, this::argumentBase);
  3470         addBuiltinWidget(widgets, ACCEPT_AND_HOLD, this::acceptAndHold);
  3348         widgets.put(BACKWARD_CHAR, this::backwardChar);
  3471         addBuiltinWidget(widgets, ACCEPT_LINE, this::acceptLine);
  3349         widgets.put(BACKWARD_DELETE_CHAR, this::backwardDeleteChar);
  3472         addBuiltinWidget(widgets, ACCEPT_LINE_AND_DOWN_HISTORY, this::acceptLineAndDownHistory);
  3350         widgets.put(BACKWARD_DELETE_WORD, this::backwardDeleteWord);
  3473         addBuiltinWidget(widgets, ARGUMENT_BASE, this::argumentBase);
  3351         widgets.put(BACKWARD_KILL_LINE, this::backwardKillLine);
  3474         addBuiltinWidget(widgets, BACKWARD_CHAR, this::backwardChar);
  3352         widgets.put(BACKWARD_KILL_WORD, this::backwardKillWord);
  3475         addBuiltinWidget(widgets, BACKWARD_DELETE_CHAR, this::backwardDeleteChar);
  3353         widgets.put(BACKWARD_WORD, this::backwardWord);
  3476         addBuiltinWidget(widgets, BACKWARD_DELETE_WORD, this::backwardDeleteWord);
  3354         widgets.put(BEEP, this::beep);
  3477         addBuiltinWidget(widgets, BACKWARD_KILL_LINE, this::backwardKillLine);
  3355         widgets.put(BEGINNING_OF_BUFFER_OR_HISTORY, this::beginningOfBufferOrHistory);
  3478         addBuiltinWidget(widgets, BACKWARD_KILL_WORD, this::backwardKillWord);
  3356         widgets.put(BEGINNING_OF_HISTORY, this::beginningOfHistory);
  3479         addBuiltinWidget(widgets, BACKWARD_WORD, this::backwardWord);
  3357         widgets.put(BEGINNING_OF_LINE, this::beginningOfLine);
  3480         addBuiltinWidget(widgets, BEEP, this::beep);
  3358         widgets.put(BEGINNING_OF_LINE_HIST, this::beginningOfLineHist);
  3481         addBuiltinWidget(widgets, BEGINNING_OF_BUFFER_OR_HISTORY, this::beginningOfBufferOrHistory);
  3359         widgets.put(CAPITALIZE_WORD, this::capitalizeWord);
  3482         addBuiltinWidget(widgets, BEGINNING_OF_HISTORY, this::beginningOfHistory);
  3360         widgets.put(CLEAR, this::clear);
  3483         addBuiltinWidget(widgets, BEGINNING_OF_LINE, this::beginningOfLine);
  3361         widgets.put(CLEAR_SCREEN, this::clearScreen);
  3484         addBuiltinWidget(widgets, BEGINNING_OF_LINE_HIST, this::beginningOfLineHist);
  3362         widgets.put(COMPLETE_PREFIX, this::completePrefix);
  3485         addBuiltinWidget(widgets, CAPITALIZE_WORD, this::capitalizeWord);
  3363         widgets.put(COMPLETE_WORD, this::completeWord);
  3486         addBuiltinWidget(widgets, CLEAR, this::clear);
  3364         widgets.put(COPY_PREV_WORD, this::copyPrevWord);
  3487         addBuiltinWidget(widgets, CLEAR_SCREEN, this::clearScreen);
  3365         widgets.put(COPY_REGION_AS_KILL, this::copyRegionAsKill);
  3488         addBuiltinWidget(widgets, COMPLETE_PREFIX, this::completePrefix);
  3366         widgets.put(DELETE_CHAR, this::deleteChar);
  3489         addBuiltinWidget(widgets, COMPLETE_WORD, this::completeWord);
  3367         widgets.put(DELETE_CHAR_OR_LIST, this::deleteCharOrList);
  3490         addBuiltinWidget(widgets, COPY_PREV_WORD, this::copyPrevWord);
  3368         widgets.put(DELETE_WORD, this::deleteWord);
  3491         addBuiltinWidget(widgets, COPY_REGION_AS_KILL, this::copyRegionAsKill);
  3369         widgets.put(DIGIT_ARGUMENT, this::digitArgument);
  3492         addBuiltinWidget(widgets, DELETE_CHAR, this::deleteChar);
  3370         widgets.put(DO_LOWERCASE_VERSION, this::doLowercaseVersion);
  3493         addBuiltinWidget(widgets, DELETE_CHAR_OR_LIST, this::deleteCharOrList);
  3371         widgets.put(DOWN_CASE_WORD, this::downCaseWord);
  3494         addBuiltinWidget(widgets, DELETE_WORD, this::deleteWord);
  3372         widgets.put(DOWN_LINE, this::downLine);
  3495         addBuiltinWidget(widgets, DIGIT_ARGUMENT, this::digitArgument);
  3373         widgets.put(DOWN_LINE_OR_HISTORY, this::downLineOrHistory);
  3496         addBuiltinWidget(widgets, DO_LOWERCASE_VERSION, this::doLowercaseVersion);
  3374         widgets.put(DOWN_LINE_OR_SEARCH, this::downLineOrSearch);
  3497         addBuiltinWidget(widgets, DOWN_CASE_WORD, this::downCaseWord);
  3375         widgets.put(DOWN_HISTORY, this::downHistory);
  3498         addBuiltinWidget(widgets, DOWN_LINE, this::downLine);
  3376         widgets.put(EMACS_EDITING_MODE, this::emacsEditingMode);
  3499         addBuiltinWidget(widgets, DOWN_LINE_OR_HISTORY, this::downLineOrHistory);
  3377         widgets.put(EMACS_BACKWARD_WORD, this::emacsBackwardWord);
  3500         addBuiltinWidget(widgets, DOWN_LINE_OR_SEARCH, this::downLineOrSearch);
  3378         widgets.put(EMACS_FORWARD_WORD, this::emacsForwardWord);
  3501         addBuiltinWidget(widgets, DOWN_HISTORY, this::downHistory);
  3379         widgets.put(END_OF_BUFFER_OR_HISTORY, this::endOfBufferOrHistory);
  3502         addBuiltinWidget(widgets, EMACS_EDITING_MODE, this::emacsEditingMode);
  3380         widgets.put(END_OF_HISTORY, this::endOfHistory);
  3503         addBuiltinWidget(widgets, EMACS_BACKWARD_WORD, this::emacsBackwardWord);
  3381         widgets.put(END_OF_LINE, this::endOfLine);
  3504         addBuiltinWidget(widgets, EMACS_FORWARD_WORD, this::emacsForwardWord);
  3382         widgets.put(END_OF_LINE_HIST, this::endOfLineHist);
  3505         addBuiltinWidget(widgets, END_OF_BUFFER_OR_HISTORY, this::endOfBufferOrHistory);
  3383         widgets.put(EXCHANGE_POINT_AND_MARK, this::exchangePointAndMark);
  3506         addBuiltinWidget(widgets, END_OF_HISTORY, this::endOfHistory);
  3384         widgets.put(EXPAND_HISTORY, this::expandHistory);
  3507         addBuiltinWidget(widgets, END_OF_LINE, this::endOfLine);
  3385         widgets.put(EXPAND_OR_COMPLETE, this::expandOrComplete);
  3508         addBuiltinWidget(widgets, END_OF_LINE_HIST, this::endOfLineHist);
  3386         widgets.put(EXPAND_OR_COMPLETE_PREFIX, this::expandOrCompletePrefix);
  3509         addBuiltinWidget(widgets, EXCHANGE_POINT_AND_MARK, this::exchangePointAndMark);
  3387         widgets.put(EXPAND_WORD, this::expandWord);
  3510         addBuiltinWidget(widgets, EXPAND_HISTORY, this::expandHistory);
  3388         widgets.put(FRESH_LINE, this::freshLine);
  3511         addBuiltinWidget(widgets, EXPAND_OR_COMPLETE, this::expandOrComplete);
  3389         widgets.put(FORWARD_CHAR, this::forwardChar);
  3512         addBuiltinWidget(widgets, EXPAND_OR_COMPLETE_PREFIX, this::expandOrCompletePrefix);
  3390         widgets.put(FORWARD_WORD, this::forwardWord);
  3513         addBuiltinWidget(widgets, EXPAND_WORD, this::expandWord);
  3391         widgets.put(HISTORY_INCREMENTAL_SEARCH_BACKWARD, this::historyIncrementalSearchBackward);
  3514         addBuiltinWidget(widgets, FRESH_LINE, this::freshLine);
  3392         widgets.put(HISTORY_INCREMENTAL_SEARCH_FORWARD, this::historyIncrementalSearchForward);
  3515         addBuiltinWidget(widgets, FORWARD_CHAR, this::forwardChar);
  3393         widgets.put(HISTORY_SEARCH_BACKWARD, this::historySearchBackward);
  3516         addBuiltinWidget(widgets, FORWARD_WORD, this::forwardWord);
  3394         widgets.put(HISTORY_SEARCH_FORWARD, this::historySearchForward);
  3517         addBuiltinWidget(widgets, HISTORY_INCREMENTAL_SEARCH_BACKWARD, this::historyIncrementalSearchBackward);
  3395         widgets.put(INSERT_CLOSE_CURLY, this::insertCloseCurly);
  3518         addBuiltinWidget(widgets, HISTORY_INCREMENTAL_SEARCH_FORWARD, this::historyIncrementalSearchForward);
  3396         widgets.put(INSERT_CLOSE_PAREN, this::insertCloseParen);
  3519         addBuiltinWidget(widgets, HISTORY_SEARCH_BACKWARD, this::historySearchBackward);
  3397         widgets.put(INSERT_CLOSE_SQUARE, this::insertCloseSquare);
  3520         addBuiltinWidget(widgets, HISTORY_SEARCH_FORWARD, this::historySearchForward);
  3398         widgets.put(INSERT_COMMENT, this::insertComment);
  3521         addBuiltinWidget(widgets, INSERT_CLOSE_CURLY, this::insertCloseCurly);
  3399         widgets.put(KILL_BUFFER, this::killBuffer);
  3522         addBuiltinWidget(widgets, INSERT_CLOSE_PAREN, this::insertCloseParen);
  3400         widgets.put(KILL_LINE, this::killLine);
  3523         addBuiltinWidget(widgets, INSERT_CLOSE_SQUARE, this::insertCloseSquare);
  3401         widgets.put(KILL_REGION, this::killRegion);
  3524         addBuiltinWidget(widgets, INSERT_COMMENT, this::insertComment);
  3402         widgets.put(KILL_WHOLE_LINE, this::killWholeLine);
  3525         addBuiltinWidget(widgets, KILL_BUFFER, this::killBuffer);
  3403         widgets.put(KILL_WORD, this::killWord);
  3526         addBuiltinWidget(widgets, KILL_LINE, this::killLine);
  3404         widgets.put(LIST_CHOICES, this::listChoices);
  3527         addBuiltinWidget(widgets, KILL_REGION, this::killRegion);
  3405         widgets.put(MENU_COMPLETE, this::menuComplete);
  3528         addBuiltinWidget(widgets, KILL_WHOLE_LINE, this::killWholeLine);
  3406         widgets.put(MENU_EXPAND_OR_COMPLETE, this::menuExpandOrComplete);
  3529         addBuiltinWidget(widgets, KILL_WORD, this::killWord);
  3407         widgets.put(NEG_ARGUMENT, this::negArgument);
  3530         addBuiltinWidget(widgets, LIST_CHOICES, this::listChoices);
  3408         widgets.put(OVERWRITE_MODE, this::overwriteMode);
  3531         addBuiltinWidget(widgets, MENU_COMPLETE, this::menuComplete);
  3409 //        widgets.put(QUIT, this::quit);
  3532         addBuiltinWidget(widgets, MENU_EXPAND_OR_COMPLETE, this::menuExpandOrComplete);
  3410         widgets.put(QUOTED_INSERT, this::quotedInsert);
  3533         addBuiltinWidget(widgets, NEG_ARGUMENT, this::negArgument);
  3411         widgets.put(REDISPLAY, this::redisplay);
  3534         addBuiltinWidget(widgets, OVERWRITE_MODE, this::overwriteMode);
  3412         widgets.put(REDRAW_LINE, this::redrawLine);
  3535 //        addBuiltinWidget(widgets, QUIT, this::quit);
  3413         widgets.put(REDO, this::redo);
  3536         addBuiltinWidget(widgets, QUOTED_INSERT, this::quotedInsert);
  3414         widgets.put(SELF_INSERT, this::selfInsert);
  3537         addBuiltinWidget(widgets, REDISPLAY, this::redisplay);
  3415         widgets.put(SELF_INSERT_UNMETA, this::selfInsertUnmeta);
  3538         addBuiltinWidget(widgets, REDRAW_LINE, this::redrawLine);
  3416         widgets.put(SEND_BREAK, this::sendBreak);
  3539         addBuiltinWidget(widgets, REDO, this::redo);
  3417         widgets.put(SET_MARK_COMMAND, this::setMarkCommand);
  3540         addBuiltinWidget(widgets, SELF_INSERT, this::selfInsert);
  3418         widgets.put(TRANSPOSE_CHARS, this::transposeChars);
  3541         addBuiltinWidget(widgets, SELF_INSERT_UNMETA, this::selfInsertUnmeta);
  3419         widgets.put(TRANSPOSE_WORDS, this::transposeWords);
  3542         addBuiltinWidget(widgets, SEND_BREAK, this::sendBreak);
  3420         widgets.put(UNDEFINED_KEY, this::undefinedKey);
  3543         addBuiltinWidget(widgets, SET_MARK_COMMAND, this::setMarkCommand);
  3421         widgets.put(UNIVERSAL_ARGUMENT, this::universalArgument);
  3544         addBuiltinWidget(widgets, TRANSPOSE_CHARS, this::transposeChars);
  3422         widgets.put(UNDO, this::undo);
  3545         addBuiltinWidget(widgets, TRANSPOSE_WORDS, this::transposeWords);
  3423         widgets.put(UP_CASE_WORD, this::upCaseWord);
  3546         addBuiltinWidget(widgets, UNDEFINED_KEY, this::undefinedKey);
  3424         widgets.put(UP_HISTORY, this::upHistory);
  3547         addBuiltinWidget(widgets, UNIVERSAL_ARGUMENT, this::universalArgument);
  3425         widgets.put(UP_LINE, this::upLine);
  3548         addBuiltinWidget(widgets, UNDO, this::undo);
  3426         widgets.put(UP_LINE_OR_HISTORY, this::upLineOrHistory);
  3549         addBuiltinWidget(widgets, UP_CASE_WORD, this::upCaseWord);
  3427         widgets.put(UP_LINE_OR_SEARCH, this::upLineOrSearch);
  3550         addBuiltinWidget(widgets, UP_HISTORY, this::upHistory);
  3428         widgets.put(VI_ADD_EOL, this::viAddEol);
  3551         addBuiltinWidget(widgets, UP_LINE, this::upLine);
  3429         widgets.put(VI_ADD_NEXT, this::viAddNext);
  3552         addBuiltinWidget(widgets, UP_LINE_OR_HISTORY, this::upLineOrHistory);
  3430         widgets.put(VI_BACKWARD_CHAR, this::viBackwardChar);
  3553         addBuiltinWidget(widgets, UP_LINE_OR_SEARCH, this::upLineOrSearch);
  3431         widgets.put(VI_BACKWARD_DELETE_CHAR, this::viBackwardDeleteChar);
  3554         addBuiltinWidget(widgets, VI_ADD_EOL, this::viAddEol);
  3432         widgets.put(VI_BACKWARD_BLANK_WORD, this::viBackwardBlankWord);
  3555         addBuiltinWidget(widgets, VI_ADD_NEXT, this::viAddNext);
  3433         widgets.put(VI_BACKWARD_BLANK_WORD_END, this::viBackwardBlankWordEnd);
  3556         addBuiltinWidget(widgets, VI_BACKWARD_CHAR, this::viBackwardChar);
  3434         widgets.put(VI_BACKWARD_KILL_WORD, this::viBackwardKillWord);
  3557         addBuiltinWidget(widgets, VI_BACKWARD_DELETE_CHAR, this::viBackwardDeleteChar);
  3435         widgets.put(VI_BACKWARD_WORD, this::viBackwardWord);
  3558         addBuiltinWidget(widgets, VI_BACKWARD_BLANK_WORD, this::viBackwardBlankWord);
  3436         widgets.put(VI_BACKWARD_WORD_END, this::viBackwardWordEnd);
  3559         addBuiltinWidget(widgets, VI_BACKWARD_BLANK_WORD_END, this::viBackwardBlankWordEnd);
  3437         widgets.put(VI_BEGINNING_OF_LINE, this::viBeginningOfLine);
  3560         addBuiltinWidget(widgets, VI_BACKWARD_KILL_WORD, this::viBackwardKillWord);
  3438         widgets.put(VI_CMD_MODE, this::viCmdMode);
  3561         addBuiltinWidget(widgets, VI_BACKWARD_WORD, this::viBackwardWord);
  3439         widgets.put(VI_DIGIT_OR_BEGINNING_OF_LINE, this::viDigitOrBeginningOfLine);
  3562         addBuiltinWidget(widgets, VI_BACKWARD_WORD_END, this::viBackwardWordEnd);
  3440         widgets.put(VI_DOWN_LINE_OR_HISTORY, this::viDownLineOrHistory);
  3563         addBuiltinWidget(widgets, VI_BEGINNING_OF_LINE, this::viBeginningOfLine);
  3441         widgets.put(VI_CHANGE, this::viChange);
  3564         addBuiltinWidget(widgets, VI_CMD_MODE, this::viCmdMode);
  3442         widgets.put(VI_CHANGE_EOL, this::viChangeEol);
  3565         addBuiltinWidget(widgets, VI_DIGIT_OR_BEGINNING_OF_LINE, this::viDigitOrBeginningOfLine);
  3443         widgets.put(VI_CHANGE_WHOLE_LINE, this::viChangeWholeLine);
  3566         addBuiltinWidget(widgets, VI_DOWN_LINE_OR_HISTORY, this::viDownLineOrHistory);
  3444         widgets.put(VI_DELETE_CHAR, this::viDeleteChar);
  3567         addBuiltinWidget(widgets, VI_CHANGE, this::viChange);
  3445         widgets.put(VI_DELETE, this::viDelete);
  3568         addBuiltinWidget(widgets, VI_CHANGE_EOL, this::viChangeEol);
  3446         widgets.put(VI_END_OF_LINE, this::viEndOfLine);
  3569         addBuiltinWidget(widgets, VI_CHANGE_WHOLE_LINE, this::viChangeWholeLine);
  3447         widgets.put(VI_KILL_EOL, this::viKillEol);
  3570         addBuiltinWidget(widgets, VI_DELETE_CHAR, this::viDeleteChar);
  3448         widgets.put(VI_FIRST_NON_BLANK, this::viFirstNonBlank);
  3571         addBuiltinWidget(widgets, VI_DELETE, this::viDelete);
  3449         widgets.put(VI_FIND_NEXT_CHAR, this::viFindNextChar);
  3572         addBuiltinWidget(widgets, VI_END_OF_LINE, this::viEndOfLine);
  3450         widgets.put(VI_FIND_NEXT_CHAR_SKIP, this::viFindNextCharSkip);
  3573         addBuiltinWidget(widgets, VI_KILL_EOL, this::viKillEol);
  3451         widgets.put(VI_FIND_PREV_CHAR, this::viFindPrevChar);
  3574         addBuiltinWidget(widgets, VI_FIRST_NON_BLANK, this::viFirstNonBlank);
  3452         widgets.put(VI_FIND_PREV_CHAR_SKIP, this::viFindPrevCharSkip);
  3575         addBuiltinWidget(widgets, VI_FIND_NEXT_CHAR, this::viFindNextChar);
  3453         widgets.put(VI_FORWARD_BLANK_WORD, this::viForwardBlankWord);
  3576         addBuiltinWidget(widgets, VI_FIND_NEXT_CHAR_SKIP, this::viFindNextCharSkip);
  3454         widgets.put(VI_FORWARD_BLANK_WORD_END, this::viForwardBlankWordEnd);
  3577         addBuiltinWidget(widgets, VI_FIND_PREV_CHAR, this::viFindPrevChar);
  3455         widgets.put(VI_FORWARD_CHAR, this::viForwardChar);
  3578         addBuiltinWidget(widgets, VI_FIND_PREV_CHAR_SKIP, this::viFindPrevCharSkip);
  3456         widgets.put(VI_FORWARD_WORD, this::viForwardWord);
  3579         addBuiltinWidget(widgets, VI_FORWARD_BLANK_WORD, this::viForwardBlankWord);
  3457         widgets.put(VI_FORWARD_WORD, this::viForwardWord);
  3580         addBuiltinWidget(widgets, VI_FORWARD_BLANK_WORD_END, this::viForwardBlankWordEnd);
  3458         widgets.put(VI_FORWARD_WORD_END, this::viForwardWordEnd);
  3581         addBuiltinWidget(widgets, VI_FORWARD_CHAR, this::viForwardChar);
  3459         widgets.put(VI_HISTORY_SEARCH_BACKWARD, this::viHistorySearchBackward);
  3582         addBuiltinWidget(widgets, VI_FORWARD_WORD, this::viForwardWord);
  3460         widgets.put(VI_HISTORY_SEARCH_FORWARD, this::viHistorySearchForward);
  3583         addBuiltinWidget(widgets, VI_FORWARD_WORD, this::viForwardWord);
  3461         widgets.put(VI_INSERT, this::viInsert);
  3584         addBuiltinWidget(widgets, VI_FORWARD_WORD_END, this::viForwardWordEnd);
  3462         widgets.put(VI_INSERT_BOL, this::viInsertBol);
  3585         addBuiltinWidget(widgets, VI_HISTORY_SEARCH_BACKWARD, this::viHistorySearchBackward);
  3463         widgets.put(VI_INSERT_COMMENT, this::viInsertComment);
  3586         addBuiltinWidget(widgets, VI_HISTORY_SEARCH_FORWARD, this::viHistorySearchForward);
  3464         widgets.put(VI_JOIN, this::viJoin);
  3587         addBuiltinWidget(widgets, VI_INSERT, this::viInsert);
  3465         widgets.put(VI_KILL_LINE, this::viKillWholeLine);
  3588         addBuiltinWidget(widgets, VI_INSERT_BOL, this::viInsertBol);
  3466         widgets.put(VI_MATCH_BRACKET, this::viMatchBracket);
  3589         addBuiltinWidget(widgets, VI_INSERT_COMMENT, this::viInsertComment);
  3467         widgets.put(VI_OPEN_LINE_ABOVE, this::viOpenLineAbove);
  3590         addBuiltinWidget(widgets, VI_JOIN, this::viJoin);
  3468         widgets.put(VI_OPEN_LINE_BELOW, this::viOpenLineBelow);
  3591         addBuiltinWidget(widgets, VI_KILL_LINE, this::viKillWholeLine);
  3469         widgets.put(VI_PUT_AFTER, this::viPutAfter);
  3592         addBuiltinWidget(widgets, VI_MATCH_BRACKET, this::viMatchBracket);
  3470         widgets.put(VI_PUT_BEFORE, this::viPutBefore);
  3593         addBuiltinWidget(widgets, VI_OPEN_LINE_ABOVE, this::viOpenLineAbove);
  3471         widgets.put(VI_REPEAT_FIND, this::viRepeatFind);
  3594         addBuiltinWidget(widgets, VI_OPEN_LINE_BELOW, this::viOpenLineBelow);
  3472         widgets.put(VI_REPEAT_SEARCH, this::viRepeatSearch);
  3595         addBuiltinWidget(widgets, VI_PUT_AFTER, this::viPutAfter);
  3473         widgets.put(VI_REPLACE_CHARS, this::viReplaceChars);
  3596         addBuiltinWidget(widgets, VI_PUT_BEFORE, this::viPutBefore);
  3474         widgets.put(VI_REV_REPEAT_FIND, this::viRevRepeatFind);
  3597         addBuiltinWidget(widgets, VI_REPEAT_FIND, this::viRepeatFind);
  3475         widgets.put(VI_REV_REPEAT_SEARCH, this::viRevRepeatSearch);
  3598         addBuiltinWidget(widgets, VI_REPEAT_SEARCH, this::viRepeatSearch);
  3476         widgets.put(VI_SWAP_CASE, this::viSwapCase);
  3599         addBuiltinWidget(widgets, VI_REPLACE_CHARS, this::viReplaceChars);
  3477         widgets.put(VI_UP_LINE_OR_HISTORY, this::viUpLineOrHistory);
  3600         addBuiltinWidget(widgets, VI_REV_REPEAT_FIND, this::viRevRepeatFind);
  3478         widgets.put(VI_YANK, this::viYankTo);
  3601         addBuiltinWidget(widgets, VI_REV_REPEAT_SEARCH, this::viRevRepeatSearch);
  3479         widgets.put(VI_YANK_WHOLE_LINE, this::viYankWholeLine);
  3602         addBuiltinWidget(widgets, VI_SWAP_CASE, this::viSwapCase);
  3480         widgets.put(VISUAL_LINE_MODE, this::visualLineMode);
  3603         addBuiltinWidget(widgets, VI_UP_LINE_OR_HISTORY, this::viUpLineOrHistory);
  3481         widgets.put(VISUAL_MODE, this::visualMode);
  3604         addBuiltinWidget(widgets, VI_YANK, this::viYankTo);
  3482         widgets.put(WHAT_CURSOR_POSITION, this::whatCursorPosition);
  3605         addBuiltinWidget(widgets, VI_YANK_WHOLE_LINE, this::viYankWholeLine);
  3483         widgets.put(YANK, this::yank);
  3606         addBuiltinWidget(widgets, VISUAL_LINE_MODE, this::visualLineMode);
  3484         widgets.put(YANK_POP, this::yankPop);
  3607         addBuiltinWidget(widgets, VISUAL_MODE, this::visualMode);
  3485         widgets.put(MOUSE, this::mouse);
  3608         addBuiltinWidget(widgets, WHAT_CURSOR_POSITION, this::whatCursorPosition);
  3486         widgets.put(BEGIN_PASTE, this::beginPaste);
  3609         addBuiltinWidget(widgets, YANK, this::yank);
  3487         widgets.put(FOCUS_IN, this::focusIn);
  3610         addBuiltinWidget(widgets, YANK_POP, this::yankPop);
  3488         widgets.put(FOCUS_OUT, this::focusOut);
  3611         addBuiltinWidget(widgets, MOUSE, this::mouse);
       
  3612         addBuiltinWidget(widgets, BEGIN_PASTE, this::beginPaste);
       
  3613         addBuiltinWidget(widgets, FOCUS_IN, this::focusIn);
       
  3614         addBuiltinWidget(widgets, FOCUS_OUT, this::focusOut);
  3489         return widgets;
  3615         return widgets;
       
  3616     }
       
  3617 
       
  3618     private void addBuiltinWidget(Map<String, Widget> widgets, String name, Widget widget) {
       
  3619         widgets.put(name, namedWidget(name, widget));
       
  3620     }
       
  3621 
       
  3622     private Widget namedWidget(String name, Widget widget) {
       
  3623         return new Widget() {
       
  3624             @Override
       
  3625             public String toString() {
       
  3626                 return name;
       
  3627             }
       
  3628             @Override
       
  3629             public boolean apply() {
       
  3630                 return widget.apply();
       
  3631             }
       
  3632         };
  3490     }
  3633     }
  3491 
  3634 
  3492     public boolean redisplay() {
  3635     public boolean redisplay() {
  3493         redisplay(true);
  3636         redisplay(true);
  3494         return true;
  3637         return true;
  3495     }
  3638     }
  3496 
  3639 
  3497     protected synchronized void redisplay(boolean flush) {
  3640     protected void redisplay(boolean flush) {
  3498         if (skipRedisplay) {
  3641         try {
  3499             skipRedisplay = false;
  3642             lock.lock();
  3500             return;
  3643 
  3501         }
  3644             if (skipRedisplay) {
  3502 
  3645                 skipRedisplay = false;
  3503         Status status = Status.getStatus(terminal, false);
  3646                 return;
  3504         if (status != null) {
  3647             }
  3505             status.redraw();
  3648 
  3506         }
  3649             Status status = Status.getStatus(terminal, false);
  3507 
  3650             if (status != null) {
  3508         if (size.getRows() > 0 && size.getRows() < MIN_ROWS) {
  3651                 status.redraw();
  3509             AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH);
  3652             }
  3510 
  3653 
  3511             sb.append(prompt);
  3654             if (size.getRows() > 0 && size.getRows() < MIN_ROWS) {
  3512             concat(getHighlightedBuffer(buf.toString()).columnSplitLength(Integer.MAX_VALUE), sb);
  3655                 AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH);
  3513             AttributedString full = sb.toAttributedString();
  3656 
  3514 
  3657                 sb.append(prompt);
  3515             sb.setLength(0);
  3658                 concat(getHighlightedBuffer(buf.toString()).columnSplitLength(Integer.MAX_VALUE), sb);
  3516             sb.append(prompt);
  3659                 AttributedString full = sb.toAttributedString();
  3517             String line = buf.upToCursor();
  3660 
  3518             if(maskingCallback != null) {
       
  3519                 line = maskingCallback.display(line);
       
  3520             }
       
  3521 
       
  3522             concat(new AttributedString(line).columnSplitLength(Integer.MAX_VALUE), sb);
       
  3523             AttributedString toCursor = sb.toAttributedString();
       
  3524 
       
  3525             int w = WCWidth.wcwidth('\u2026');
       
  3526             int width = size.getColumns();
       
  3527             int cursor = toCursor.columnLength();
       
  3528             int inc = width /2 + 1;
       
  3529             while (cursor <= smallTerminalOffset + w) {
       
  3530                 smallTerminalOffset -= inc;
       
  3531             }
       
  3532             while (cursor >= smallTerminalOffset + width - w) {
       
  3533                 smallTerminalOffset += inc;
       
  3534             }
       
  3535             if (smallTerminalOffset > 0) {
       
  3536                 sb.setLength(0);
  3661                 sb.setLength(0);
  3537                 sb.append("\u2026");
  3662                 sb.append(prompt);
  3538                 sb.append(full.columnSubSequence(smallTerminalOffset + w, Integer.MAX_VALUE));
  3663                 String line = buf.upToCursor();
  3539                 full = sb.toAttributedString();
  3664                 if (maskingCallback != null) {
  3540             }
  3665                     line = maskingCallback.display(line);
  3541             int length = full.columnLength();
  3666                 }
  3542             if (length >= smallTerminalOffset + width) {
  3667 
  3543                 sb.setLength(0);
  3668                 concat(new AttributedString(line).columnSplitLength(Integer.MAX_VALUE), sb);
  3544                 sb.append(full.columnSubSequence(0, width - w));
  3669                 AttributedString toCursor = sb.toAttributedString();
  3545                 sb.append("\u2026");
  3670 
  3546                 full = sb.toAttributedString();
  3671                 int w = WCWidth.wcwidth('\u2026');
  3547             }
  3672                 int width = size.getColumns();
  3548 
  3673                 int cursor = toCursor.columnLength();
  3549             display.update(Collections.singletonList(full), cursor - smallTerminalOffset, flush);
  3674                 int inc = width / 2 + 1;
  3550             return;
  3675                 while (cursor <= smallTerminalOffset + w) {
  3551         }
  3676                     smallTerminalOffset -= inc;
  3552 
  3677                 }
  3553         List<AttributedString> secondaryPrompts = new ArrayList<>();
  3678                 while (cursor >= smallTerminalOffset + width - w) {
  3554         AttributedString full = getDisplayedBufferWithPrompts(secondaryPrompts);
  3679                     smallTerminalOffset += inc;
  3555 
  3680                 }
  3556         List<AttributedString> newLines;
  3681                 if (smallTerminalOffset > 0) {
  3557         if (size.getColumns() <= 0) {
  3682                     sb.setLength(0);
  3558             newLines = new ArrayList<>();
  3683                     sb.append("\u2026");
  3559             newLines.add(full);
  3684                     sb.append(full.columnSubSequence(smallTerminalOffset + w, Integer.MAX_VALUE));
  3560         } else {
  3685                     full = sb.toAttributedString();
  3561             newLines = full.columnSplitLength(size.getColumns(), true, display.delayLineWrap());
  3686                 }
  3562         }
  3687                 int length = full.columnLength();
  3563 
  3688                 if (length >= smallTerminalOffset + width) {
  3564         List<AttributedString> rightPromptLines;
  3689                     sb.setLength(0);
  3565         if (rightPrompt.length() == 0 || size.getColumns() <= 0) {
  3690                     sb.append(full.columnSubSequence(0, width - w));
  3566             rightPromptLines = new ArrayList<>();
  3691                     sb.append("\u2026");
  3567         } else {
  3692                     full = sb.toAttributedString();
  3568             rightPromptLines = rightPrompt.columnSplitLength(size.getColumns());
  3693                 }
  3569         }
  3694 
  3570         while (newLines.size() < rightPromptLines.size()) {
  3695                 display.update(Collections.singletonList(full), cursor - smallTerminalOffset, flush);
  3571             newLines.add(new AttributedString(""));
  3696                 return;
  3572         }
  3697             }
  3573         for (int i = 0; i < rightPromptLines.size(); i++) {
  3698 
  3574             AttributedString line = rightPromptLines.get(i);
  3699             List<AttributedString> secondaryPrompts = new ArrayList<>();
  3575             newLines.set(i, addRightPrompt(line, newLines.get(i)));
  3700             AttributedString full = getDisplayedBufferWithPrompts(secondaryPrompts);
  3576         }
  3701 
  3577 
  3702             List<AttributedString> newLines;
  3578         int cursorPos = -1;
  3703             if (size.getColumns() <= 0) {
  3579         if (size.getColumns() > 0) {
  3704                 newLines = new ArrayList<>();
  3580             AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH);
  3705                 newLines.add(full);
  3581             sb.append(prompt);
  3706             } else {
  3582             String buffer = buf.upToCursor();
  3707                 newLines = full.columnSplitLength(size.getColumns(), true, display.delayLineWrap());
  3583             if (maskingCallback != null) {
  3708             }
  3584                 buffer = maskingCallback.display(buffer);
  3709 
  3585             }
  3710             List<AttributedString> rightPromptLines;
  3586             sb.append(insertSecondaryPrompts(new AttributedString(buffer), secondaryPrompts, false));
  3711             if (rightPrompt.length() == 0 || size.getColumns() <= 0) {
  3587             List<AttributedString> promptLines = sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap());
  3712                 rightPromptLines = new ArrayList<>();
  3588             if (!promptLines.isEmpty()) {
  3713             } else {
  3589                 cursorPos = size.cursorPos(promptLines.size() - 1,
  3714                 rightPromptLines = rightPrompt.columnSplitLength(size.getColumns());
  3590                                            promptLines.get(promptLines.size() - 1).columnLength());
  3715             }
  3591             }
  3716             while (newLines.size() < rightPromptLines.size()) {
  3592         }
  3717                 newLines.add(new AttributedString(""));
  3593 
  3718             }
  3594         display.update(newLines, cursorPos, flush);
  3719             for (int i = 0; i < rightPromptLines.size(); i++) {
       
  3720                 AttributedString line = rightPromptLines.get(i);
       
  3721                 newLines.set(i, addRightPrompt(line, newLines.get(i)));
       
  3722             }
       
  3723 
       
  3724             int cursorPos = -1;
       
  3725             int cursorNewLinesId = -1;
       
  3726             int cursorColPos = -1;
       
  3727             if (size.getColumns() > 0) {
       
  3728                 AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH);
       
  3729                 sb.append(prompt);
       
  3730                 String buffer = buf.upToCursor();
       
  3731                 if (maskingCallback != null) {
       
  3732                     buffer = maskingCallback.display(buffer);
       
  3733                 }
       
  3734                 sb.append(insertSecondaryPrompts(new AttributedString(buffer), secondaryPrompts, false));
       
  3735                 List<AttributedString> promptLines = sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap());
       
  3736                 if (!promptLines.isEmpty()) {
       
  3737                     cursorNewLinesId = promptLines.size() - 1;
       
  3738                     cursorColPos = promptLines.get(promptLines.size() - 1).columnLength();
       
  3739                     cursorPos = size.cursorPos(cursorNewLinesId, cursorColPos);
       
  3740                 }
       
  3741             }
       
  3742 
       
  3743             List<AttributedString> newLinesToDisplay = new ArrayList<>();
       
  3744             int displaySize = size.getRows() - (status != null ? status.size() : 0);
       
  3745             if (newLines.size() > displaySize && !isTerminalDumb()) {
       
  3746                 StringBuilder sb = new StringBuilder(">....");
       
  3747                 // blanks are needed when displaying command completion candidate list
       
  3748                 for (int i = sb.toString().length(); i < size.getColumns(); i++) {
       
  3749                     sb.append(" ");
       
  3750                 }
       
  3751                 AttributedString partialCommandInfo = new AttributedString(sb.toString());
       
  3752                 int lineId = newLines.size() - displaySize + 1;
       
  3753                 int endId = displaySize;
       
  3754                 int startId = 1;
       
  3755                 if (lineId  > cursorNewLinesId) {
       
  3756                     lineId = cursorNewLinesId;
       
  3757                     endId = displaySize - 1;
       
  3758                     startId = 0;
       
  3759                 } else {
       
  3760                     newLinesToDisplay.add(partialCommandInfo);
       
  3761                 }
       
  3762                 int cursorRowPos = 0;
       
  3763                 for (int i = startId; i < endId; i++) {
       
  3764                     if (cursorNewLinesId == lineId) {
       
  3765                         cursorRowPos = i;
       
  3766                     }
       
  3767                     newLinesToDisplay.add(newLines.get(lineId++));
       
  3768                 }
       
  3769                 if (startId == 0) {
       
  3770                     newLinesToDisplay.add(partialCommandInfo);
       
  3771                 }
       
  3772                 cursorPos = size.cursorPos(cursorRowPos, cursorColPos);
       
  3773             } else {
       
  3774                 newLinesToDisplay = newLines;
       
  3775             }
       
  3776             display.update(newLinesToDisplay, cursorPos, flush);
       
  3777         } finally {
       
  3778             lock.unlock();
       
  3779         }
  3595     }
  3780     }
  3596 
  3781 
  3597     private void concat(List<AttributedString> lines, AttributedStringBuilder sb) {
  3782     private void concat(List<AttributedString> lines, AttributedStringBuilder sb) {
  3598         if (lines.size() > 1) {
  3783         if (lines.size() > 1) {
  3599             for (int i = 0; i < lines.size() - 1; i++) {
  3784             for (int i = 0; i < lines.size() - 1; i++) {
  3654                 int count = 0;
  3839                 int count = 0;
  3655                 boolean countSeen = false;
  3840                 boolean countSeen = false;
  3656                 decode: while (true) {
  3841                 decode: while (true) {
  3657                     ch = pattern.charAt(i++);
  3842                     ch = pattern.charAt(i++);
  3658                     switch (ch) {
  3843                     switch (ch) {
  3659                        case '{':
  3844                         case '{':
  3660                        case '}':
  3845                         case '}':
  3661                            String str = sb.toString();
  3846                             String str = sb.toString();
  3662                            AttributedString astr;
  3847                             AttributedString astr;
  3663                            if (!isHidden) {
  3848                             if (!isHidden) {
  3664                                astr = AttributedString.fromAnsi(str);
  3849                                 astr = AttributedString.fromAnsi(str);
  3665                                cols += astr.columnLength();
  3850                                 cols += astr.columnLength();
  3666                            } else {
  3851                             } else {
  3667                                astr = new AttributedString(str, AttributedStyle.HIDDEN);
  3852                                 astr = new AttributedString(str, AttributedStyle.HIDDEN);
  3668                            }
  3853                             }
  3669                            if (padPartIndex == parts.size()) {
  3854                             if (padPartIndex == parts.size()) {
  3670                                padPartString = sb;
  3855                                 padPartString = sb;
  3671                                if (i < plen) {
  3856                                 if (i < plen) {
  3672                                    sb = new StringBuilder();
  3857                                     sb = new StringBuilder();
  3673                                }
  3858                                 }
  3674                            } else {
  3859                             } else {
  3675                                sb.setLength(0);
  3860                                 sb.setLength(0);
  3676                            }
  3861                             }
  3677                            parts.add(astr);
  3862                             parts.add(astr);
  3678                            isHidden = ch == '{';
  3863                             isHidden = ch == '{';
  3679                             break decode;
  3864                             break decode;
  3680                         case '%':
  3865                         case '%':
  3681                             sb.append(ch);
  3866                             sb.append(ch);
  3682                             break decode;
  3867                             break decode;
  3683                         case 'N':
  3868                         case 'N':
  4046 
  4231 
  4047         // If we have no matches, bail out
  4232         // If we have no matches, bail out
  4048         if (matching.isEmpty()) {
  4233         if (matching.isEmpty()) {
  4049             return false;
  4234             return false;
  4050         }
  4235         }
  4051 
  4236         size.copy(terminal.getSize());
  4052         // If we only need to display the list, do it now
  4237         try {
  4053         if (lst == CompletionType.List) {
  4238             // If we only need to display the list, do it now
       
  4239             if (lst == CompletionType.List) {
       
  4240                 List<Candidate> possible = matching.entrySet().stream()
       
  4241                         .flatMap(e -> e.getValue().stream())
       
  4242                         .collect(Collectors.toList());
       
  4243                 doList(possible, line.word(), false, line::escape);
       
  4244                 return !possible.isEmpty();
       
  4245             }
       
  4246 
       
  4247             // Check if there's a single possible match
       
  4248             Candidate completion = null;
       
  4249             // If there's a single possible completion
       
  4250             if (matching.size() == 1) {
       
  4251                 completion = matching.values().stream().flatMap(Collection::stream)
       
  4252                         .findFirst().orElse(null);
       
  4253             }
       
  4254             // Or if RECOGNIZE_EXACT is set, try to find an exact match
       
  4255             else if (isSet(Option.RECOGNIZE_EXACT)) {
       
  4256                 completion = matching.values().stream().flatMap(Collection::stream)
       
  4257                         .filter(Candidate::complete)
       
  4258                         .filter(c -> exact.test(c.value()))
       
  4259                         .findFirst().orElse(null);
       
  4260             }
       
  4261             // Complete and exit
       
  4262             if (completion != null && !completion.value().isEmpty()) {
       
  4263                 if (prefix) {
       
  4264                     buf.backspace(line.rawWordCursor());
       
  4265                 } else {
       
  4266                     buf.move(line.rawWordLength() - line.rawWordCursor());
       
  4267                     buf.backspace(line.rawWordLength());
       
  4268                 }
       
  4269                 buf.write(line.escape(completion.value(), completion.complete()));
       
  4270                 if (completion.complete()) {
       
  4271                     if (buf.currChar() != ' ') {
       
  4272                         buf.write(" ");
       
  4273                     } else {
       
  4274                         buf.move(1);
       
  4275                     }
       
  4276                 }
       
  4277                 if (completion.suffix() != null) {
       
  4278                     redisplay();
       
  4279                     Binding op = readBinding(getKeys());
       
  4280                     if (op != null) {
       
  4281                         String chars = getString(REMOVE_SUFFIX_CHARS, DEFAULT_REMOVE_SUFFIX_CHARS);
       
  4282                         String ref = op instanceof Reference ? ((Reference) op).name() : null;
       
  4283                         if (SELF_INSERT.equals(ref) && chars.indexOf(getLastBinding().charAt(0)) >= 0
       
  4284                                 || ACCEPT_LINE.equals(ref)) {
       
  4285                             buf.backspace(completion.suffix().length());
       
  4286                             if (getLastBinding().charAt(0) != ' ') {
       
  4287                                 buf.write(' ');
       
  4288                             }
       
  4289                         }
       
  4290                         pushBackBinding(true);
       
  4291                     }
       
  4292                 }
       
  4293                 return true;
       
  4294             }
       
  4295 
  4054             List<Candidate> possible = matching.entrySet().stream()
  4296             List<Candidate> possible = matching.entrySet().stream()
  4055                     .flatMap(e -> e.getValue().stream())
  4297                     .flatMap(e -> e.getValue().stream())
  4056                     .collect(Collectors.toList());
  4298                     .collect(Collectors.toList());
  4057             doList(possible, line.word(), false, line::escape);
  4299 
  4058             return !possible.isEmpty();
  4300             if (useMenu) {
  4059         }
  4301                 buf.move(line.word().length() - line.wordCursor());
  4060 
  4302                 buf.backspace(line.word().length());
  4061         // Check if there's a single possible match
  4303                 doMenu(possible, line.word(), line::escape);
  4062         Candidate completion = null;
  4304                 return true;
  4063         // If there's a single possible completion
  4305             }
  4064         if (matching.size() == 1) {
  4306 
  4065             completion = matching.values().stream().flatMap(Collection::stream)
  4307             // Find current word and move to end
  4066                     .findFirst().orElse(null);
  4308             String current;
  4067         }
       
  4068         // Or if RECOGNIZE_EXACT is set, try to find an exact match
       
  4069         else if (isSet(Option.RECOGNIZE_EXACT)) {
       
  4070             completion = matching.values().stream().flatMap(Collection::stream)
       
  4071                     .filter(Candidate::complete)
       
  4072                     .filter(c -> exact.test(c.value()))
       
  4073                     .findFirst().orElse(null);
       
  4074         }
       
  4075         // Complete and exit
       
  4076         if (completion != null && !completion.value().isEmpty()) {
       
  4077             if (prefix) {
  4309             if (prefix) {
  4078                 buf.backspace(line.rawWordCursor());
  4310                 current = line.word().substring(0, line.wordCursor());
  4079             } else {
  4311             } else {
       
  4312                 current = line.word();
  4080                 buf.move(line.rawWordLength() - line.rawWordCursor());
  4313                 buf.move(line.rawWordLength() - line.rawWordCursor());
       
  4314             }
       
  4315             // Now, we need to find the unambiguous completion
       
  4316             // TODO: need to find common suffix
       
  4317             String commonPrefix = null;
       
  4318             for (String key : matching.keySet()) {
       
  4319                 commonPrefix = commonPrefix == null ? key : getCommonStart(commonPrefix, key, caseInsensitive);
       
  4320             }
       
  4321             boolean hasUnambiguous = commonPrefix.startsWith(current) && !commonPrefix.equals(current);
       
  4322 
       
  4323             if (hasUnambiguous) {
  4081                 buf.backspace(line.rawWordLength());
  4324                 buf.backspace(line.rawWordLength());
  4082             }
  4325                 buf.write(line.escape(commonPrefix, false));
  4083             buf.write(line.escape(completion.value(), completion.complete()));
  4326                 current = commonPrefix;
  4084             if (completion.complete()) {
  4327                 if ((!isSet(Option.AUTO_LIST) && isSet(Option.AUTO_MENU))
  4085                 if (buf.currChar() != ' ') {
  4328                         || (isSet(Option.AUTO_LIST) && isSet(Option.LIST_AMBIGUOUS))) {
  4086                     buf.write(" ");
  4329                     if (!nextBindingIsComplete()) {
  4087                 } else {
  4330                         return true;
  4088                     buf.move(1);
       
  4089                 }
       
  4090             }
       
  4091             if (completion.suffix() != null) {
       
  4092                 redisplay();
       
  4093                 Binding op = readBinding(getKeys());
       
  4094                 if (op != null) {
       
  4095                     String chars = getString(REMOVE_SUFFIX_CHARS, DEFAULT_REMOVE_SUFFIX_CHARS);
       
  4096                     String ref = op instanceof Reference ? ((Reference) op).name() : null;
       
  4097                     if (SELF_INSERT.equals(ref) && chars.indexOf(getLastBinding().charAt(0)) >= 0
       
  4098                             || ACCEPT_LINE.equals(ref)) {
       
  4099                         buf.backspace(completion.suffix().length());
       
  4100                         if (getLastBinding().charAt(0) != ' ') {
       
  4101                             buf.write(' ');
       
  4102                         }
       
  4103                     }
  4331                     }
  4104                     pushBackBinding(true);
  4332                 }
  4105                 }
  4333             }
       
  4334             if (isSet(Option.AUTO_LIST)) {
       
  4335                 if (!doList(possible, current, true, line::escape)) {
       
  4336                     return true;
       
  4337                 }
       
  4338             }
       
  4339             if (isSet(Option.AUTO_MENU)) {
       
  4340                 buf.backspace(current.length());
       
  4341                 doMenu(possible, line.word(), line::escape);
  4106             }
  4342             }
  4107             return true;
  4343             return true;
  4108         }
  4344         } finally {
  4109 
  4345             size.copy(terminal.getBufferSize());
  4110         List<Candidate> possible = matching.entrySet().stream()
  4346         }
  4111                 .flatMap(e -> e.getValue().stream())
       
  4112                 .collect(Collectors.toList());
       
  4113 
       
  4114         if (useMenu) {
       
  4115             buf.move(line.word().length() - line.wordCursor());
       
  4116             buf.backspace(line.word().length());
       
  4117             doMenu(possible, line.word(), line::escape);
       
  4118             return true;
       
  4119         }
       
  4120 
       
  4121         // Find current word and move to end
       
  4122         String current;
       
  4123         if (prefix) {
       
  4124             current = line.word().substring(0, line.wordCursor());
       
  4125         } else {
       
  4126             current = line.word();
       
  4127             buf.move(line.rawWordLength() - line.rawWordCursor());
       
  4128         }
       
  4129         // Now, we need to find the unambiguous completion
       
  4130         // TODO: need to find common suffix
       
  4131         String commonPrefix = null;
       
  4132         for (String key : matching.keySet()) {
       
  4133             commonPrefix = commonPrefix == null ? key : getCommonStart(commonPrefix, key, caseInsensitive);
       
  4134         }
       
  4135         boolean hasUnambiguous = commonPrefix.startsWith(current) && !commonPrefix.equals(current);
       
  4136 
       
  4137         if (hasUnambiguous) {
       
  4138             buf.backspace(line.rawWordLength());
       
  4139             buf.write(line.escape(commonPrefix, false));
       
  4140             current = commonPrefix;
       
  4141             if ((!isSet(Option.AUTO_LIST) && isSet(Option.AUTO_MENU))
       
  4142                     || (isSet(Option.AUTO_LIST) && isSet(Option.LIST_AMBIGUOUS))) {
       
  4143                 if (!nextBindingIsComplete()) {
       
  4144                     return true;
       
  4145                 }
       
  4146             }
       
  4147         }
       
  4148         if (isSet(Option.AUTO_LIST)) {
       
  4149             if (!doList(possible, current, true, line::escape)) {
       
  4150                 return true;
       
  4151             }
       
  4152         }
       
  4153         if (isSet(Option.AUTO_MENU)) {
       
  4154             buf.backspace(current.length());
       
  4155             doMenu(possible, line.word(), line::escape);
       
  4156         }
       
  4157         return true;
       
  4158     }
  4347     }
  4159 
  4348 
  4160     private CompletingParsedLine wrap(ParsedLine line) {
  4349     private CompletingParsedLine wrap(ParsedLine line) {
  4161         if (line instanceof CompletingParsedLine) {
  4350         if (line instanceof CompletingParsedLine) {
  4162             return (CompletingParsedLine) line;
  4351             return (CompletingParsedLine) line;
  4532         int lines = postResult.lines;
  4721         int lines = postResult.lines;
  4533         int listMax = getInt(LIST_MAX, DEFAULT_LIST_MAX);
  4722         int listMax = getInt(LIST_MAX, DEFAULT_LIST_MAX);
  4534         if (listMax > 0 && possible.size() >= listMax
  4723         if (listMax > 0 && possible.size() >= listMax
  4535                 || lines >= size.getRows() - promptLines) {
  4724                 || lines >= size.getRows() - promptLines) {
  4536             // prompt
  4725             // prompt
  4537             post = () -> new AttributedString(getAppName() + ": do you wish to see to see all " + possible.size()
  4726             post = () -> new AttributedString(getAppName() + ": do you wish to see all " + possible.size()
  4538                     + " possibilities (" + lines + " lines)?");
  4727                     + " possibilities (" + lines + " lines)?");
  4539             redisplay(true);
  4728             redisplay(true);
  4540             int c = readCharacter();
  4729             int c = readCharacter();
  4541             if (c != 'y' && c != 'Y' && c != '\t') {
  4730             if (c != 'y' && c != 'Y' && c != '\t') {
  4542                 post = null;
  4731                 post = null;
  4584             if (!runLoop) {
  4773             if (!runLoop) {
  4585                 return false;
  4774                 return false;
  4586             }
  4775             }
  4587             redisplay();
  4776             redisplay();
  4588             // TODO: use a different keyMap ?
  4777             // TODO: use a different keyMap ?
  4589             Binding b = bindingReader.readBinding(getKeys());
  4778             Binding b = doReadBinding(getKeys(), null);
  4590             if (b instanceof Reference) {
  4779             if (b instanceof Reference) {
  4591                 String name = ((Reference) b).name();
  4780                 String name = ((Reference) b).name();
  4592                 if (BACKWARD_DELETE_CHAR.equals(name) || VI_BACKWARD_DELETE_CHAR.equals(name)) {
  4781                 if (BACKWARD_DELETE_CHAR.equals(name) || VI_BACKWARD_DELETE_CHAR.equals(name)) {
  4593                     if (sb.length() == 0) {
  4782                     if (sb.length() == 0) {
  4594                         pushBackBinding();
  4783                         pushBackBinding();
  4729         return new PostResult(sb.toAttributedString(), out[0], out[1]);
  4918         return new PostResult(sb.toAttributedString(), out[0], out[1]);
  4730     }
  4919     }
  4731 
  4920 
  4732     @SuppressWarnings("unchecked")
  4921     @SuppressWarnings("unchecked")
  4733     protected void toColumns(Object items, int width, int maxWidth, AttributedStringBuilder sb, Candidate selection, String completed, boolean rowsFirst, int[] out) {
  4922     protected void toColumns(Object items, int width, int maxWidth, AttributedStringBuilder sb, Candidate selection, String completed, boolean rowsFirst, int[] out) {
  4734         if (maxWidth <= 0) {
  4923         if (maxWidth <= 0 || width <= 0) {
  4735             return;
  4924             return;
  4736         }
  4925         }
  4737         // This is a group
  4926         // This is a group
  4738         if (items instanceof String) {
  4927         if (items instanceof String) {
  4739             sb.style(getCompletionStyleGroup())
  4928             sb.style(getCompletionStyleGroup())
  4983             end = start;
  5172             end = start;
  4984             while (count-- > 0) {
  5173             while (count-- > 0) {
  4985                 while (end < buf.length() && buf.atChar(end) != '\n') {
  5174                 while (end < buf.length() && buf.atChar(end) != '\n') {
  4986                     end++;
  5175                     end++;
  4987                 }
  5176                 }
  4988                 end++;
  5177                 if (end < buf.length()) {
       
  5178                     end++;
       
  5179                 }
  4989             }
  5180             }
  4990         }
  5181         }
  4991         String killed = buf.substring(start, end);
  5182         String killed = buf.substring(start, end);
  4992         buf.cursor(start);
  5183         buf.cursor(start);
  4993         buf.delete(end - start);
  5184         buf.delete(end - start);
  5186         keyMap.setNomatch(SELF_INSERT);
  5377         keyMap.setNomatch(SELF_INSERT);
  5187         keyMap.setAmbiguousTimeout(0);
  5378         keyMap.setAmbiguousTimeout(0);
  5188         keyMap.bind(END_PASTE, BRACKETED_PASTE_END);
  5379         keyMap.bind(END_PASTE, BRACKETED_PASTE_END);
  5189         StringBuilder sb = new StringBuilder();
  5380         StringBuilder sb = new StringBuilder();
  5190         while (true) {
  5381         while (true) {
  5191             Object b = bindingReader.readBinding(keyMap);
  5382             Object b = doReadBinding(keyMap, null);
  5192             if (b == END_PASTE) {
  5383             if (b == END_PASTE) {
  5193                 break;
  5384                 break;
  5194             }
  5385             }
  5195             String s = getLastBinding();
  5386             String s = getLastBinding();
  5196             if ("\r".equals(s)) {
  5387             if ("\r".equals(s)) {
  5225      * Clear the screen by issuing the ANSI "clear screen" code.
  5416      * Clear the screen by issuing the ANSI "clear screen" code.
  5226      * @return <code>true</code>
  5417      * @return <code>true</code>
  5227      */
  5418      */
  5228     public boolean clearScreen() {
  5419     public boolean clearScreen() {
  5229         if (terminal.puts(Capability.clear_screen)) {
  5420         if (terminal.puts(Capability.clear_screen)) {
       
  5421             // ConEMU extended fonts support
       
  5422             if (AbstractWindowsTerminal.TYPE_WINDOWS_CONEMU.equals(terminal.getType())
       
  5423                     && !Boolean.getBoolean("org.jline.terminal.conemu.disable-activate")) {
       
  5424                 terminal.writer().write("\u001b[9999E");
       
  5425             }
  5230             Status status = Status.getStatus(terminal, false);
  5426             Status status = Status.getStatus(terminal, false);
  5231             if (status != null) {
  5427             if (status != null) {
  5232                 status.reset();
  5428                 status.reset();
  5233             }
  5429             }
  5234             redrawLine();
  5430             redrawLine();
  5356         return keyMaps;
  5552         return keyMaps;
  5357     }
  5553     }
  5358 
  5554 
  5359     public KeyMap<Binding> emacs() {
  5555     public KeyMap<Binding> emacs() {
  5360         KeyMap<Binding> emacs = new KeyMap<>();
  5556         KeyMap<Binding> emacs = new KeyMap<>();
       
  5557         bindKeys(emacs);
  5361         bind(emacs, SET_MARK_COMMAND,                       ctrl('@'));
  5558         bind(emacs, SET_MARK_COMMAND,                       ctrl('@'));
  5362         bind(emacs, BEGINNING_OF_LINE,                      ctrl('A'));
  5559         bind(emacs, BEGINNING_OF_LINE,                      ctrl('A'));
  5363         bind(emacs, BACKWARD_CHAR,                          ctrl('B'));
  5560         bind(emacs, BACKWARD_CHAR,                          ctrl('B'));
  5364         bind(emacs, DELETE_CHAR_OR_LIST,                    ctrl('D'));
  5561         bind(emacs, DELETE_CHAR_OR_LIST,                    ctrl('D'));
  5365         bind(emacs, END_OF_LINE,                            ctrl('E'));
  5562         bind(emacs, END_OF_LINE,                            ctrl('E'));
  5370         bind(emacs, ACCEPT_LINE,                            ctrl('J'));
  5567         bind(emacs, ACCEPT_LINE,                            ctrl('J'));
  5371         bind(emacs, KILL_LINE,                              ctrl('K'));
  5568         bind(emacs, KILL_LINE,                              ctrl('K'));
  5372         bind(emacs, CLEAR_SCREEN,                           ctrl('L'));
  5569         bind(emacs, CLEAR_SCREEN,                           ctrl('L'));
  5373         bind(emacs, ACCEPT_LINE,                            ctrl('M'));
  5570         bind(emacs, ACCEPT_LINE,                            ctrl('M'));
  5374         bind(emacs, DOWN_LINE_OR_HISTORY,                   ctrl('N'));
  5571         bind(emacs, DOWN_LINE_OR_HISTORY,                   ctrl('N'));
       
  5572         bind(emacs, ACCEPT_LINE_AND_DOWN_HISTORY,           ctrl('O'));
  5375         bind(emacs, UP_LINE_OR_HISTORY,                     ctrl('P'));
  5573         bind(emacs, UP_LINE_OR_HISTORY,                     ctrl('P'));
  5376         bind(emacs, HISTORY_INCREMENTAL_SEARCH_BACKWARD,    ctrl('R'));
  5574         bind(emacs, HISTORY_INCREMENTAL_SEARCH_BACKWARD,    ctrl('R'));
  5377         bind(emacs, HISTORY_INCREMENTAL_SEARCH_FORWARD,     ctrl('S'));
  5575         bind(emacs, HISTORY_INCREMENTAL_SEARCH_FORWARD,     ctrl('S'));
  5378         bind(emacs, TRANSPOSE_CHARS,                        ctrl('T'));
  5576         bind(emacs, TRANSPOSE_CHARS,                        ctrl('T'));
  5379         bind(emacs, KILL_WHOLE_LINE,                        ctrl('U'));
  5577         bind(emacs, KILL_WHOLE_LINE,                        ctrl('U'));
  5413         bind(emacs, BEGINNING_OF_HISTORY,                   alt('<'));
  5611         bind(emacs, BEGINNING_OF_HISTORY,                   alt('<'));
  5414         bind(emacs, LIST_CHOICES,                           alt('='));
  5612         bind(emacs, LIST_CHOICES,                           alt('='));
  5415         bind(emacs, END_OF_HISTORY,                         alt('>'));
  5613         bind(emacs, END_OF_HISTORY,                         alt('>'));
  5416         bind(emacs, LIST_CHOICES,                           alt('?'));
  5614         bind(emacs, LIST_CHOICES,                           alt('?'));
  5417         bind(emacs, DO_LOWERCASE_VERSION,                   range("^[A-^[Z"));
  5615         bind(emacs, DO_LOWERCASE_VERSION,                   range("^[A-^[Z"));
       
  5616         bind(emacs, ACCEPT_AND_HOLD,                        alt('a'));
  5418         bind(emacs, BACKWARD_WORD,                          alt('b'));
  5617         bind(emacs, BACKWARD_WORD,                          alt('b'));
  5419         bind(emacs, CAPITALIZE_WORD,                        alt('c'));
  5618         bind(emacs, CAPITALIZE_WORD,                        alt('c'));
  5420         bind(emacs, KILL_WORD,                              alt('d'));
  5619         bind(emacs, KILL_WORD,                              alt('d'));
  5421         bind(emacs, KILL_WORD,                              translate("^[[3;5~")); // ctrl-delete
  5620         bind(emacs, KILL_WORD,                              translate("^[[3;5~")); // ctrl-delete
  5422         bind(emacs, FORWARD_WORD,                           alt('f'));
  5621         bind(emacs, FORWARD_WORD,                           alt('f'));
  5437         return emacs;
  5636         return emacs;
  5438     }
  5637     }
  5439 
  5638 
  5440     public KeyMap<Binding> viInsertion() {
  5639     public KeyMap<Binding> viInsertion() {
  5441         KeyMap<Binding> viins = new KeyMap<>();
  5640         KeyMap<Binding> viins = new KeyMap<>();
       
  5641         bindKeys(viins);
  5442         bind(viins, SELF_INSERT,                            range("^@-^_"));
  5642         bind(viins, SELF_INSERT,                            range("^@-^_"));
  5443         bind(viins, LIST_CHOICES,                           ctrl('D'));
  5643         bind(viins, LIST_CHOICES,                           ctrl('D'));
  5444         bind(viins, SEND_BREAK,                             ctrl('G'));
  5644         bind(viins, SEND_BREAK,                             ctrl('G'));
  5445         bind(viins, BACKWARD_DELETE_CHAR,                   ctrl('H'));
  5645         bind(viins, BACKWARD_DELETE_CHAR,                   ctrl('H'));
  5446         bind(viins, EXPAND_OR_COMPLETE,                     ctrl('I'));
  5646         bind(viins, EXPAND_OR_COMPLETE,                     ctrl('I'));
  5636 
  5836 
  5637     private String key(Capability capability) {
  5837     private String key(Capability capability) {
  5638         return KeyMap.key(terminal, capability);
  5838         return KeyMap.key(terminal, capability);
  5639     }
  5839     }
  5640 
  5840 
       
  5841     private void bindKeys(KeyMap<Binding> emacs) {
       
  5842         Widget beep = namedWidget("beep", this::beep);
       
  5843         Stream.of(Capability.values())
       
  5844                 .filter(c -> c.name().startsWith("key_"))
       
  5845                 .map(this::key)
       
  5846                 .forEach(k -> bind(emacs, beep, k));
       
  5847     }
       
  5848 
  5641     private void bindArrowKeys(KeyMap<Binding> map) {
  5849     private void bindArrowKeys(KeyMap<Binding> map) {
  5642         bind(map, UP_LINE_OR_SEARCH,    key(Capability.key_up));
  5850         bind(map, UP_LINE_OR_SEARCH,    key(Capability.key_up));
  5643         bind(map, DOWN_LINE_OR_SEARCH,  key(Capability.key_down));
  5851         bind(map, DOWN_LINE_OR_SEARCH,  key(Capability.key_down));
  5644         bind(map, BACKWARD_CHAR,        key(Capability.key_left));
  5852         bind(map, BACKWARD_CHAR,        key(Capability.key_left));
  5645         bind(map, FORWARD_CHAR,         key(Capability.key_right));
  5853         bind(map, FORWARD_CHAR,         key(Capability.key_right));