Skip to content

Commit

Permalink
Added TailTipWidgets, fixes #394
Browse files Browse the repository at this point in the history
  • Loading branch information
mattirn committed Oct 9, 2019
1 parent 969fa1a commit 55567b0
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 22 deletions.
180 changes: 176 additions & 4 deletions builtins/src/main/java/org/jline/builtins/Widgets.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public void replaceBuffer(Buffer buffer) {
reader.getBuffer().copyFrom(buffer);
}

public List<String> args(String line) {
return reader.getParser().parse(line, 0).words();
}

public String prevChar() {
return String.valueOf((char)reader.getBuffer().prevChar());
}
Expand All @@ -87,8 +91,16 @@ public String tailTip() {
return reader.getTailTip();
}

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

public void clearTailTip() {
reader.setTailTip("");
}

public void setSuggestionType(SuggestionType type) {
reader.setAutosuggestion(type);
}

public static class AutopairWidgets extends Widgets {
Expand Down Expand Up @@ -425,7 +437,7 @@ public void autosuggestionBindings() {
}
}
autosuggestion = true;
enableTailTip(autosuggestion);
setSuggestionType(SuggestionType.HISTORY);
}

public void defaultBindings() {
Expand All @@ -439,7 +451,167 @@ public void defaultBindings() {
}
}
autosuggestion = false;
enableTailTip(autosuggestion);
setSuggestionType(SuggestionType.NONE);
}
}

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

public TailTipWidgets(LineReader reader, Map<String,List<String>> tailTips) {
this(reader, tailTips, true);
}

public TailTipWidgets(LineReader reader, Map<String, List<String>> tailTips, boolean withCompleter) {
super(reader);
this.tailTips.putAll(tailTips);
this.withCompleter = withCompleter;
addWidget("_tailtip-accept-line", this::tailtipAcceptLine);
addWidget("_tailtip-insert", this::tailtipInsert);
addWidget("_tailtip-backward-delete-char", this::tailtipBackwardDelete);
addWidget("_tailtip-delete-char", this::tailtipDelete);
addWidget("tailtip-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.ACCEPT_LINE)){
addKeySequence(w, bound.getKey());
} else if (w.name().equals(LineReader.BACKWARD_DELETE_CHAR)){
addKeySequence(w, bound.getKey());
} else if (w.name().equals(LineReader.DELETE_CHAR)){
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 tailtipAcceptLine() {
if (withCompleter){
setSuggestionType(SuggestionType.COMPLETER);
}
return clearTailTip(LineReader.ACCEPT_LINE);
}

public boolean tailtipBackwardDelete() {
return clearTailTip(LineReader.BACKWARD_DELETE_CHAR);
}

private boolean clearTailTip(String widget) {
clearTailTip();
callWidget(widget);
return true;
}

public boolean tailtipDelete() {
clearTailTip();
return doTailTip(LineReader.DELETE_CHAR);
}

public boolean tailtipInsert() {
return doTailTip(LineReader.SELF_INSERT);
}

private boolean doTailTip(String widget) {
Buffer buffer = buffer();
if (buffer.length() == buffer.cursor()) {
List<String> bp = args(buffer.toString());
if (bp.size() > 0 && tailTips.containsKey(bp.get(0))) {
setSuggestionType(SuggestionType.TAIL_TIP);
List<String> params = tailTips.get(bp.get(0));
if (bp.size() - 1 < params.size()) {
StringBuilder tip = new StringBuilder();
boolean first = true;
for (int i = bp.size() - 1; i < params.size(); i++) {
if (!first) {
tip.append(" ");
}
tip.append(params.get(i));
first = false;
}
setTailTip(tip.toString());
} else if (params.get(params.size() - 1).charAt(0) == '[') {
setTailTip(params.get(params.size() - 1));
}
} else if (withCompleter){
setSuggestionType(SuggestionType.COMPLETER);
}
}
callWidget(widget);
return true;
}

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

/*
* key bindings...
*
*/
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.ACCEPT_LINE)) {
for (String s: entry.getValue()) {
map.bind(new Reference("_tailtip-accept-line"), s);
}
}
if (entry.getKey().name().equals(LineReader.BACKWARD_DELETE_CHAR)) {
for (String s: entry.getValue()) {
map.bind(new Reference("_tailtip-backward-delete-char"), s);
}
}
if (entry.getKey().name().equals(LineReader.DELETE_CHAR)) {
for (String s: entry.getValue()) {
map.bind(new Reference("_tailtip-delete-char"), s);
}
}
}
map.bind(new Reference("_tailtip-insert"), " ");
if (withCompleter) {
setSuggestionType(SuggestionType.COMPLETER);
} else {
setSuggestionType(SuggestionType.TAIL_TIP);
}
autosuggestion = true;
}

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);
}
}
map.bind(new Reference(LineReader.SELF_INSERT), " ");
setSuggestionType(SuggestionType.NONE);
autosuggestion = false;
}
}

}
25 changes: 15 additions & 10 deletions builtins/src/test/java/org/jline/example/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.jline.builtins.TTop;
import org.jline.builtins.Widgets.AutopairWidgets;
import org.jline.builtins.Widgets.AutosuggestionWidgets;
import org.jline.builtins.Widgets.TailTipWidgets;
import org.jline.keymap.KeyMap;
import org.jline.reader.*;
import org.jline.reader.LineReader.SuggestionType;
Expand Down Expand Up @@ -98,7 +99,7 @@ public static void help() {
, " unsetopt unset options"
, " widget UNAVAILABLE"
, " autopair toggle brackets/quotes autopair key bindings"
, " autosuggestion history, completer or none"
, " autosuggestion history, completer, tailtip or none"
, " Example:"
, " cls clear screen"
, " help list available commands"
Expand Down Expand Up @@ -202,7 +203,7 @@ public static void main(String[] args) throws IOException {
break;
case "argument":
completer = new ArgumentCompleter(
new StringsCompleter("foo11", "foo12", "foo13"),
new StringsCompleter("foo11", "foo12", "foo13", "tail"),
new StringsCompleter("foo21", "foo22", "foo23"),
new Completer() {
@Override
Expand Down Expand Up @@ -306,6 +307,9 @@ public void complete(LineReader reader, ParsedLine line, List<Candidate> candida
.build();
AutopairWidgets autopairWidgets = new AutopairWidgets(reader);
AutosuggestionWidgets autosuggestionWidgets = new AutosuggestionWidgets(reader);
Map<String, List<String>> tailTips = new HashMap<>();
tailTips.put("tail", Arrays.asList("param1", "param2", "[paramN...]"));
TailTipWidgets tailtipWidgets = new TailTipWidgets(reader, tailTips);
if (timer) {
Executors.newScheduledThreadPool(1)
.scheduleAtFixedRate(() -> {
Expand Down Expand Up @@ -460,18 +464,19 @@ else if ("autosuggestion".equals(pl.word())) {
if (pl.words().size() == 2) {
String type = pl.words().get(1);
if (type.toLowerCase().startsWith("his")) {
tailtipWidgets.defaultBindings();
autosuggestionWidgets.autosuggestionBindings();
} else if (type.toLowerCase().startsWith("tai")) {
autosuggestionWidgets.defaultBindings();
tailtipWidgets.autosuggestionBindings();
} else if (type.toLowerCase().startsWith("com")) {
if (reader.getAutosuggestion() == SuggestionType.HISTORY) {
autosuggestionWidgets.defaultBindings();
}
autosuggestionWidgets.defaultBindings();
tailtipWidgets.defaultBindings();
reader.setAutosuggestion(SuggestionType.COMPLETER);
} else if (type.toLowerCase().startsWith("non")) {
if (reader.getAutosuggestion() == SuggestionType.HISTORY) {
autosuggestionWidgets.defaultBindings();
} else {
reader.setAutosuggestion(SuggestionType.NONE);
}
autosuggestionWidgets.defaultBindings();
tailtipWidgets.defaultBindings();
reader.setAutosuggestion(SuggestionType.NONE);
} else {
terminal.writer().println("Usage: autosuggestion history|completer|none");
}
Expand Down
13 changes: 8 additions & 5 deletions reader/src/main/java/org/jline/reader/LineReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,8 @@ enum RegionType {
enum SuggestionType {
NONE,
HISTORY,
COMPLETER
COMPLETER,
TAIL_TIP
}

/**
Expand Down Expand Up @@ -668,11 +669,13 @@ enum SuggestionType {

void editAndAddInBuffer(File file) throws Exception;

public String getLastBinding();
String getLastBinding();

public String getTailTip();
String getTailTip();

public void setAutosuggestion(SuggestionType type);
void setTailTip(String tailTip);

public SuggestionType getAutosuggestion();
void setAutosuggestion(SuggestionType type);

SuggestionType getAutosuggestion();
}
29 changes: 26 additions & 3 deletions reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@ public String getTailTip(){
return tailTip;
}

@Override
public void setTailTip(String tailTip) {
this.tailTip = tailTip;
}

@Override
public void runMacro(String macro) {
bindingReader.runMacro(macro);
Expand Down Expand Up @@ -3947,12 +3952,30 @@ public AttributedString getDisplayedBufferWithPrompts(List<AttributedString> sec
if (buf.length() > 0 && buf.length() == buf.cursor()
&& (!lastBinding.equals("\t") || buf.prevChar() == ' ')) {
if (buf.prevChar() == ' ') {
doEmptyList();
clearChoices();
}
listChoices(true);
} else if (!lastBinding.equals("\t")){
doEmptyList();
clearChoices();
}
} else if (autosuggestion == SuggestionType.TAIL_TIP
&& buf.length() == buf.cursor()) {
if (!lastBinding.equals("\t")){
clearChoices();
}
AttributedStringBuilder sb = new AttributedStringBuilder();
if (buf.prevChar() != ' ') {
if (!tailTip.startsWith("[")) {
int idx = tailTip.indexOf(' ');
if (idx > 0) {
tailTip = tailTip.substring(idx);
}
} else {
sb.append(" ");
}
}
sb.styled(AttributedStyle::faint, tailTip);
full.append(sb.toAttributedString());
}
if (post != null) {
full.append("\n");
Expand Down Expand Up @@ -4868,7 +4891,7 @@ && getLastBinding().charAt(0) != ' '
return false;
}

protected boolean doEmptyList() {
protected boolean clearChoices() {
return doList(new ArrayList<Candidate>(), "", false, null, false);
}

Expand Down

0 comments on commit 55567b0

Please sign in to comment.