Skip to content

Commit

Permalink
TailTipWidgets added argument descriptions, fixes #254
Browse files Browse the repository at this point in the history
  • Loading branch information
mattirn committed Oct 10, 2019
1 parent 55567b0 commit 3462231
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 52 deletions.
187 changes: 161 additions & 26 deletions builtins/src/main/java/org/jline/builtins/Widgets.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.jline.reader.Reference;
import org.jline.reader.Widget;
import org.jline.reader.impl.BufferImpl;
import org.jline.utils.AttributedString;
import org.jline.utils.Status;

public abstract class Widgets {
private final LineReader reader;
Expand Down Expand Up @@ -103,6 +105,30 @@ public void setSuggestionType(SuggestionType type) {
reader.setAutosuggestion(type);
}

public void addDescription(List<AttributedString> desc) {
Status.getStatus(reader.getTerminal()).update(desc);
}

public void clearDescription() {
clearDescription(0);
}

public void clearDescription(int size) {
if (size > 0) {
List<AttributedString> as = new ArrayList<>();
for (int i = 0; i < size; i++) {
as.add(new AttributedString(""));
}
addDescription(as);
} else {
Status status = Status.getStatus(reader.getTerminal(), false);
if (status != null) {
status.clear();
}
}
}


public static class AutopairWidgets extends Widgets {
/*
* Inspired by zsh-autopair
Expand Down Expand Up @@ -456,23 +482,40 @@ public void defaultBindings() {
}

public static class TailTipWidgets extends Widgets {
public enum TipType {
TAIL_TIP,
COMPLETER,
COMBINED
}
private final Map<Reference, Set<String>> defaultBindings = new HashMap<>();
private boolean autosuggestion = false;
private Map<String,List<String>> tailTips = new HashMap<>();
private boolean withCompleter;
private Map<String,List<ArgDesc>> tailTips = new HashMap<>();
private TipType tipType;
private int descriptionSize = 0;

public TailTipWidgets(LineReader reader, Map<String,List<ArgDesc>> tailTips) {
this(reader, tailTips, 0, TipType.COMBINED);
}

public TailTipWidgets(LineReader reader, Map<String,List<ArgDesc>> tailTips, TipType tipType) {
this(reader, tailTips, 0, tipType);
}

public TailTipWidgets(LineReader reader, Map<String,List<String>> tailTips) {
this(reader, tailTips, true);
public TailTipWidgets(LineReader reader, Map<String,List<ArgDesc>> tailTips, int descriptionSize) {
this(reader, tailTips, descriptionSize, TipType.COMBINED);
}

public TailTipWidgets(LineReader reader, Map<String, List<String>> tailTips, boolean withCompleter) {
public TailTipWidgets(LineReader reader, Map<String,List<ArgDesc>> tailTips, int descriptionSize, TipType tipType) {
super(reader);
this.tailTips.putAll(tailTips);
this.withCompleter = withCompleter;
this.descriptionSize = descriptionSize;
this.tipType = tipType;
clearDescription(descriptionSize);
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-expand-or-complete", this::tailtipComplete);
addWidget("tailtip-toggle", this::toggleKeyBindings);
KeyMap<Binding> map = getKeyMap(LineReader.MAIN);
for (Map.Entry<String, Binding> bound : map.getBoundKeys().entrySet()) {
Expand All @@ -484,6 +527,8 @@ public TailTipWidgets(LineReader reader, Map<String, List<String>> tailTips, boo
addKeySequence(w, bound.getKey());
} else if (w.name().equals(LineReader.DELETE_CHAR)){
addKeySequence(w, bound.getKey());
} else if (w.name().equals(LineReader.EXPAND_OR_COMPLETE)){
addKeySequence(w, bound.getKey());
}
}
}
Expand All @@ -496,18 +541,49 @@ private void addKeySequence(Reference widget, String keySequence) {
defaultBindings.get(widget).add(keySequence);
}

public void setDescriptionSize(int descriptionSize) {
this.descriptionSize = descriptionSize;
clearDescription(descriptionSize);
}

public int getDescriptionSize() {
return descriptionSize;
}

public void setTipType(TipType type) {
this.tipType = type;
if (tipType == TipType.TAIL_TIP) {
setSuggestionType(SuggestionType.TAIL_TIP);
} else {
setSuggestionType(SuggestionType.COMPLETER);
}
}

public TipType getTipType() {
return tipType;
}

public boolean isActive() {
return autosuggestion;
}

/*
* widgets
*/
public boolean tailtipComplete() {
return doTailTip(LineReader.EXPAND_OR_COMPLETE);
}

public boolean tailtipAcceptLine() {
if (withCompleter){
if (tipType != TipType.TAIL_TIP){
setSuggestionType(SuggestionType.COMPLETER);
}
clearDescription();
return clearTailTip(LineReader.ACCEPT_LINE);
}

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

private boolean clearTailTip(String widget) {
Expand All @@ -527,33 +603,57 @@ public boolean tailtipInsert() {

private boolean doTailTip(String widget) {
Buffer buffer = buffer();
if (buffer.length() == buffer.cursor()) {
callWidget(widget);
if (buffer.length() == buffer.cursor()
&& ((!widget.equals(LineReader.BACKWARD_DELETE_CHAR) && prevChar().equals(" ")) ||
(widget.equals(LineReader.BACKWARD_DELETE_CHAR) && !prevChar().equals(" ")))) {
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()) {
int bpsize = bp.size() + (widget.equals(LineReader.BACKWARD_DELETE_CHAR) ? -1 : 0);
List<AttributedString> desc = new ArrayList<>();
if (bpsize > 0 && tailTips.containsKey(bp.get(0))) {
List<ArgDesc> params = tailTips.get(bp.get(0));
setSuggestionType(tipType == TipType.COMPLETER ? SuggestionType.COMPLETER : SuggestionType.TAIL_TIP);
if (bpsize - 1 < params.size()) {
desc = params.get(bpsize - 1).getDescription();
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;
for (int i = bpsize - 1; i < params.size(); i++) {
tip.append(params.get(i).getName());
tip.append(" ");
}
setTailTip(tip.toString());
} else if (params.get(params.size() - 1).charAt(0) == '[') {
setTailTip(params.get(params.size() - 1));
} else if (params.get(params.size() - 1).getName().charAt(0) == '[') {
setTailTip(params.get(params.size() - 1).getName());
desc = params.get(params.size() - 1).getDescription();
}
} else {
setTailTip("");
if (tipType != TipType.TAIL_TIP){
setSuggestionType(SuggestionType.COMPLETER);
}
} else if (withCompleter){
setSuggestionType(SuggestionType.COMPLETER);
}
doDescription(desc);
}
callWidget(widget);
return true;
}

private void doDescription(List<AttributedString> desc) {
if (descriptionSize == 0) {
return;
}
if (desc.isEmpty()) {
clearDescription();
} else if (desc.size() == descriptionSize) {
addDescription(desc);
} else if (desc.size() > descriptionSize) {
addDescription(desc.subList(0, descriptionSize));
} else if (desc.size() < descriptionSize) {
while (desc.size() != descriptionSize) {
desc.add(new AttributedString(""));
}
addDescription(desc);
}
}

public boolean toggleKeyBindings() {
if (autosuggestion) {
defaultBindings();
Expand Down Expand Up @@ -588,9 +688,14 @@ public void autosuggestionBindings() {
map.bind(new Reference("_tailtip-delete-char"), s);
}
}
if (entry.getKey().name().equals(LineReader.EXPAND_OR_COMPLETE)) {
for (String s: entry.getValue()) {
map.bind(new Reference("_tailtip-expand-or-complete"), s);
}
}
}
map.bind(new Reference("_tailtip-insert"), " ");
if (withCompleter) {
if (tipType != TipType.TAIL_TIP) {
setSuggestionType(SuggestionType.COMPLETER);
} else {
setSuggestionType(SuggestionType.TAIL_TIP);
Expand All @@ -614,4 +719,34 @@ public void defaultBindings() {
}
}

public static class ArgDesc {
private String name;
private List<AttributedString> description = new ArrayList<AttributedString>();

public ArgDesc(String name) {
this(name, new ArrayList<AttributedString>());
}

public ArgDesc(String name, List<AttributedString> description) {
this.name = name;
this.description.addAll(description);
}

public String getName() {
return name;
}

public List<AttributedString> getDescription() {
return description;
}

public static List<ArgDesc> doArgNames(List<String> names) {
List<ArgDesc> out = new ArrayList<>();
for (String n: names) {
out.add(new ArgDesc(n));
}
return out;
}
}

}
47 changes: 40 additions & 7 deletions builtins/src/test/java/org/jline/example/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.jline.builtins.Widgets.AutopairWidgets;
import org.jline.builtins.Widgets.AutosuggestionWidgets;
import org.jline.builtins.Widgets.TailTipWidgets;
import org.jline.builtins.Widgets.TailTipWidgets.TipType;
import org.jline.builtins.Widgets.ArgDesc;
import org.jline.keymap.KeyMap;
import org.jline.reader.*;
import org.jline.reader.LineReader.SuggestionType;
Expand Down Expand Up @@ -99,7 +101,7 @@ public static void help() {
, " unsetopt unset options"
, " widget UNAVAILABLE"
, " autopair toggle brackets/quotes autopair key bindings"
, " autosuggestion history, completer, tailtip or none"
, " autosuggestion history, completer, tailtip [tailtip|completer|combined] or none"
, " Example:"
, " cls clear screen"
, " help list available commands"
Expand Down Expand Up @@ -307,9 +309,22 @@ 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);
Map<String, List<ArgDesc>> tailTips = new HashMap<>();
tailTips.put("tail", ArgDesc.doArgNames(Arrays.asList("param1", "param2", "[paramN...]")));
tailTips.put("foo11", Arrays.asList(
new ArgDesc("param1",Arrays.asList(new AttributedString("line 1")
, new AttributedString("line 2")
, new AttributedString("line 3")
, new AttributedString("line 4")
, new AttributedString("line 5")
, new AttributedString("line 6")
))
, new ArgDesc("param2",Arrays.asList(new AttributedString("line 1")
, new AttributedString("line 2")
))
, new ArgDesc("param3", new ArrayList<>())
));
TailTipWidgets tailtipWidgets = new TailTipWidgets(reader, tailTips, TipType.COMPLETER);
if (timer) {
Executors.newScheduledThreadPool(1)
.scheduleAtFixedRate(() -> {
Expand Down Expand Up @@ -461,14 +476,25 @@ else if ("autopair".equals(pl.word())) {
}
}
else if ("autosuggestion".equals(pl.word())) {
if (pl.words().size() == 2) {
if (pl.words().size() > 1) {
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();
tailtipWidgets.setDescriptionSize(5);
if (pl.words().size() > 2) {
String mode = pl.words().get(2);
if (mode.toLowerCase().startsWith("tai")) {
tailtipWidgets.setTipType(TipType.TAIL_TIP);
} else if (mode.toLowerCase().startsWith("comp")) {
tailtipWidgets.setTipType(TipType.COMPLETER);
} else if (mode.toLowerCase().startsWith("comb")) {
tailtipWidgets.setTipType(TipType.COMBINED);
}
}
} else if (type.toLowerCase().startsWith("com")) {
autosuggestionWidgets.defaultBindings();
tailtipWidgets.defaultBindings();
Expand All @@ -478,10 +504,17 @@ else if ("autosuggestion".equals(pl.word())) {
tailtipWidgets.defaultBindings();
reader.setAutosuggestion(SuggestionType.NONE);
} else {
terminal.writer().println("Usage: autosuggestion history|completer|none");
terminal.writer().println("Usage: autosuggestion history|completer|tailtip|none");
}
} else {
terminal.writer().println("Autosuggestion: " + reader.getAutosuggestion());
if (tailtipWidgets.isActive()) {
terminal.writer().println("Autosuggestion: tailtip/" + tailtipWidgets.getTipType());
} else {
terminal.writer().println("Autosuggestion: " + reader.getAutosuggestion());
}
}
if (!tailtipWidgets.isActive()) {
Status.getStatus(terminal).update(null);
}
}
else if ("help".equals(pl.word()) || "?".equals(pl.word())) {
Expand Down

0 comments on commit 3462231

Please sign in to comment.