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

jline.ArgumentCompletor Maven / Gradle / Ivy

There is a newer version: 2024.03.6
Show newest version
/*
 * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
 *
 * This software is distributable under the BSD license. See the terms of the
 * BSD license in the documentation provided with this software.
 */
package jline;

import java.util.*;

/**
 *  A {@link Completor} implementation that invokes a child completor
 *  using the appropriate separator argument. This
 *  can be used instead of the individual completors having to
 *  know about argument parsing semantics.
 *  

* Example 1: Any argument of the command line can * use file completion. *

*

 *        consoleReader.addCompletor (new ArgumentCompletor (
 *                new {@link FileNameCompletor} ()))
 *  
*

* Example 2: The first argument of the command line * can be completed with any of "foo", "bar", or "baz", and remaining * arguments can be completed with a file name. *

*

 *        consoleReader.addCompletor (new ArgumentCompletor (
 *                new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"})));
 *        consoleReader.addCompletor (new ArgumentCompletor (
 *                new {@link FileNameCompletor} ()));
 *  
* *

* When the argument index is past the last embedded completors, the last * completors is always used. To disable this behavior, have the last * completor be a {@link NullCompletor}. For example: *

* *
 *        consoleReader.addCompletor (new ArgumentCompletor (
 *                new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"}),
 *                new {@link SimpleCompletor} (new String [] { "xxx", "yyy", "xxx"}),
 *                new {@link NullCompletor}
 *                ));
 *        
*

* TODO: handle argument quoting and escape characters *

* * @author Marc Prud'hommeaux */ public class ArgumentCompletor implements Completor { final Completor[] completors; final ArgumentDelimiter delim; boolean strict = true; /** * Constuctor: create a new completor with the default * argument separator of " ". * * @param completor the embedded completor */ public ArgumentCompletor(final Completor completor) { this(new Completor[] { completor }); } /** * Constuctor: create a new completor with the default * argument separator of " ". * * @param completors the List of completors to use */ public ArgumentCompletor(final List completors) { this((Completor[]) completors.toArray(new Completor[completors.size()])); } /** * Constuctor: create a new completor with the default * argument separator of " ". * * @param completors the embedded argument completors */ public ArgumentCompletor(final Completor[] completors) { this(completors, new WhitespaceArgumentDelimiter()); } /** * Constuctor: create a new completor with the specified * argument delimiter. * * @param completor the embedded completor * @param delim the delimiter for parsing arguments */ public ArgumentCompletor(final Completor completor, final ArgumentDelimiter delim) { this(new Completor[] { completor }, delim); } /** * Constuctor: create a new completor with the specified * argument delimiter. * * @param completors the embedded completors * @param delim the delimiter for parsing arguments */ public ArgumentCompletor(final Completor[] completors, final ArgumentDelimiter delim) { this.completors = completors; this.delim = delim; } /** * If true, a completion at argument index N will only succeed * if all the completions from 0-(N-1) also succeed. */ public void setStrict(final boolean strict) { this.strict = strict; } /** * Returns whether a completion at argument index N will succees * if all the completions from arguments 0-(N-1) also succeed. */ public boolean getStrict() { return this.strict; } public int complete(final String buffer, final int cursor, final List candidates) { ArgumentList list = delim.delimit(buffer, cursor); int argpos = list.getArgumentPosition(); int argIndex = list.getCursorArgumentIndex(); if (argIndex < 0) { return -1; } final Completor comp; // if we are beyond the end of the completors, just use the last one if (argIndex >= completors.length) { comp = completors[completors.length - 1]; } else { comp = completors[argIndex]; } // ensure that all the previous completors are successful before // allowing this completor to pass (only if strict is true). for (int i = 0; getStrict() && (i < argIndex); i++) { Completor sub = completors[(i >= completors.length) ? (completors.length - 1) : i]; String[] args = list.getArguments(); String arg = ((args == null) || (i >= args.length)) ? "" : args[i]; List subCandidates = new LinkedList(); if (sub.complete(arg, arg.length(), subCandidates) == -1) { return -1; } if (subCandidates.size() == 0) { return -1; } } int ret = comp.complete(list.getCursorArgument(), argpos, candidates); if (ret == -1) { return -1; } int pos = ret + (list.getBufferPosition() - argpos); /** * Special case: when completing in the middle of a line, and the * area under the cursor is a delimiter, then trim any delimiters * from the candidates, since we do not need to have an extra * delimiter. * * E.g., if we have a completion for "foo", and we * enter "f bar" into the buffer, and move to after the "f" * and hit TAB, we want "foo bar" instead of "foo bar". */ if ((cursor != buffer.length()) && delim.isDelimiter(buffer, cursor)) { for (int i = 0; i < candidates.size(); i++) { String val = candidates.get(i).toString(); while ((val.length() > 0) && delim.isDelimiter(val, val.length() - 1)) { val = val.substring(0, val.length() - 1); } candidates.set(i, val); } } ConsoleReader.debug("Completing " + buffer + "(pos=" + cursor + ") " + "with: " + candidates + ": offset=" + pos); return pos; } /** * The {@link ArgumentCompletor.ArgumentDelimiter} allows custom * breaking up of a {@link String} into individual arguments in * order to dispatch the arguments to the nested {@link Completor}. * * @author Marc Prud'hommeaux */ public static interface ArgumentDelimiter { /** * Break the specified buffer into individual tokens * that can be completed on their own. * * @param buffer the buffer to split * @param argumentPosition the current position of the * cursor in the buffer * @return the tokens */ ArgumentList delimit(String buffer, int argumentPosition); /** * Returns true if the specified character is a whitespace * parameter. * * @param buffer the complete command buffer * @param pos the index of the character in the buffer * @return true if the character should be a delimiter */ boolean isDelimiter(String buffer, int pos); } /** * Abstract implementation of a delimiter that uses the * {@link #isDelimiter} method to determine if a particular * character should be used as a delimiter. * * @author Marc Prud'hommeaux */ public abstract static class AbstractArgumentDelimiter implements ArgumentDelimiter { private char[] quoteChars = new char[] { '\'', '"' }; private char[] escapeChars = new char[] { '\\' }; public void setQuoteChars(final char[] quoteChars) { this.quoteChars = quoteChars; } public char[] getQuoteChars() { return this.quoteChars; } public void setEscapeChars(final char[] escapeChars) { this.escapeChars = escapeChars; } public char[] getEscapeChars() { return this.escapeChars; } public ArgumentList delimit(final String buffer, final int cursor) { List args = new LinkedList(); StringBuffer arg = new StringBuffer(); int argpos = -1; int bindex = -1; for (int i = 0; (buffer != null) && (i <= buffer.length()); i++) { // once we reach the cursor, set the // position of the selected index if (i == cursor) { bindex = args.size(); // the position in the current argument is just the // length of the current argument argpos = arg.length(); } if ((i == buffer.length()) || isDelimiter(buffer, i)) { if (arg.length() > 0) { args.add(arg.toString()); arg.setLength(0); // reset the arg } } else { arg.append(buffer.charAt(i)); } } return new ArgumentList((String[]) args. toArray(new String[args.size()]), bindex, argpos, cursor); } /** * Returns true if the specified character is a whitespace * parameter. Check to ensure that the character is not * escaped by any of * {@link #getQuoteChars}, and is not escaped by ant of the * {@link #getEscapeChars}, and returns true from * {@link #isDelimiterChar}. * * @param buffer the complete command buffer * @param pos the index of the character in the buffer * @return true if the character should be a delimiter */ public boolean isDelimiter(final String buffer, final int pos) { if (isQuoted(buffer, pos)) { return false; } if (isEscaped(buffer, pos)) { return false; } return isDelimiterChar(buffer, pos); } public boolean isQuoted(final String buffer, final int pos) { return false; } public boolean isEscaped(final String buffer, final int pos) { if (pos <= 0) { return false; } for (int i = 0; (escapeChars != null) && (i < escapeChars.length); i++) { if (buffer.charAt(pos) == escapeChars[i]) { return !isEscaped(buffer, pos - 1); // escape escape } } return false; } /** * Returns true if the character at the specified position * if a delimiter. This method will only be called if the * character is not enclosed in any of the * {@link #getQuoteChars}, and is not escaped by ant of the * {@link #getEscapeChars}. To perform escaping manually, * override {@link #isDelimiter} instead. */ public abstract boolean isDelimiterChar(String buffer, int pos); } /** * {@link ArgumentCompletor.ArgumentDelimiter} * implementation that counts all * whitespace (as reported by {@link Character#isWhitespace}) * as being a delimiter. * * @author Marc Prud'hommeaux */ public static class WhitespaceArgumentDelimiter extends AbstractArgumentDelimiter { /** * The character is a delimiter if it is whitespace, and the * preceeding character is not an escape character. */ public boolean isDelimiterChar(String buffer, int pos) { return Character.isWhitespace(buffer.charAt(pos)); } } /** * The result of a delimited buffer. * * @author Marc Prud'hommeaux */ public static class ArgumentList { private String[] arguments; private int cursorArgumentIndex; private int argumentPosition; private int bufferPosition; /** * @param arguments the array of tokens * @param cursorArgumentIndex the token index of the cursor * @param argumentPosition the position of the cursor in the * current token * @param bufferPosition the position of the cursor in * the whole buffer */ public ArgumentList(String[] arguments, int cursorArgumentIndex, int argumentPosition, int bufferPosition) { this.arguments = arguments; this.cursorArgumentIndex = cursorArgumentIndex; this.argumentPosition = argumentPosition; this.bufferPosition = bufferPosition; } public void setCursorArgumentIndex(int cursorArgumentIndex) { this.cursorArgumentIndex = cursorArgumentIndex; } public int getCursorArgumentIndex() { return this.cursorArgumentIndex; } public String getCursorArgument() { if ((cursorArgumentIndex < 0) || (cursorArgumentIndex >= arguments.length)) { return null; } return arguments[cursorArgumentIndex]; } public void setArgumentPosition(int argumentPosition) { this.argumentPosition = argumentPosition; } public int getArgumentPosition() { return this.argumentPosition; } public void setArguments(String[] arguments) { this.arguments = arguments; } public String[] getArguments() { return this.arguments; } public void setBufferPosition(int bufferPosition) { this.bufferPosition = bufferPosition; } public int getBufferPosition() { return this.bufferPosition; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy