Skip to content

Commit

Permalink
Added custom autosuggestion widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
mattirn committed Oct 7, 2019
1 parent 58d6015 commit 4c48c03
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 42 deletions.
162 changes: 143 additions & 19 deletions builtins/src/main/java/org/jline/builtins/Widgets.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@

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;

import org.jline.keymap.KeyMap;
import org.jline.reader.Binding;
Expand All @@ -23,12 +26,13 @@
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 {
private final LineReaderImpl reader;
private final LineReader reader;

public Widgets(LineReader reader) {
this.reader = (LineReaderImpl)reader;
this.reader = reader;
}

public void addWidget(String name, Widget widget) {
Expand All @@ -47,19 +51,23 @@ public boolean apply() {
}
};
}

public void callWidget(String name) {
reader.callWidget(name);
}

public KeyMap<Binding> getKeyMap(String name) {
return reader.getKeyMaps().get(name);
}

public Buffer buffer() {
return reader.getBuffer();
}


public void replaceBuffer(Buffer buffer) {
reader.getBuffer().copyFrom(buffer);
}

public String prevChar() {
return String.valueOf((char)reader.getBuffer().prevChar());
}
Expand All @@ -71,9 +79,17 @@ public String currChar() {
public String lastBinding() {
return reader.getLastBinding();
}

public void putString(String string) {
reader.putString(string);
reader.getBuffer().write(string);
}

public String tailTip() {
return reader.getTailTip();
}

public void enableTailTip(boolean enable) {
reader.enableTailTip(enable);
}

public static class AutopairWidgets extends Widgets {
Expand Down Expand Up @@ -130,13 +146,13 @@ public AutopairWidgets(LineReader reader, boolean addCurlyBrackets) {

KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
for (Map.Entry<String, String> p: pairs.entrySet()) {
defaultBindings.put(p.getKey(), map.getBound(p.getKey()));
defaultBindings.put(p.getKey(), map.getBound(p.getKey()));
if (!p.getKey().equals(p.getValue())) {
defaultBindings.put(p.getValue(), map.getBound(p.getValue()));
defaultBindings.put(p.getValue(), map.getBound(p.getValue()));
}
}
defaultBindings.put(ctrl('H'), map.getBound(ctrl('H')));
defaultBindings.put(del(), map.getBound(del()));
defaultBindings.put(del(), map.getBound(del()));
}

/*
Expand Down Expand Up @@ -179,7 +195,7 @@ && canDelete(prevChar())) {
}

public boolean toggleKeyBindings() {
if(autopair) {
if (autopair) {
defaultBindings();
} else {
autopairBindings();
Expand All @@ -191,9 +207,6 @@ public boolean toggleKeyBindings() {
*
*/
private void autopairBindings() {
if (autopair) {
return;
}
KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
for (Map.Entry<String, String> p: pairs.entrySet()) {
map.bind(new Reference("autopair-insert"), p.getKey());
Expand All @@ -207,9 +220,6 @@ private void autopairBindings() {
}

private void defaultBindings() {
if (!autopair) {
return;
}
KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
for (Map.Entry<String, String> p: pairs.entrySet()) {
map.bind(defaultBindings.get(p.getKey()), p.getKey());
Expand Down Expand Up @@ -283,7 +293,7 @@ private boolean balanced(String d) {
}

private boolean boundary(String lb, String rb) {
if((lb.length() > 0 && prevChar().matches(lb))
if ((lb.length() > 0 && prevChar().matches(lb))
||
(rb.length() > 0 && currChar().matches(rb))) {
return true;
Expand Down Expand Up @@ -312,4 +322,118 @@ private boolean nexToBoundary(String d) {
return false;
}
}

public static class AutosuggestionWidgets extends Widgets {
private final Map<Reference, Set<String>> defaultBindings = new HashMap<>();
private boolean autosuggestion = false;

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-toggle", this::toggleKeyBindings);
KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
for (Map.Entry<String, Binding> bound : map.getBoundKeys().entrySet()) {
if (bound.getValue() instanceof Reference) {
Reference w = (Reference)bound.getValue();
if (w.name().equals(LineReader.FORWARD_CHAR)){
addKeySequence(w, bound.getKey());
} else if (w.name().equals(LineReader.END_OF_LINE)){
addKeySequence(w, bound.getKey());
} else if (w.name().equals(LineReader.FORWARD_WORD)){
addKeySequence(w, bound.getKey());
}
}
}
}

private void addKeySequence(Reference widget, String keySequence) {
if (!defaultBindings.containsKey(widget)) {
defaultBindings.put(widget, new HashSet<String>());
}
defaultBindings.get(widget).add(keySequence);
}
/*
* Widgets
*/
public boolean partialAccept() {
Buffer buffer = buffer();
if (buffer.cursor() == buffer.length()) {
int curPos = buffer.cursor();
buffer.write(tailTip());
buffer.cursor(curPos);
replaceBuffer(buffer);
callWidget(LineReader.FORWARD_WORD);
Buffer newBuf = new BufferImpl();
newBuf.write(buffer().substring(0, buffer().cursor()));
replaceBuffer(newBuf);
} else {
callWidget(LineReader.FORWARD_WORD);
}
return true;
}

public boolean autosuggestForwardChar() {
return accept(LineReader.FORWARD_CHAR);
}

public boolean autosuggestEndOfLine() {
return accept(LineReader.END_OF_LINE);
}

public boolean toggleKeyBindings() {
if (autosuggestion) {
defaultBindings();
} else {
autosuggestionBindings();
}
enableTailTip(autosuggestion);
return autosuggestion;
}


private boolean accept(String widget) {
Buffer buffer = buffer();
if (buffer.cursor() == buffer.length()) {
putString(tailTip());
} else {
callWidget(widget);
}
return true;
}
/*
* key bindings...
*
*/
private void autosuggestionBindings() {
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);
}
} else if (entry.getKey().name().equals(LineReader.END_OF_LINE)) {
for (String s: entry.getValue()) {
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);
}
}
}
autosuggestion = true;
}

private void defaultBindings() {
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;
}
}
}
48 changes: 29 additions & 19 deletions builtins/src/test/java/org/jline/example/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.jline.builtins.Options.HelpException;
import org.jline.builtins.TTop;
import org.jline.builtins.Widgets.AutopairWidgets;
import org.jline.builtins.Widgets.AutosuggestionWidgets;
import org.jline.keymap.KeyMap;
import org.jline.reader.*;
import org.jline.reader.impl.DefaultParser;
Expand Down Expand Up @@ -85,25 +86,26 @@ public static void help() {
String[] help = {
"List of available commands:"
, " Builtin:"
, " complete UNAVAILABLE"
, " history list history of commands"
, " keymap manipulate keymaps"
, " less file pager"
, " nano nano editor"
, " setopt set options"
, " tmux UNAVAILABLE"
, " ttop display and update sorted information about threads"
, " unsetopt unset options"
, " widget UNAVAILABLE"
, " autopair toggle brackets/quotes autopair key bindings"
, " complete UNAVAILABLE"
, " history list history of commands"
, " keymap manipulate keymaps"
, " less file pager"
, " nano nano editor"
, " setopt set options"
, " tmux UNAVAILABLE"
, " ttop display and update sorted information about threads"
, " unsetopt unset options"
, " widget UNAVAILABLE"
, " autopair toggle brackets/quotes autopair key bindings"
, " autosuggestion toggle autosuggestion key bindings"
, " Example:"
, " cls clear screen"
, " help list available commands"
, " exit exit from example app"
, " set set lineReader variable"
, " sleep sleep 3 seconds"
, " testkey display key events"
, " tput set terminal capability"
, " cls clear screen"
, " help list available commands"
, " exit exit from example app"
, " set set lineReader variable"
, " sleep sleep 3 seconds"
, " testkey display key events"
, " tput set terminal capability"
, " Additional help:"
, " <command> --help"};
for (String u: help) {
Expand Down Expand Up @@ -302,7 +304,7 @@ public void complete(LineReader reader, ParsedLine line, List<Candidate> candida
.variable(LineReader.SECONDARY_PROMPT_PATTERN, "%M%P > ")
.build();
AutopairWidgets autopairWidgets = new AutopairWidgets(reader);

AutosuggestionWidgets autosuggestionWidgets = new AutosuggestionWidgets(reader);
if (timer) {
Executors.newScheduledThreadPool(1)
.scheduleAtFixedRate(() -> {
Expand Down Expand Up @@ -453,6 +455,14 @@ else if ("autopair".equals(pl.word())) {
terminal.writer().println("unbounded.");
}
}
else if ("autosuggestion".equals(pl.word())) {
terminal.writer().print("Autosuggestion widgets are ");
if (autosuggestionWidgets.toggleKeyBindings()) {
terminal.writer().println("bounded.");
} else {
terminal.writer().println("unbounded.");
}
}
else if ("help".equals(pl.word()) || "?".equals(pl.word())) {
help();
}
Expand Down
6 changes: 6 additions & 0 deletions reader/src/main/java/org/jline/reader/LineReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -662,4 +662,10 @@ enum RegionType {

void editAndAddInBuffer(File file) throws Exception;

public String getLastBinding();

public String getTailTip();

public void enableTailTip(boolean enable);

}
Loading

1 comment on commit 4c48c03

@mattirn
Copy link
Collaborator Author

@mattirn mattirn commented on 4c48c03 Oct 7, 2019

Choose a reason for hiding this comment

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

Autosuggestion widgets suggests commands as you type based on command history. Probably it can be make working also with command completers. #254

Please sign in to comment.