Skip to content

Commit

Permalink
Autosuggestion using command completer data, #254
Browse files Browse the repository at this point in the history
  • Loading branch information
mattirn committed Oct 8, 2019
1 parent 4c48c03 commit 969fa1a
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 39 deletions.
44 changes: 25 additions & 19 deletions builtins/src/main/java/org/jline/builtins/Widgets.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -23,9 +22,9 @@
import org.jline.reader.Binding;
import org.jline.reader.Buffer;
import org.jline.reader.LineReader;
import org.jline.reader.LineReader.SuggestionType;
import org.jline.reader.Reference;
import org.jline.reader.Widget;
import org.jline.reader.impl.LineReaderImpl;
import org.jline.reader.impl.BufferImpl;

public abstract class Widgets {
Expand Down Expand Up @@ -89,7 +88,7 @@ public String tailTip() {
}

public void enableTailTip(boolean enable) {
reader.enableTailTip(enable);
reader.setAutosuggestion(enable ? SuggestionType.HISTORY : SuggestionType.NONE);
}

public static class AutopairWidgets extends Widgets {
Expand Down Expand Up @@ -139,9 +138,9 @@ public AutopairWidgets(LineReader reader, boolean addCurlyBrackets) {
if (addCurlyBrackets) {
pairs.put("{", "}");
}
addWidget("autopair-insert", this::autopairInsert);
addWidget("autopair-close", this::autopairClose);
addWidget("autopair-delete", this::autopairDelete);
addWidget("_autopair-insert", this::autopairInsert);
addWidget("_autopair-close", this::autopairClose);
addWidget("_autopair-delete", this::autopairDelete);
addWidget("autopair-toggle", this::toggleKeyBindings);

KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
Expand Down Expand Up @@ -209,13 +208,13 @@ public boolean toggleKeyBindings() {
private void autopairBindings() {
KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
for (Map.Entry<String, String> p: pairs.entrySet()) {
map.bind(new Reference("autopair-insert"), p.getKey());
map.bind(new Reference("_autopair-insert"), p.getKey());
if (!p.getKey().equals(p.getValue())) {
map.bind(new Reference("autopair-close"), p.getValue());
map.bind(new Reference("_autopair-close"), p.getValue());
}
}
map.bind(new Reference("autopair-delete"), ctrl('H'));
map.bind(new Reference("autopair-delete"), del());
map.bind(new Reference("_autopair-delete"), ctrl('H'));
map.bind(new Reference("_autopair-delete"), del());
autopair = true;
}

Expand Down Expand Up @@ -329,9 +328,9 @@ public static class AutosuggestionWidgets extends Widgets {

public AutosuggestionWidgets(LineReader reader) {
super(reader);
addWidget("autosuggest-forward-char", this::autosuggestForwardChar);
addWidget("autosuggest-end-of-line", this::autosuggestEndOfLine);
addWidget("autosuggest-forward-word", this::partialAccept);
addWidget("_autosuggest-forward-char", this::autosuggestForwardChar);
addWidget("_autosuggest-end-of-line", this::autosuggestEndOfLine);
addWidget("_autosuggest-forward-word", this::partialAccept);
addWidget("autosuggest-toggle", this::toggleKeyBindings);
KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
for (Map.Entry<String, Binding> bound : map.getBoundKeys().entrySet()) {
Expand Down Expand Up @@ -388,7 +387,6 @@ public boolean toggleKeyBindings() {
} else {
autosuggestionBindings();
}
enableTailTip(autosuggestion);
return autosuggestion;
}

Expand All @@ -406,34 +404,42 @@ private boolean accept(String widget) {
* key bindings...
*
*/
private void autosuggestionBindings() {
public void autosuggestionBindings() {
if (autosuggestion) {
return;
}
KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
for (Map.Entry<Reference, Set<String>> entry : defaultBindings.entrySet()) {
if (entry.getKey().name().equals(LineReader.FORWARD_CHAR)) {
for (String s: entry.getValue()) {
map.bind(new Reference("autosuggest-forward-char"), s);
map.bind(new Reference("_autosuggest-forward-char"), s);
}
} else if (entry.getKey().name().equals(LineReader.END_OF_LINE)) {
for (String s: entry.getValue()) {
map.bind(new Reference("autosuggest-end-of-line"), s);
map.bind(new Reference("_autosuggest-end-of-line"), s);
}
} else if (entry.getKey().name().equals(LineReader.FORWARD_WORD)) {
for (String s: entry.getValue()) {
map.bind(new Reference("autosuggest-forward-word"), s);
map.bind(new Reference("_autosuggest-forward-word"), s);
}
}
}
autosuggestion = true;
enableTailTip(autosuggestion);
}

private void defaultBindings() {
public void defaultBindings() {
if (!autosuggestion) {
return;
}
KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
for (Map.Entry<Reference, Set<String>> entry : defaultBindings.entrySet()) {
for (String s: entry.getValue()) {
map.bind(entry.getKey(), s);
}
}
autosuggestion = false;
enableTailTip(autosuggestion);
}
}
}
26 changes: 21 additions & 5 deletions builtins/src/test/java/org/jline/example/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.jline.builtins.Widgets.AutosuggestionWidgets;
import org.jline.keymap.KeyMap;
import org.jline.reader.*;
import org.jline.reader.LineReader.SuggestionType;
import org.jline.reader.impl.DefaultParser;
import org.jline.reader.impl.DefaultParser.Bracket;
import org.jline.reader.impl.LineReaderImpl;
Expand Down Expand Up @@ -97,7 +98,7 @@ public static void help() {
, " unsetopt unset options"
, " widget UNAVAILABLE"
, " autopair toggle brackets/quotes autopair key bindings"
, " autosuggestion toggle autosuggestion key bindings"
, " autosuggestion history, completer or none"
, " Example:"
, " cls clear screen"
, " help list available commands"
Expand Down Expand Up @@ -456,11 +457,26 @@ else if ("autopair".equals(pl.word())) {
}
}
else if ("autosuggestion".equals(pl.word())) {
terminal.writer().print("Autosuggestion widgets are ");
if (autosuggestionWidgets.toggleKeyBindings()) {
terminal.writer().println("bounded.");
if (pl.words().size() == 2) {
String type = pl.words().get(1);
if (type.toLowerCase().startsWith("his")) {
autosuggestionWidgets.autosuggestionBindings();
} else if (type.toLowerCase().startsWith("com")) {
if (reader.getAutosuggestion() == SuggestionType.HISTORY) {
autosuggestionWidgets.defaultBindings();
}
reader.setAutosuggestion(SuggestionType.COMPLETER);
} else if (type.toLowerCase().startsWith("non")) {
if (reader.getAutosuggestion() == SuggestionType.HISTORY) {
autosuggestionWidgets.defaultBindings();
} else {
reader.setAutosuggestion(SuggestionType.NONE);
}
} else {
terminal.writer().println("Usage: autosuggestion history|completer|none");
}
} else {
terminal.writer().println("unbounded.");
terminal.writer().println("Autosuggestion: " + reader.getAutosuggestion());
}
}
else if ("help".equals(pl.word()) || "?".equals(pl.word())) {
Expand Down
9 changes: 8 additions & 1 deletion reader/src/main/java/org/jline/reader/LineReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,12 @@ enum RegionType {
PASTE
}

enum SuggestionType {
NONE,
HISTORY,
COMPLETER
}

/**
* Read the next line and return the contents of the buffer.
*
Expand Down Expand Up @@ -666,6 +672,7 @@ enum RegionType {

public String getTailTip();

public void enableTailTip(boolean enable);
public void setAutosuggestion(SuggestionType type);

public SuggestionType getAutosuggestion();
}
67 changes: 53 additions & 14 deletions reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ protected enum BellType {

protected final Buffer buf = new BufferImpl();
protected String tailTip = "";
protected boolean doTailTip;
protected SuggestionType autosuggestion = SuggestionType.NONE;

protected final Size size = new Size();

Expand Down Expand Up @@ -330,8 +330,13 @@ public Buffer getBuffer() {
}

@Override
public void enableTailTip(boolean enable) {
this.doTailTip = enable;
public void setAutosuggestion(SuggestionType type) {
this.autosuggestion = type;
}

@Override
public SuggestionType getAutosuggestion() {
return autosuggestion;
}

@Override
Expand Down Expand Up @@ -3932,11 +3937,22 @@ public AttributedString getDisplayedBufferWithPrompts(List<AttributedString> sec
AttributedStringBuilder full = new AttributedStringBuilder().tabs(TAB_WIDTH);
full.append(prompt);
full.append(tNewBuf);
if (doTailTip) {
String lastBinding = getLastBinding() != null ? getLastBinding() : "";
if (autosuggestion == SuggestionType.HISTORY) {
AttributedStringBuilder sb = new AttributedStringBuilder();
tailTip = matchPreviousCommand(buf.toString());
sb.styled(AttributedStyle::faint, tailTip);
full.append(sb.toAttributedString());
} else if (autosuggestion == SuggestionType.COMPLETER) {
if (buf.length() > 0 && buf.length() == buf.cursor()
&& (!lastBinding.equals("\t") || buf.prevChar() == ' ')) {
if (buf.prevChar() == ' ') {
doEmptyList();
}
listChoices(true);
} else if (!lastBinding.equals("\t")){
doEmptyList();
}
}
if (post != null) {
full.append("\n");
Expand Down Expand Up @@ -4237,7 +4253,11 @@ protected boolean completePrefix() {
}

protected boolean listChoices() {
return doComplete(CompletionType.List, isSet(Option.MENU_COMPLETE), false);
return listChoices(false);
}

private boolean listChoices(boolean forSuggestion) {
return doComplete(CompletionType.List, isSet(Option.MENU_COMPLETE), false, forSuggestion);
}

protected boolean deleteCharOrList() {
Expand All @@ -4249,6 +4269,10 @@ protected boolean deleteCharOrList() {
}

protected boolean doComplete(CompletionType lst, boolean useMenu, boolean prefix) {
return doComplete(lst, useMenu, prefix, false);
}

protected boolean doComplete(CompletionType lst, boolean useMenu, boolean prefix, boolean forSuggestion) {
// If completion is disabled, just bail out
if (getBoolean(DISABLE_COMPLETION, false)) {
return true;
Expand Down Expand Up @@ -4375,7 +4399,7 @@ protected boolean doComplete(CompletionType lst, boolean useMenu, boolean prefix
List<Candidate> possible = matching.entrySet().stream()
.flatMap(e -> e.getValue().stream())
.collect(Collectors.toList());
doList(possible, line.word(), false, line::escape);
doList(possible, line.word(), false, line::escape, forSuggestion);
return !possible.isEmpty();
}

Expand Down Expand Up @@ -4844,7 +4868,18 @@ && getLastBinding().charAt(0) != ' '
return false;
}

protected boolean doList(List<Candidate> possible, String completed, boolean runLoop, BiFunction<CharSequence, Boolean, CharSequence> escaper) {
protected boolean doEmptyList() {
return doList(new ArrayList<Candidate>(), "", false, null, false);
}

protected boolean doList(List<Candidate> possible
, String completed, boolean runLoop, BiFunction<CharSequence, Boolean, CharSequence> escaper) {
return doList(possible, completed, runLoop, escaper, false);
}

protected boolean doList(List<Candidate> possible
, String completed
, boolean runLoop, BiFunction<CharSequence, Boolean, CharSequence> escaper, boolean forSuggestion) {
// If we list only and if there's a big
// number of items, we should ask the user
// for confirmation, display the list
Expand All @@ -4857,13 +4892,17 @@ protected boolean doList(List<Candidate> possible, String completed, boolean run
int listMax = getInt(LIST_MAX, DEFAULT_LIST_MAX);
if (listMax > 0 && possible.size() >= listMax
|| lines >= size.getRows() - promptLines) {
// prompt
post = () -> new AttributedString(getAppName() + ": do you wish to see all " + possible.size()
+ " possibilities (" + lines + " lines)?");
redisplay(true);
int c = readCharacter();
if (c != 'y' && c != 'Y' && c != '\t') {
post = null;
if (!forSuggestion) {
// prompt
post = () -> new AttributedString(getAppName() + ": do you wish to see all " + possible.size()
+ " possibilities (" + lines + " lines)?");
redisplay(true);
int c = readCharacter();
if (c != 'y' && c != 'Y' && c != '\t') {
post = null;
return false;
}
} else {
return false;
}
}
Expand Down

1 comment on commit 969fa1a

@mattirn
Copy link
Collaborator Author

@mattirn mattirn commented on 969fa1a Oct 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Travis fail, network problems???

[INFO] ------------------------------------------------------------------------

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy (default-deploy) on project jline-demo: Failed to deploy metadata: Could not transfer metadata org.jline:jline-terminal:3.12.2-SNAPSHOT/maven-metadata.xml from/to sonatype-nexus-snapshots (https://oss.sonatype.org/content/repositories/snapshots/): Failed to transfer file: https://oss.sonatype.org/content/repositories/snapshots/org/jline/jline-terminal/3.12.2-SNAPSHOT/maven-metadata.xml. Return code is: 502, ReasonPhrase: Bad Gateway. -> [Help 1]

[ERROR]

Please sign in to comment.