From 2ae5901b6452e306d2c8e514fa0ad03cd79314a6 Mon Sep 17 00:00:00 2001 From: mattirn Date: Mon, 14 Oct 2019 17:12:47 +0200 Subject: [PATCH] TailTipWidget: added descriptions of command options --- .../main/java/org/jline/builtins/Widgets.java | 86 ++++++++++++++++--- .../test/java/org/jline/example/Example.java | 14 ++- .../org/jline/reader/impl/LineReaderImpl.java | 6 +- 3 files changed, 87 insertions(+), 19 deletions(-) diff --git a/builtins/src/main/java/org/jline/builtins/Widgets.java b/builtins/src/main/java/org/jline/builtins/Widgets.java index c5a8f78e6..c37e1524a 100644 --- a/builtins/src/main/java/org/jline/builtins/Widgets.java +++ b/builtins/src/main/java/org/jline/builtins/Widgets.java @@ -15,13 +15,12 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import org.jline.keymap.KeyMap; import org.jline.reader.Binding; import org.jline.reader.Buffer; -import org.jline.reader.EndOfFileException; import org.jline.reader.LineReader; -import org.jline.reader.UserInterruptException; import org.jline.reader.LineReader.SuggestionType; import org.jline.reader.Reference; import org.jline.reader.Widget; @@ -561,7 +560,7 @@ public enum TipType { COMBINED } private boolean enabled = false; - private Map> tailTips = new HashMap<>(); + private Map tailTips = new HashMap<>(); private TipType tipType; private int descriptionSize = 0; @@ -571,10 +570,10 @@ public enum TipType { * Status bar for argument descriptions will not be created. * * @param reader LineReader. - * @param tailTips Commands positional argument descriptions. + * @param tailTips Commands options and positional argument descriptions. * @throws IllegalStateException If widgets are already created. */ - public TailTipWidgets(LineReader reader, Map> tailTips) { + public TailTipWidgets(LineReader reader, Map tailTips) { this(reader, tailTips, 0, TipType.COMBINED); } @@ -583,11 +582,11 @@ public TailTipWidgets(LineReader reader, Map> tailTips) { * Status bar for argument descriptions will not be created. * * @param reader LineReader. - * @param tailTips Commands positional argument descriptions. + * @param tailTips Commands options and positional argument descriptions. * @param tipType Defines which data will be used for suggestions. * @throws IllegalStateException If widgets are already created. */ - public TailTipWidgets(LineReader reader, Map> tailTips, TipType tipType) { + public TailTipWidgets(LineReader reader, Map tailTips, TipType tipType) { this(reader, tailTips, 0, tipType); } @@ -596,11 +595,11 @@ public TailTipWidgets(LineReader reader, Map> tailTips, Tip * positional argument names. If argument descriptions do not exists command completer data will be used. * * @param reader LineReader. - * @param tailTips Commands positional argument descriptions. + * @param tailTips Commands options and positional argument descriptions. * @param descriptionSize Size of the status bar. * @throws IllegalStateException If widgets are already created. */ - public TailTipWidgets(LineReader reader, Map> tailTips, int descriptionSize) { + public TailTipWidgets(LineReader reader, Map tailTips, int descriptionSize) { this(reader, tailTips, descriptionSize, TipType.COMBINED); } @@ -613,7 +612,7 @@ public TailTipWidgets(LineReader reader, Map> tailTips, int * @param tipType Defines which data will be used for suggestions. * @throws IllegalStateException If widgets are already created. */ - public TailTipWidgets(LineReader reader, Map> tailTips, int descriptionSize, TipType tipType) { + public TailTipWidgets(LineReader reader, Map tailTips, int descriptionSize, TipType tipType) { super(reader); if (existsWidget(TT_ACCEPT_LINE)) { throw new IllegalStateException("TailTipWidgets already created!"); @@ -704,16 +703,27 @@ private boolean doTailTip(String widget) { Buffer buffer = buffer(); callWidget(widget); if (buffer.length() == buffer.cursor() - && ((!widget.endsWith(LineReader.BACKWARD_DELETE_CHAR) && prevChar().equals(" ")) || + && ((!widget.endsWith(LineReader.BACKWARD_DELETE_CHAR) && (prevChar().equals(" ") || prevChar().equals("=") || prevChar().equals("-"))) || (widget.endsWith(LineReader.BACKWARD_DELETE_CHAR) && !prevChar().equals(" ")))) { List bp = args(buffer.toString()); - int bpsize = bp.size() + (widget.endsWith(LineReader.BACKWARD_DELETE_CHAR) ? -1 : 0); + int argnum = 0; + for (String a: bp) { + if (!a.startsWith("-")) { + argnum++; + } + } + String lastArg = !prevChar().equals(" ") ? bp.get(bp.size() - 1) : ""; + int bpsize = argnum + (!lastArg.startsWith("-") && widget.endsWith(LineReader.BACKWARD_DELETE_CHAR) ? -1 : 0); List desc = new ArrayList<>(); if (bpsize > 0 && tailTips.containsKey(bp.get(0))) { - List params = tailTips.get(bp.get(0)); + List params = tailTips.get(bp.get(0)).getArgsDesc(); setSuggestionType(tipType == TipType.COMPLETER ? SuggestionType.COMPLETER : SuggestionType.TAIL_TIP); if (bpsize - 1 < params.size()) { - desc = params.get(bpsize - 1).getDescription(); + if (lastArg.startsWith("-")) { + desc = tailTips.get(bp.get(0)).getOptionDescription(lastArg); + } else { + desc = params.get(bpsize - 1).getDescription(); + } StringBuilder tip = new StringBuilder(); for (int i = bpsize - 1; i < params.size(); i++) { tip.append(params.get(i).getName()); @@ -788,6 +798,8 @@ private boolean defaultBindings() { aliasWidget("." + LineReader.EXPAND_OR_COMPLETE, LineReader.EXPAND_OR_COMPLETE); KeyMap map = getKeyMap(LineReader.MAIN); map.bind(new Reference(LineReader.SELF_INSERT), " "); + map.bind(new Reference(LineReader.SELF_INSERT), "="); + map.bind(new Reference(LineReader.SELF_INSERT), "-"); setSuggestionType(SuggestionType.NONE); if (autopairEnabled()) { callWidget(AP_TOGGLE); @@ -807,6 +819,8 @@ private void customBindings() { aliasWidget("_tailtip-expand-or-complete", LineReader.EXPAND_OR_COMPLETE); KeyMap map = getKeyMap(LineReader.MAIN); map.bind(new Reference("_tailtip-insert"), " "); + map.bind(new Reference("_tailtip-insert"), "="); + map.bind(new Reference("_tailtip-insert"), "-"); if (tipType != TipType.TAIL_TIP) { setSuggestionType(SuggestionType.COMPLETER); } else { @@ -847,4 +861,48 @@ public static List doArgNames(List names) { } } + public static class CmdDesc { + private List argsDesc; + private TreeMap> optsDesc; + + public CmdDesc(List argsDesc) { + this(argsDesc, new HashMap<>()); + } + + public CmdDesc(List argsDesc, Map> optsDesc) { + this.argsDesc = new ArrayList<>(argsDesc); + this.optsDesc = new TreeMap<>(optsDesc); + } + + public List getArgsDesc() { + return argsDesc; + } + + public List getOptionDescription(String opt) { + List out = new ArrayList<>(); + if (!opt.startsWith("-")) { + return out; + } else if (opt.startsWith("--")) { + int ind = opt.indexOf("="); + if (ind > 0) { + opt = opt.substring(0, ind); + } + } + if (optsDesc.containsKey(opt)) { + out = new ArrayList<>(optsDesc.get(opt)); + } else { + for (Map.Entry> entry: optsDesc.entrySet()) { + if (entry.getKey().startsWith(opt)) { + AttributedStringBuilder asb = new AttributedStringBuilder(); + asb.append(entry.getKey()); + asb.append("\t"); + asb.append(entry.getValue().get(0)); + out.add(asb.toAttributedString()); + } + } + } + return out; + } + } + } diff --git a/builtins/src/test/java/org/jline/example/Example.java b/builtins/src/test/java/org/jline/example/Example.java index bfd2e8fc9..40466288a 100644 --- a/builtins/src/test/java/org/jline/example/Example.java +++ b/builtins/src/test/java/org/jline/example/Example.java @@ -29,6 +29,7 @@ import org.jline.builtins.Widgets.TailTipWidgets; import org.jline.builtins.Widgets.TailTipWidgets.TipType; import org.jline.builtins.Widgets.ArgDesc; +import org.jline.builtins.Widgets.CmdDesc; import org.jline.keymap.KeyMap; import org.jline.reader.*; import org.jline.reader.LineReader.Option; @@ -319,9 +320,14 @@ public void complete(LineReader reader, ParsedLine line, List candida .build(); AutopairWidgets autopairWidgets = new AutopairWidgets(reader); AutosuggestionWidgets autosuggestionWidgets = new AutosuggestionWidgets(reader); - Map> tailTips = new HashMap<>(); - tailTips.put("foo12", ArgDesc.doArgNames(Arrays.asList("param1", "param2", "[paramN...]"))); - tailTips.put("foo11", Arrays.asList( + Map tailTips = new HashMap<>(); + Map> optDesc = new HashMap<>(); + optDesc.put("--option1", Arrays.asList(new AttributedString("option1 description..."))); + optDesc.put("--option2", Arrays.asList(new AttributedString("option2 description..."))); + optDesc.put("--option3", Arrays.asList(new AttributedString("option3 description...") + , new AttributedString("line2"))); + tailTips.put("foo12", new CmdDesc(ArgDesc.doArgNames(Arrays.asList("param1", "param2", "[paramN...]")))); + tailTips.put("foo11", new CmdDesc(Arrays.asList( new ArgDesc("param1",Arrays.asList(new AttributedString("Param1 description...") , new AttributedString("line 2: This is a very long line that does exceed the terminal width." +" The line will be truncated automatically (by Status class) be before printing out.") @@ -334,7 +340,7 @@ public void complete(LineReader reader, ParsedLine line, List candida , new AttributedString("line 2") )) , new ArgDesc("param3", new ArrayList<>()) - )); + ), optDesc)); TailTipWidgets tailtipWidgets = new TailTipWidgets(reader, tailTips, TipType.COMPLETER); if (timer) { Executors.newScheduledThreadPool(1) diff --git a/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java b/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java index a72dd1f9d..b44311379 100644 --- a/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java +++ b/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java @@ -4007,8 +4007,12 @@ public AttributedString getDisplayedBufferWithPrompts(List sec if (buf.prevChar() != ' ') { if (!tailTip.startsWith("[")) { int idx = tailTip.indexOf(' '); - if (idx > 0) { + int idb = buf.toString().lastIndexOf(' '); + int idd = buf.toString().lastIndexOf('-'); + if (idx > 0 && ((idb == -1 && idb == idd) || (idb >= 0 && idb > idd))) { tailTip = tailTip.substring(idx); + } else if (idb >= 0 && idb < idd) { + sb.append(" "); } } else { sb.append(" ");