You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2010/09/03 21:51:44 UTC
svn commit: r992437 - in /karaf/trunk/shell/console/src:
main/java/org/apache/karaf/shell/console/completer/
main/java/org/apache/karaf/shell/console/jline/
test/java/org/apache/karaf/shell/console/completer/
Author: gnodet
Date: Fri Sep 3 19:51:43 2010
New Revision: 992437
URL: http://svn.apache.org/viewvc?rev=992437&view=rev
Log:
KARAF-187: The completers do not work when several commands are on the same line separated by a column or pipe
Added:
karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/Parser.java
karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/completer/
karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java
Removed:
karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/SessionScopeCompleter.java
Modified:
karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java
karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java
Modified: karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java?rev=992437&r1=992436&r2=992437&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java Fri Sep 3 19:51:43 2010
@@ -24,7 +24,8 @@
*/
package org.apache.karaf.shell.console.completer;
-import java.util.*;
+import java.util.LinkedList;
+import java.util.List;
import org.apache.karaf.shell.console.Completer;
@@ -62,7 +63,7 @@ public class ArgumentCompleter implement
* @param completers the embedded argument completers
*/
public ArgumentCompleter(final Completer[] completers) {
- this(completers, new WhitespaceArgumentDelimiter());
+ this(completers, new GogoArgumentDelimiter());
}
/**
@@ -210,69 +211,32 @@ public class ArgumentCompleter implement
}
/**
- * Abstract implementation of a delimiter that uses the
- * {@link #isDelimiter} method to determine if a particular
- * character should be used as a delimiter.
+ * Implementation of a delimiter that uses the
+ * Gogo parser.
*
- * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ * @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
*/
- 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 static class GogoArgumentDelimiter implements ArgumentDelimiter {
public ArgumentList delimit(final String buffer, final int cursor) {
- List<String> args = new LinkedList<String>();
- 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));
+ Parser parser = new Parser(buffer, cursor);
+ try {
+ List<List<List<CharSequence>>> program = parser.program();
+ List<CharSequence> pipe = program.get(parser.c0).get(parser.c1);
+ List<String> args = new LinkedList<String>();
+ for (CharSequence arg : pipe) {
+ args.add(arg.toString());
}
+ return new ArgumentList(args.toArray(new String[args.size()]), parser.c2, parser.c3, cursor);
+ } catch (Throwable t) {
+ return new ArgumentList(new String[] { buffer }, 0, cursor, cursor);
}
-
- return new ArgumentList(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
+ * escaped and returns true from
* {@link #isDelimiterChar}.
*
* @param buffer the complete command buffer
@@ -280,64 +244,21 @@ public class ArgumentCompleter implement
* @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;
+ return !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos);
}
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;
+ return pos > 0 && buffer.charAt(pos) == '\\' && !isEscaped(buffer, pos - 1);
}
/**
- * 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 ArgumentCompleter.ArgumentDelimiter}
- * implementation that counts all
- * whitespace (as reported by {@link Character#isWhitespace})
- * as being a delimiter.
- *
- * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
- */
- 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));
}
+
}
/**
Modified: karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java?rev=992437&r1=992436&r2=992437&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java Fri Sep 3 19:51:43 2010
@@ -67,7 +67,7 @@ public class CommandsCompleter implement
function = unProxy(function);
if (function instanceof AbstractCommand) {
List<Completer> cl = new ArrayList<Completer>();
- cl.add(new StringsCompleter(new String[] { command }));
+ cl.add(new StringsCompleter(getNames(command)));
if (function instanceof CompletableFunction) {
List<Completer> fcl = ((CompletableFunction) function).getCompleters();
if (fcl != null) {
@@ -87,6 +87,11 @@ public class CommandsCompleter implement
}
}
+ private String[] getNames(String command) {
+ String[] s = command.split(":");
+ return new String[] { command, s[1] };
+ }
+
protected Function unProxy(Function function) {
try {
if (function instanceof CommandProxy) {
Added: karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/Parser.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/Parser.java?rev=992437&view=auto
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/Parser.java (added)
+++ karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/Parser.java Fri Sep 3 19:51:43 2010
@@ -0,0 +1,472 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+// DWB14: parser loops if // comment at start of program
+// DWB15: allow program to have trailing ';'
+package org.apache.karaf.shell.console.completer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Parser
+{
+ int current = 0;
+ CharSequence text;
+ boolean escaped;
+ static final String SPECIAL = "<;|{[\"'$`(=";
+
+ List<List<List<CharSequence>>> program;
+ List<List<CharSequence>> statements;
+ List<CharSequence> statement;
+ int cursor;
+ int start = -1;
+ int c0;
+ int c1;
+ int c2;
+ int c3;
+
+ public Parser(CharSequence text, int cursor)
+ {
+ this.text = text;
+ this.cursor = cursor;
+ }
+
+ void ws()
+ {
+ // derek: BUGFIX: loop if comment at beginning of input
+ //while (!eof() && Character.isWhitespace(peek())) {
+ while (!eof() && (!escaped && Character.isWhitespace(peek()) || current == 0))
+ {
+ if (current != 0 || !escaped && Character.isWhitespace(peek()))
+ {
+ current++;
+ }
+ if (peek() == '/' && current < text.length() - 2
+ && text.charAt(current + 1) == '/')
+ {
+ comment();
+ }
+ if (current == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ private void comment()
+ {
+ while (!eof() && peek() != '\n' && peek() != '\r')
+ {
+ next();
+ }
+ }
+
+ boolean eof()
+ {
+ return current >= text.length();
+ }
+
+ char peek()
+ {
+ return peek(false);
+ }
+
+ char peek(boolean increment)
+ {
+ escaped = false;
+ if (eof())
+ {
+ return 0;
+ }
+
+ int last = current;
+ char c = text.charAt(current++);
+
+ if (c == '\\')
+ {
+ escaped = true;
+ if (eof())
+ {
+ throw new RuntimeException("Eof found after \\"); // derek
+ }
+
+ c = text.charAt(current++);
+
+ switch (c)
+ {
+ case 't':
+ c = '\t';
+ break;
+ case '\r':
+ case '\n':
+ c = ' ';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 'u':
+ c = unicode();
+ current += 4;
+ break;
+ default:
+ // We just take the next character literally
+ // but have the escaped flag set, important for {},[] etc
+ }
+ }
+ if (cursor > last && cursor <= current)
+ {
+ c0 = program != null ? program.size() : 0;
+ c1 = statements != null ? statements.size() : 0;
+ c2 = statement != null ? statement.size() : 0;
+ c3 = (start >= 0) ? current - start : 0;
+ }
+ if (!increment)
+ {
+ current = last;
+ }
+ return c;
+ }
+
+ public List<List<List<CharSequence>>> program()
+ {
+ program = new ArrayList<List<List<CharSequence>>>();
+ ws();
+ if (!eof())
+ {
+ program.add(pipeline());
+ while (peek() == ';')
+ {
+ current++;
+ List<List<CharSequence>> pipeline = pipeline();
+ program.add(pipeline);
+ }
+ }
+ if (!eof())
+ {
+ throw new RuntimeException("Program has trailing text: " + context(current));
+ }
+
+ List<List<List<CharSequence>>> p = program;
+ program = null;
+ return p;
+ }
+
+ CharSequence context(int around)
+ {
+ return text.subSequence(Math.max(0, current - 20), Math.min(text.length(),
+ current + 4));
+ }
+
+ public List<List<CharSequence>> pipeline()
+ {
+ statements = new ArrayList<List<CharSequence>>();
+ statements.add(statement());
+ while (peek() == '|')
+ {
+ current++;
+ ws();
+ if (!eof())
+ {
+ statements.add(statement());
+ }
+ else
+ {
+ statements.add(new ArrayList<CharSequence>());
+ break;
+ }
+ }
+ List<List<CharSequence>> s = statements;
+ statements = null;
+ return s;
+ }
+
+ public List<CharSequence> statement()
+ {
+ statement = new ArrayList<CharSequence>();
+ statement.add(value());
+ while (!eof())
+ {
+ ws();
+ if (peek() == '|' || peek() == ';')
+ {
+ break;
+ }
+
+ if (!eof())
+ {
+ statement.add(messy());
+ }
+ }
+ List<CharSequence> s = statement;
+ statement = null;
+ return s;
+ }
+
+ public CharSequence messy()
+ {
+ char c = peek();
+ if (c > 0 && SPECIAL.indexOf(c) < 0)
+ {
+ start = current++;
+ try {
+ while (!eof())
+ {
+ c = peek();
+ if (!escaped && (c == ';' || c == '|' || Character.isWhitespace(c)))
+ {
+ break;
+ }
+ next();
+ }
+ return text.subSequence(start, current);
+ } finally {
+ start = -1;
+ }
+ }
+ else
+ {
+ return value();
+ }
+ }
+
+ CharSequence value()
+ {
+ ws();
+
+ start = current;
+ try {
+ char c = next();
+ if (!escaped)
+ {
+ switch (c)
+ {
+ case '{':
+ return text.subSequence(start, find('}', '{'));
+ case '(':
+ return text.subSequence(start, find(')', '('));
+ case '[':
+ return text.subSequence(start, find(']', '['));
+ case '<':
+ return text.subSequence(start, find('>', '<'));
+ case '=':
+ return text.subSequence(start, current);
+ case '"':
+ case '\'':
+ quote(c);
+ break;
+ }
+ }
+
+ // Some identifier or number
+ while (!eof())
+ {
+ c = peek();
+ if (!escaped)
+ {
+ if (Character.isWhitespace(c) || c == ';' || c == '|' || c == '=')
+ {
+ break;
+ }
+ else if (c == '{')
+ {
+ next();
+ find('}', '{');
+ }
+ else if (c == '(')
+ {
+ next();
+ find(')', '(');
+ }
+ else if (c == '<')
+ {
+ next();
+ find('>', '<');
+ }
+ else if (c == '[')
+ {
+ next();
+ find(']', '[');
+ }
+ else if (c == '\'' || c == '"')
+ {
+ next();
+ quote(c);
+ next();
+ }
+ else
+ {
+ next();
+ }
+ }
+ else
+ {
+ next();
+ }
+ }
+ return text.subSequence(start, current);
+ } finally {
+ start = -1;
+ }
+ }
+
+ boolean escaped()
+ {
+ return escaped;
+ }
+
+ char next()
+ {
+ return peek(true);
+ }
+
+ char unicode()
+ {
+ if (current + 4 > text.length())
+ {
+ throw new IllegalArgumentException("Unicode \\u escape at eof at pos ..."
+ + context(current) + "...");
+ }
+
+ String s = text.subSequence(current, current + 4).toString();
+ int n = Integer.parseInt(s, 16);
+ return (char) n;
+ }
+
+ int find(char target, char deeper)
+ {
+ int start = current;
+ int level = 1;
+
+ while (level != 0)
+ {
+ if (eof())
+ {
+ throw new RuntimeException("Eof found in the middle of a compound for '"
+ + target + deeper + "', begins at " + context(start));
+ }
+
+ char c = next();
+ if (!escaped)
+ {
+ if (c == target)
+ {
+ level--;
+ }
+ else
+ {
+ if (c == deeper)
+ {
+ level++;
+ }
+ else
+ {
+ if (c == '"')
+ {
+ quote('"');
+ }
+ else
+ {
+ if (c == '\'')
+ {
+ quote('\'');
+ }
+ else
+ {
+ if (c == '`')
+ {
+ quote('`');
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return current;
+ }
+
+ int quote(char which)
+ {
+ while (!eof() && (peek() != which || escaped))
+ {
+ next();
+ }
+
+ return current++;
+ }
+
+ CharSequence findVar()
+ {
+ int start = current;
+ char c = peek();
+
+ if (c == '{')
+ {
+ next();
+ int end = find('}', '{');
+ return text.subSequence(start, end);
+ }
+ if (c == '(')
+ {
+ next();
+ int end = find(')', '(');
+ return text.subSequence(start, end);
+ }
+
+ if (Character.isJavaIdentifierPart(c))
+ {
+ while (c == '$')
+ {
+ c = next();
+ }
+ while (!eof() && (Character.isJavaIdentifierPart(c) || c == '.') && c != '$')
+ {
+ next();
+ c = peek();
+ }
+ return text.subSequence(start, current);
+ }
+ throw new IllegalArgumentException(
+ "Reference to variable does not match syntax of a variable: "
+ + context(start));
+ }
+
+ public String toString()
+ {
+ return "..." + context(current) + "...";
+ }
+
+ public String unescape()
+ {
+ StringBuilder sb = new StringBuilder();
+ while (!eof())
+ {
+ sb.append(next());
+ }
+ return sb.toString();
+ }
+}
Modified: karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java?rev=992437&r1=992436&r2=992437&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java Fri Sep 3 19:51:43 2010
@@ -28,7 +28,6 @@ import java.io.InterruptedIOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
-import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
@@ -42,9 +41,7 @@ import jline.Terminal;
import jline.UnsupportedTerminal;
import org.apache.karaf.shell.console.CloseShellException;
import org.apache.karaf.shell.console.Completer;
-import org.apache.karaf.shell.console.completer.AggregateCompleter;
import org.apache.karaf.shell.console.completer.CommandsCompleter;
-import org.apache.karaf.shell.console.completer.SessionScopeCompleter;
import org.fusesource.jansi.Ansi;
import org.osgi.service.command.CommandProcessor;
import org.osgi.service.command.CommandSession;
@@ -242,13 +239,7 @@ public class Console implements Runnable
}
protected Completer createCompleter() {
- Completer completer = new CommandsCompleter(session);
- return new AggregateCompleter(
- Arrays.asList(
- completer,
- new SessionScopeCompleter( session, completer )
- )
- );
+ return new CommandsCompleter(session);
}
protected Properties loadBrandingProperties() {
Added: karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java?rev=992437&view=auto
==============================================================================
--- karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java (added)
+++ karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java Fri Sep 3 19:51:43 2010
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.shell.console.completer;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ArgumentCompleterTest {
+
+ @Test
+ public void testParser1() throws Exception {
+ Parser parser = new Parser("echo foo | cat bar ; ta", 23);
+ List<List<List<CharSequence>>> p = parser.program();
+ assertEquals(1, parser.c0);
+ assertEquals(0, parser.c1);
+ assertEquals(0, parser.c2);
+ assertEquals(2, parser.c3);
+ }
+
+ @Test
+ public void testParser2() throws Exception {
+ Parser parser = new Parser("echo foo ; cat bar | ta", 23);
+ List<List<List<CharSequence>>> p = parser.program();
+ assertEquals(1, parser.c0);
+ assertEquals(1, parser.c1);
+ assertEquals(0, parser.c2);
+ assertEquals(2, parser.c3);
+ }
+
+ @Test
+ public void testParser3() throws Exception {
+ Parser parser = new Parser("echo foo ; cat bar | ta", 22);
+ List<List<List<CharSequence>>> p = parser.program();
+ assertEquals(1, parser.c0);
+ assertEquals(1, parser.c1);
+ assertEquals(0, parser.c2);
+ assertEquals(1, parser.c3);
+ }
+
+ @Test
+ public void testParser4() throws Exception {
+ Parser parser = new Parser("echo foo ; cat bar | ta reta", 27);
+ List<List<List<CharSequence>>> p = parser.program();
+ assertEquals(1, parser.c0);
+ assertEquals(1, parser.c1);
+ assertEquals(1, parser.c2);
+ assertEquals(3, parser.c3);
+ }
+
+ @Test
+ public void testParser5() throws Exception {
+ Parser parser = new Parser("echo foo ; cat bar | ta reta", 24);
+ List<List<List<CharSequence>>> p = parser.program();
+ assertEquals(1, parser.c0);
+ assertEquals(1, parser.c1);
+ assertEquals(1, parser.c2);
+ assertEquals(0, parser.c3);
+ }
+
+}