All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jline.builtins.Completers Maven / Gradle / Ivy

There is a newer version: 3.26.3
Show newest version
/*
 * Copyright (c) 2002-2021, the original author(s).
 *
 * This software is distributable under the BSD license. See the terms of the
 * BSD license in the documentation provided with this software.
 *
 * https://opensource.org/licenses/BSD-3-Clause
 */
package org.jline.builtins;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;

import org.jline.reader.Candidate;
import org.jline.reader.LineReader;
import org.jline.reader.LineReader.Option;
import org.jline.reader.ParsedLine;
import org.jline.reader.impl.completer.NullCompleter;
import org.jline.reader.impl.completer.StringsCompleter;
import org.jline.terminal.Terminal;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.OSUtils;
import org.jline.utils.StyleResolver;

public class Completers {

    public interface CompletionEnvironment {
        Map> getCompletions();

        Set getCommands();

        String resolveCommand(String command);

        String commandName(String command);

        Object evaluate(LineReader reader, ParsedLine line, String func) throws Exception;
    }

    public static class CompletionData {
        public final List options;
        public final String description;
        public final String argument;
        public final String condition;

        public CompletionData(List options, String description, String argument, String condition) {
            this.options = options;
            this.description = description;
            this.argument = argument;
            this.condition = condition;
        }
    }

    public static class Completer implements org.jline.reader.Completer {

        private final CompletionEnvironment environment;

        public Completer(CompletionEnvironment environment) {
            this.environment = environment;
        }

        public void complete(LineReader reader, ParsedLine line, List candidates) {
            if (line.wordIndex() == 0) {
                completeCommand(candidates);
            } else {
                tryCompleteArguments(reader, line, candidates);
            }
        }

        protected void tryCompleteArguments(LineReader reader, ParsedLine line, List candidates) {
            String command = line.words().get(0);
            String resolved = environment.resolveCommand(command);
            Map> comp = environment.getCompletions();
            if (comp != null) {
                List cmd = comp.get(resolved);
                if (cmd != null) {
                    completeCommandArguments(reader, line, candidates, cmd);
                }
            }
        }

        protected void completeCommandArguments(
                LineReader reader, ParsedLine line, List candidates, List completions) {
            for (CompletionData completion : completions) {
                boolean isOption = line.word().startsWith("-");
                String prevOption = line.wordIndex() >= 2
                                && line.words().get(line.wordIndex() - 1).startsWith("-")
                        ? line.words().get(line.wordIndex() - 1)
                        : null;
                String key = UUID.randomUUID().toString();
                boolean conditionValue = true;
                if (completion.condition != null) {
                    Object res = Boolean.FALSE;
                    try {
                        res = environment.evaluate(reader, line, completion.condition);
                    } catch (Throwable t) {
                        // Ignore
                    }
                    conditionValue = isTrue(res);
                }
                if (conditionValue && isOption && completion.options != null) {
                    for (String opt : completion.options) {
                        candidates.add(new Candidate(opt, opt, "options", completion.description, null, key, true));
                    }
                } else if (!isOption
                        && prevOption != null
                        && completion.argument != null
                        && (completion.options != null && completion.options.contains(prevOption))) {
                    Object res = null;
                    try {
                        res = environment.evaluate(reader, line, completion.argument);
                    } catch (Throwable t) {
                        // Ignore
                    }
                    if (res instanceof Candidate) {
                        candidates.add((Candidate) res);
                    } else if (res instanceof String) {
                        candidates.add(new Candidate((String) res, (String) res, null, null, null, null, true));
                    } else if (res instanceof Collection) {
                        for (Object s : (Collection) res) {
                            if (s instanceof Candidate) {
                                candidates.add((Candidate) s);
                            } else if (s instanceof String) {
                                candidates.add(new Candidate((String) s, (String) s, null, null, null, null, true));
                            }
                        }
                    } else if (res != null && res.getClass().isArray()) {
                        for (int i = 0, l = Array.getLength(res); i < l; i++) {
                            Object s = Array.get(res, i);
                            if (s instanceof Candidate) {
                                candidates.add((Candidate) s);
                            } else if (s instanceof String) {
                                candidates.add(new Candidate((String) s, (String) s, null, null, null, null, true));
                            }
                        }
                    }
                } else if (!isOption && completion.argument != null) {
                    Object res = null;
                    try {
                        res = environment.evaluate(reader, line, completion.argument);
                    } catch (Throwable t) {
                        // Ignore
                    }
                    if (res instanceof Candidate) {
                        candidates.add((Candidate) res);
                    } else if (res instanceof String) {
                        candidates.add(new Candidate(
                                (String) res, (String) res, null, completion.description, null, null, true));
                    } else if (res instanceof Collection) {
                        for (Object s : (Collection) res) {
                            if (s instanceof Candidate) {
                                candidates.add((Candidate) s);
                            } else if (s instanceof String) {
                                candidates.add(new Candidate(
                                        (String) s, (String) s, null, completion.description, null, null, true));
                            }
                        }
                    }
                }
            }
        }

        protected void completeCommand(List candidates) {
            Set commands = environment.getCommands();
            for (String command : commands) {
                String name = environment.commandName(command);
                boolean resolved = command.equals(environment.resolveCommand(name));
                if (!name.startsWith("_")) {
                    String desc = null;
                    Map> comp = environment.getCompletions();
                    if (comp != null) {
                        List completions = comp.get(command);
                        if (completions != null) {
                            for (CompletionData completion : completions) {
                                if (completion.description != null
                                        && completion.options == null
                                        && completion.argument == null
                                        && completion.condition == null) {
                                    desc = completion.description;
                                }
                            }
                        }
                    }
                    String key = UUID.randomUUID().toString();
                    if (desc != null) {
                        candidates.add(new Candidate(command, command, null, desc, null, key, true));
                        if (resolved) {
                            candidates.add(new Candidate(name, name, null, desc, null, key, true));
                        }
                    } else {
                        candidates.add(new Candidate(command, command, null, null, null, key, true));
                        if (resolved) {
                            candidates.add(new Candidate(name, name, null, null, null, key, true));
                        }
                    }
                }
            }
        }

        private boolean isTrue(Object result) {
            if (result == null) return false;
            if (result instanceof Boolean) return (Boolean) result;
            if (result instanceof Number && 0 == ((Number) result).intValue()) {
                return false;
            }
            return !("".equals(result) || "0".equals(result));
        }
    }

    public static class DirectoriesCompleter extends FileNameCompleter {

        private final Supplier currentDir;

        public DirectoriesCompleter(File currentDir) {
            this(currentDir.toPath());
        }

        public DirectoriesCompleter(Path currentDir) {
            this.currentDir = () -> currentDir;
        }

        public DirectoriesCompleter(Supplier currentDir) {
            this.currentDir = currentDir;
        }

        @Override
        protected Path getUserDir() {
            return currentDir.get();
        }

        @Override
        protected boolean accept(Path path) {
            return Files.isDirectory(path) && super.accept(path);
        }
    }

    public static class FilesCompleter extends FileNameCompleter {

        private final Supplier currentDir;
        private final String namePattern;

        public FilesCompleter(File currentDir) {
            this(currentDir.toPath(), null);
        }

        public FilesCompleter(File currentDir, String namePattern) {
            this(currentDir.toPath(), namePattern);
        }

        public FilesCompleter(Path currentDir) {
            this(currentDir, null);
        }

        public FilesCompleter(Path currentDir, String namePattern) {
            this.currentDir = () -> currentDir;
            this.namePattern = compilePattern(namePattern);
        }

        public FilesCompleter(Supplier currentDir) {
            this(currentDir, null);
        }

        public FilesCompleter(Supplier currentDir, String namePattern) {
            this.currentDir = currentDir;
            this.namePattern = compilePattern(namePattern);
        }

        private String compilePattern(String pattern) {
            if (pattern == null) {
                return null;
            }
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < pattern.length(); i++) {
                char ch = pattern.charAt(i);
                if (ch == '\\') {
                    ch = pattern.charAt(++i);
                    sb.append(ch);
                } else if (ch == '.') {
                    sb.append('\\').append('.');
                } else if (ch == '*') {
                    sb.append('.').append('*');
                } else {
                    sb.append(ch);
                }
            }
            return sb.toString();
        }

        @Override
        protected Path getUserDir() {
            return currentDir.get();
        }

        @Override
        protected boolean accept(Path path) {
            if (namePattern == null || Files.isDirectory(path)) {
                return super.accept(path);
            }
            return path.getFileName().toString().matches(namePattern) && super.accept(path);
        }
    }

    /**
     * A file name completer takes the buffer and issues a list of
     * potential completions.
     * 

* This completer tries to behave as similar as possible to * bash's file name completion (using GNU readline) * with the following exceptions: *

    *
  • Candidates that are directories will end with "/"
  • *
  • Wildcard regular expressions are not evaluated or replaced
  • *
  • The "~" character can be used to represent the user's home, * but it cannot complete to other users' homes, since java does * not provide any way of determining that easily
  • *
* * @author Marc Prud'hommeaux * @author Jason Dillon * @since 2.3 */ public static class FileNameCompleter implements org.jline.reader.Completer { public void complete(LineReader reader, ParsedLine commandLine, final List candidates) { assert commandLine != null; assert candidates != null; String buffer = commandLine.word().substring(0, commandLine.wordCursor()); Path current; String curBuf; String sep = getSeparator(reader.isSet(LineReader.Option.USE_FORWARD_SLASH)); int lastSep = buffer.lastIndexOf(sep); try { if (lastSep >= 0) { curBuf = buffer.substring(0, lastSep + 1); if (curBuf.startsWith("~")) { if (curBuf.startsWith("~" + sep)) { current = getUserHome().resolve(curBuf.substring(2)); } else { current = getUserHome().getParent().resolve(curBuf.substring(1)); } } else { current = getUserDir().resolve(curBuf); } } else { curBuf = ""; current = getUserDir(); } StyleResolver resolver = Styles.lsStyle(); try (DirectoryStream directory = Files.newDirectoryStream(current, this::accept)) { directory.forEach(p -> { String value = curBuf + p.getFileName().toString(); if (Files.isDirectory(p)) { candidates.add(new Candidate( value + (reader.isSet(LineReader.Option.AUTO_PARAM_SLASH) ? sep : ""), getDisplay(reader.getTerminal(), p, resolver, sep), null, null, reader.isSet(LineReader.Option.AUTO_REMOVE_SLASH) ? sep : null, null, false)); } else { candidates.add(new Candidate( value, getDisplay(reader.getTerminal(), p, resolver, sep), null, null, null, null, true)); } }); } catch (IOException e) { // Ignore } } catch (Exception e) { // Ignore } } protected boolean accept(Path path) { try { return !Files.isHidden(path); } catch (IOException e) { return false; } } protected Path getUserDir() { return Paths.get(System.getProperty("user.dir")); } protected Path getUserHome() { return Paths.get(System.getProperty("user.home")); } protected String getSeparator(boolean useForwardSlash) { return useForwardSlash ? "/" : getUserDir().getFileSystem().getSeparator(); } protected String getDisplay(Terminal terminal, Path p, StyleResolver resolver, String separator) { AttributedStringBuilder sb = new AttributedStringBuilder(); String name = p.getFileName().toString(); int idx = name.lastIndexOf("."); String type = idx != -1 ? ".*" + name.substring(idx) : null; if (Files.isSymbolicLink(p)) { sb.styled(resolver.resolve(".ln"), name).append("@"); } else if (Files.isDirectory(p)) { sb.styled(resolver.resolve(".di"), name).append(separator); } else if (Files.isExecutable(p) && !OSUtils.IS_WINDOWS) { sb.styled(resolver.resolve(".ex"), name).append("*"); } else if (type != null && resolver.resolve(type).getStyle() != 0) { sb.styled(resolver.resolve(type), name); } else if (Files.isRegularFile(p)) { sb.styled(resolver.resolve(".fi"), name); } else { sb.append(name); } return sb.toAnsi(terminal); } } public static class TreeCompleter implements org.jline.reader.Completer { final Map completers = new HashMap<>(); final RegexCompleter completer; public TreeCompleter(Node... nodes) { this(Arrays.asList(nodes)); } public TreeCompleter(List nodes) { StringBuilder sb = new StringBuilder(); addRoots(sb, nodes); completer = new RegexCompleter(sb.toString(), completers::get); } public static Node node(Object... objs) { org.jline.reader.Completer comp = null; List cands = new ArrayList<>(); List nodes = new ArrayList<>(); for (Object obj : objs) { if (obj instanceof String) { cands.add(new Candidate((String) obj)); } else if (obj instanceof Candidate) { cands.add((Candidate) obj); } else if (obj instanceof Node) { nodes.add((Node) obj); } else if (obj instanceof org.jline.reader.Completer) { comp = (org.jline.reader.Completer) obj; } else { throw new IllegalArgumentException(); } } if (comp != null) { if (!cands.isEmpty()) { throw new IllegalArgumentException(); } return new Node(comp, nodes); } else if (!cands.isEmpty()) { return new Node((r, l, c) -> c.addAll(cands), nodes); } else { throw new IllegalArgumentException(); } } void addRoots(StringBuilder sb, List nodes) { if (!nodes.isEmpty()) { sb.append(" ( "); boolean first = true; for (Node n : nodes) { if (first) { first = false; } else { sb.append(" | "); } String name = "c" + completers.size(); completers.put(name, n.completer); sb.append(name); addRoots(sb, n.nodes); } sb.append(" ) "); } } @Override public void complete(LineReader reader, ParsedLine line, List candidates) { completer.complete(reader, line, candidates); } public static class Node { final org.jline.reader.Completer completer; final List nodes; public Node(org.jline.reader.Completer completer, List nodes) { this.completer = completer; this.nodes = nodes; } } } public static class RegexCompleter implements org.jline.reader.Completer { private final NfaMatcher matcher; private final Function completers; private final ThreadLocal reader = new ThreadLocal<>(); public RegexCompleter(String syntax, Function completers) { this.matcher = new NfaMatcher<>(syntax, this::doMatch); this.completers = completers; } @Override public synchronized void complete(LineReader reader, ParsedLine line, List candidates) { List words = line.words().subList(0, line.wordIndex()); this.reader.set(reader); Set next = matcher.matchPartial(words); for (String n : next) { completers.apply(n).complete(reader, new ArgumentLine(line.word(), line.wordCursor()), candidates); } this.reader.set(null); } private boolean doMatch(String arg, String name) { List candidates = new ArrayList<>(); LineReader r = reader.get(); boolean caseInsensitive = r != null && r.isSet(Option.CASE_INSENSITIVE); completers.apply(name).complete(r, new ArgumentLine(arg, arg.length()), candidates); return candidates.stream() .anyMatch(c -> caseInsensitive ? c.value().equalsIgnoreCase(arg) : c.value().equals(arg)); } public static class ArgumentLine implements ParsedLine { private final String word; private final int cursor; public ArgumentLine(String word, int cursor) { this.word = word; this.cursor = cursor; } @Override public String word() { return word; } @Override public int wordCursor() { return cursor; } @Override public int wordIndex() { return 0; } @Override public List words() { return Collections.singletonList(word); } @Override public String line() { return word; } @Override public int cursor() { return cursor; } } } public static class OptDesc { private String shortOption; private String longOption; private String description; private org.jline.reader.Completer valueCompleter; protected static List compile(Map> optionValues, Collection options) { List out = new ArrayList<>(); for (Map.Entry> entry : optionValues.entrySet()) { if (entry.getKey().startsWith("--")) { out.add(new OptDesc(null, entry.getKey(), new StringsCompleter(entry.getValue()))); } else if (entry.getKey().matches("-[a-zA-Z]")) { out.add(new OptDesc(entry.getKey(), null, new StringsCompleter(entry.getValue()))); } } for (String o : options) { if (o.startsWith("--")) { out.add(new OptDesc(null, o)); } else if (o.matches("-[a-zA-Z]")) { out.add(new OptDesc(o, null)); } } return out; } /** * Command option description. If option does not have short/long option assign to it null value. * If option does not have value set valueCompleter = NullCompleter.INSTANCE * @param shortOption short option * @param longOption long option * @param description short option description * @param valueCompleter option value completer */ public OptDesc( String shortOption, String longOption, String description, org.jline.reader.Completer valueCompleter) { this.shortOption = shortOption; this.longOption = longOption; this.description = description; this.valueCompleter = valueCompleter; } /** * Command option description. If option does not have short/long option assign to it null value. * If option does not have value set valueCompleter = NullCompleter.INSTANCE * @param shortOption short option * @param longOption long option * @param valueCompleter option value completer */ public OptDesc(String shortOption, String longOption, org.jline.reader.Completer valueCompleter) { this(shortOption, longOption, null, valueCompleter); } /** * Command option description. If option does not have short/long option assign to it null value. * @param shortOption short option * @param longOption long option * @param description short option description */ public OptDesc(String shortOption, String longOption, String description) { this(shortOption, longOption, description, null); } /** * Command option description. If option does not have short/long option assign to it null value. * @param shortOption short option * @param longOption long option */ public OptDesc(String shortOption, String longOption) { this(shortOption, longOption, null, null); } protected OptDesc() {} public void setValueCompleter(org.jline.reader.Completer valueCompleter) { this.valueCompleter = valueCompleter; } public String longOption() { return longOption; } public String shortOption() { return shortOption; } public String description() { return description; } protected boolean hasValue() { return valueCompleter != null && valueCompleter != NullCompleter.INSTANCE; } protected org.jline.reader.Completer valueCompleter() { return valueCompleter; } protected void completeOption( LineReader reader, final ParsedLine commandLine, List candidates, boolean longOpt) { if (!longOpt) { if (shortOption != null) { candidates.add(new Candidate(shortOption, shortOption, null, description, null, null, false)); } } else if (longOption != null) { if (hasValue()) { candidates.add(new Candidate(longOption + "=", longOption, null, description, null, null, false)); } else { candidates.add(new Candidate(longOption, longOption, null, description, null, null, true)); } } } protected boolean completeValue( LineReader reader, final ParsedLine commandLine, List candidates, String curBuf, String partialValue) { boolean out = false; List temp = new ArrayList<>(); ParsedLine pl = reader.getParser().parse(partialValue, partialValue.length()); valueCompleter.complete(reader, pl, temp); for (Candidate c : temp) { String v = c.value(); if (v.startsWith(partialValue)) { out = true; String val = c.value(); if (valueCompleter instanceof FileNameCompleter) { FileNameCompleter cc = (FileNameCompleter) valueCompleter; String sep = cc.getSeparator(reader.isSet(LineReader.Option.USE_FORWARD_SLASH)); val = cc.getDisplay(reader.getTerminal(), Paths.get(c.value()), Styles.lsStyle(), sep); } candidates.add(new Candidate(curBuf + v, val, null, null, null, null, c.complete())); } } return out; } protected boolean match(String option) { return (shortOption != null && shortOption.equals(option)) || (longOption != null && longOption.equals(option)); } protected boolean startsWith(String option) { return (shortOption != null && shortOption.startsWith(option)) || (longOption != null && longOption.startsWith(option)); } } public static class OptionCompleter implements org.jline.reader.Completer { private Function> commandOptions; private Collection options; private List argsCompleters = new ArrayList<>(); private int startPos; /** * OptionCompleter completes command options and parameters. OptionCompleter should be used as an argument of ArgumentCompleter * @param completer command parameter completer * @param commandOptions command options descriptions * @param startPos OptionCompleter position in ArgumentCompleter parameters */ public OptionCompleter( org.jline.reader.Completer completer, Function> commandOptions, int startPos) { this.startPos = startPos; this.commandOptions = commandOptions; this.argsCompleters.add(completer); } /** * OptionCompleter completes command options and parameters. OptionCompleter should be used as an argument of ArgumentCompleter * @param completers command parameters completers * @param commandOptions command options descriptions * @param startPos OptionCompleter position in ArgumentCompleter parameters */ public OptionCompleter( List completers, Function> commandOptions, int startPos) { this.startPos = startPos; this.commandOptions = commandOptions; this.argsCompleters = new ArrayList<>(completers); } /** * OptionCompleter completes command options and parameters. OptionCompleter should be used as an argument of ArgumentCompleter * @param completers command parameters completers * @param optionValues command value options as map key and its possible values as map value * @param options command options that do not have value * @param startPos OptionCompleter position in ArgumentCompleter parameters */ public OptionCompleter( List completers, Map> optionValues, Collection options, int startPos) { this(optionValues, options, startPos); this.argsCompleters = new ArrayList<>(completers); } /** * OptionCompleter completes command options and parameters. OptionCompleter should be used as an argument of ArgumentCompleter * @param completer command parameter completer * @param optionValues command value options as map key and its possible values as map value * @param options command options that do not have value * @param startPos OptionCompleter position in ArgumentCompleter parameters */ public OptionCompleter( org.jline.reader.Completer completer, Map> optionValues, Collection options, int startPos) { this(optionValues, options, startPos); this.argsCompleters.add(completer); } /** * OptionCompleter completes command options and parameters. OptionCompleter should be used as an argument of ArgumentCompleter * @param optionValues command value options as map key and its possible values as map value * @param options command options that do not have value * @param startPos OptionCompleter position in ArgumentCompleter parameters */ public OptionCompleter(Map> optionValues, Collection options, int startPos) { this(OptDesc.compile(optionValues, options), startPos); } /** * OptionCompleter completes command options and parameters. OptionCompleter should be used as an argument of ArgumentCompleter * @param completer command parameter completer * @param options command options that do not have value * @param startPos OptionCompleter position in ArgumentCompleter parameters */ public OptionCompleter(org.jline.reader.Completer completer, Collection options, int startPos) { this(options, startPos); this.argsCompleters.add(completer); } /** * OptionCompleter completes command options and parameters. OptionCompleter should be used as an argument of ArgumentCompleter * @param completers command parameters completers * @param options command options that do not have value * @param startPos OptionCompleter position in ArgumentCompleter parameters */ public OptionCompleter(List completers, Collection options, int startPos) { this(options, startPos); this.argsCompleters = new ArrayList<>(completers); } /** * OptionCompleter completes command options and parameters. OptionCompleter should be used as an argument of ArgumentCompleter * @param options command options that do not have value * @param startPos OptionCompleter position in ArgumentCompleter parameters */ public OptionCompleter(Collection options, int startPos) { this.options = options; this.startPos = startPos; } public void setStartPos(int startPos) { this.startPos = startPos; } @Override public void complete(LineReader reader, final ParsedLine commandLine, List candidates) { assert commandLine != null; assert candidates != null; List words = commandLine.words(); String buffer = commandLine.word().substring(0, commandLine.wordCursor()); if (startPos >= words.size()) { candidates.add(new Candidate(buffer, buffer, null, null, null, null, true)); return; } String command = reader.getParser().getCommand(words.get(startPos - 1)); if (buffer.startsWith("-")) { boolean addbuff = true; boolean valueCandidates = false; boolean longOption = buffer.startsWith("--"); int eq = buffer.matches("-[a-zA-Z][a-zA-Z0-9]+") ? 2 : buffer.indexOf('='); if (eq < 0) { List usedOptions = new ArrayList<>(); for (int i = startPos; i < words.size(); i++) { if (words.get(i).startsWith("-")) { String w = words.get(i); int ind = w.indexOf('='); if (ind < 0) { usedOptions.add(w); } else { usedOptions.add(w.substring(0, ind)); } } } for (OptDesc o : commandOptions == null ? options : commandOptions.apply(command)) { if (usedOptions.contains(o.shortOption()) || usedOptions.contains(o.longOption())) { continue; } if (o.startsWith(buffer)) { addbuff = false; } o.completeOption(reader, commandLine, candidates, longOption); } } else { addbuff = false; int nb = buffer.contains("=") ? 1 : 0; String value = buffer.substring(eq + nb); String curBuf = buffer.substring(0, eq + nb); String opt = buffer.substring(0, eq); OptDesc option = findOptDesc(command, opt); if (option.hasValue()) { valueCandidates = option.completeValue(reader, commandLine, candidates, curBuf, value); } } if ((buffer.contains("=") && !buffer.endsWith("=") && !valueCandidates) || addbuff) { candidates.add(new Candidate(buffer, buffer, null, null, null, null, true)); } } else if (words.size() > 1 && shortOptionValueCompleter(command, words.get(words.size() - 2)) != null) { shortOptionValueCompleter(command, words.get(words.size() - 2)) .complete(reader, commandLine, candidates); } else if (words.size() > 1 && longOptionValueCompleter(command, words.get(words.size() - 2)) != null) { longOptionValueCompleter(command, words.get(words.size() - 2)) .complete(reader, commandLine, candidates); } else if (!argsCompleters.isEmpty()) { int args = -1; for (int i = startPos; i < words.size(); i++) { if (!words.get(i).startsWith("-")) { if (i > 0 && shortOptionValueCompleter(command, words.get(i - 1)) == null && longOptionValueCompleter(command, words.get(i - 1)) == null) { args++; } } } if (args == -1) { candidates.add(new Candidate(buffer, buffer, null, null, null, null, true)); } else if (args < argsCompleters.size()) { argsCompleters.get(args).complete(reader, commandLine, candidates); } else { argsCompleters.get(argsCompleters.size() - 1).complete(reader, commandLine, candidates); } } } private org.jline.reader.Completer longOptionValueCompleter(String command, String opt) { if (!opt.matches("--[a-zA-Z]+")) { return null; } Collection optDescs = commandOptions == null ? options : commandOptions.apply(command); OptDesc option = findOptDesc(optDescs, opt); return option.hasValue() ? option.valueCompleter() : null; } private org.jline.reader.Completer shortOptionValueCompleter(String command, String opt) { if (!opt.matches("-[a-zA-Z]+")) { return null; } org.jline.reader.Completer out = null; Collection optDescs = commandOptions == null ? options : commandOptions.apply(command); if (opt.length() == 2) { out = findOptDesc(optDescs, opt).valueCompleter(); } else if (opt.length() > 2) { for (int i = 1; i < opt.length(); i++) { OptDesc o = findOptDesc(optDescs, "-" + opt.charAt(i)); if (o.shortOption() == null) { return null; } else if (out == null) { out = o.valueCompleter(); } } } return out; } private OptDesc findOptDesc(String command, String opt) { return findOptDesc(commandOptions == null ? options : commandOptions.apply(command), opt); } private OptDesc findOptDesc(Collection optDescs, String opt) { for (OptDesc o : optDescs) { if (o.match(opt)) { return o; } } return new OptDesc(); } } public static class AnyCompleter implements org.jline.reader.Completer { public static final AnyCompleter INSTANCE = new AnyCompleter(); @Override public void complete(LineReader reader, ParsedLine commandLine, List candidates) { assert commandLine != null; assert candidates != null; String buffer = commandLine.word().substring(0, commandLine.wordCursor()); candidates.add(new Candidate(AttributedString.stripAnsi(buffer), buffer, null, null, null, null, true)); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy