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 2016/07/20 10:59:39 UTC

karaf git commit: [KARAF-4554] Completion of the 'watch' command hangs Karaf

Repository: karaf
Updated Branches:
  refs/heads/master d14ac13b3 -> a26dea75e


[KARAF-4554] Completion of the 'watch' command hangs Karaf

Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/a26dea75
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/a26dea75
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/a26dea75

Branch: refs/heads/master
Commit: a26dea75e03108439334e853d0d86333576560f0
Parents: d14ac13
Author: Guillaume Nodet <gn...@apache.org>
Authored: Wed Jul 20 12:59:25 2016 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Wed Jul 20 12:59:25 2016 +0200

----------------------------------------------------------------------
 .../karaf/shell/commands/impl/WatchAction.java  | 109 +++++++++++++++--
 .../shell/impl/console/CommandsCompleter.java   |   2 +-
 .../shell/impl/console/ConsoleSessionImpl.java  |   3 +-
 .../shell/impl/console/parsing/KarafParser.java | 117 +++++++++++++++++++
 4 files changed, 220 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/a26dea75/shell/commands/src/main/java/org/apache/karaf/shell/commands/impl/WatchAction.java
----------------------------------------------------------------------
diff --git a/shell/commands/src/main/java/org/apache/karaf/shell/commands/impl/WatchAction.java b/shell/commands/src/main/java/org/apache/karaf/shell/commands/impl/WatchAction.java
index 9e25585..7f46282 100644
--- a/shell/commands/src/main/java/org/apache/karaf/shell/commands/impl/WatchAction.java
+++ b/shell/commands/src/main/java/org/apache/karaf/shell/commands/impl/WatchAction.java
@@ -21,6 +21,8 @@ package org.apache.karaf.shell.commands.impl;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -30,14 +32,22 @@ import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Completion;
 import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.Parsing;
 import org.apache.karaf.shell.api.action.lifecycle.Reference;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Parser;
 import org.apache.karaf.shell.api.console.Session;
 import org.apache.karaf.shell.api.console.SessionFactory;
+import org.apache.karaf.shell.commands.impl.WatchAction.WatchParser;
 import org.apache.karaf.shell.support.ShellUtil;
 import org.apache.karaf.shell.support.completers.CommandsCompleter;
+import org.apache.karaf.shell.support.parsing.CommandLineImpl;
+import org.apache.karaf.shell.support.parsing.DefaultParser;
 
 @Command(scope = "shell", name = "watch", description = "Watches & refreshes the output of a command")
+@Parsing(WatchParser.class)
 @Service
 public class WatchAction implements Action {
 
@@ -47,9 +57,9 @@ public class WatchAction implements Action {
     @Option(name = "-a", aliases = {"--append"}, description = "The output should be appended but not clear the console", required = false, multiValued = false)
     private boolean append = false;
 
-    @Argument(index = 0, name = "command", description = "The command to watch / refresh", required = true, multiValued = true)
-    @Completion(CommandsCompleter.class)
-    private String[] arguments;
+    @Argument(index = 0, name = "command", description = "The command to watch / refresh", required = true, multiValued = false)
+    @Completion(SubCommandCompleter.class)
+    private String arguments;
 
     @Reference
     Session session;
@@ -61,14 +71,10 @@ public class WatchAction implements Action {
 
     @Override
     public Object execute() throws Exception {
-        if (arguments == null || arguments.length == 0) {
+        if (arguments == null || arguments.length() == 0) {
             System.err.println("Argument expected");
         } else {
-            StringBuilder command = new StringBuilder();
-            for (String arg : arguments) {
-                command.append(arg).append(" ");
-            }
-            WatchTask watchTask = new WatchTask(command.toString().trim());
+            WatchTask watchTask = new WatchTask(arguments.trim());
             executorService.scheduleAtFixedRate(watchTask, 0, interval, TimeUnit.SECONDS);
             try {
                 session.getKeyboard().read();
@@ -137,4 +143,89 @@ public class WatchAction implements Action {
         }
     }
 
+    @Service
+    public static class WatchParser implements Parser {
+        @Override
+        public CommandLine parse(Session session, String command, int cursor) {
+            int n1 = 0;
+            while (n1 < command.length() && Character.isWhitespace(command.charAt(n1))) {
+                n1++;
+                if (n1 == command.length()) {
+                    throw new IllegalArgumentException();
+                }
+            }
+            int n2 = n1 + 1;
+            while (!Character.isWhitespace(command.charAt(n2))) {
+                n2++;
+                if (n2 == command.length()) {
+                    return new CommandLineImpl(
+                            new String[]{command.substring(n1)},
+                            0,
+                            cursor - n1,
+                            cursor,
+                            command
+                    );
+                }
+            }
+            int n3 = n2 + 1;
+            while (n3 < command.length() && Character.isWhitespace(command.charAt(n3))) {
+                n3++;
+                if (n3 == command.length()) {
+                    return new CommandLineImpl(
+                            new String[]{command.substring(n1, n2), ""},
+                            cursor >= n2 ? 1 : 0,
+                            cursor >= n2 ? 0 : cursor - n1,
+                            cursor,
+                            command
+                    );
+                }
+            }
+            return new CommandLineImpl(
+                    new String[]{command.substring(n1, n2), command.substring(n3)},
+                    cursor >= n3 ? 1 : 0,
+                    cursor >= n3 ? cursor - n3 : cursor - n1,
+                    cursor,
+                    command
+            );
+        }
+
+        @Override
+        public String preprocess(Session session, CommandLine cmdLine) {
+            StringBuilder parsed = new StringBuilder();
+            for (int i = 0; i < cmdLine.getArguments().length; i++) {
+                String arg = cmdLine.getArguments()[i];
+                if (i > 0) {
+                    parsed.append(" \"");
+                }
+                parsed.append(arg);
+                if (i > 0) {
+                    parsed.append("\"");
+                }
+            }
+            return parsed.toString();
+        }
+    }
+
+    @Service
+    public static class SubCommandCompleter implements Completer {
+
+        @Override
+        public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+            String arg = commandLine.getCursorArgument();
+            int pos = commandLine.getArgumentPosition();
+            CommandLine cmdLine = new DefaultParser().parse(session, arg, pos);
+            Completer completer = session.getRegistry().getService(CommandsCompleter.class);
+            List<String> cands = new ArrayList<>();
+            int res = completer.complete(session, cmdLine, cands);
+            for (String cand : cands) {
+                candidates.add(arg.substring(0, cmdLine.getBufferPosition() - cmdLine.getArgumentPosition()) + cand);
+            }
+            if (res >= 0) {
+                res += commandLine.getBufferPosition() - commandLine.getArgumentPosition();
+            }
+            return res;
+        }
+
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/a26dea75/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CommandsCompleter.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CommandsCompleter.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CommandsCompleter.java
index 8a878d3..84f87c6 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CommandsCompleter.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CommandsCompleter.java
@@ -44,7 +44,7 @@ import org.jline.reader.ParsedLine;
 /**
  * Overall command line completer.
  */
-public class CommandsCompleter implements org.jline.reader.Completer {
+public class CommandsCompleter extends org.apache.karaf.shell.support.completers.CommandsCompleter implements org.jline.reader.Completer {
 
     private final SessionFactory factory;
     private final Session session;

http://git-wip-us.apache.org/repos/asf/karaf/blob/a26dea75/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
index 413f2af..d338f47 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
@@ -44,6 +44,7 @@ import org.apache.karaf.shell.api.console.Session;
 import org.apache.karaf.shell.api.console.SessionFactory;
 import org.apache.karaf.shell.api.console.Terminal;
 import org.apache.karaf.shell.impl.console.parsing.CommandLineParser;
+import org.apache.karaf.shell.impl.console.parsing.KarafParser;
 import org.apache.karaf.shell.support.ShellUtil;
 import org.apache.karaf.shell.support.completers.FileCompleter;
 import org.apache.karaf.shell.support.completers.FileOrUriCompleter;
@@ -187,7 +188,7 @@ public class ConsoleSessionImpl implements Session {
         session.currentDir(null);
 
         reader.setHighlighter(new org.apache.felix.gogo.jline.Highlighter(session));
-        reader.setParser(new org.apache.felix.gogo.jline.Parser());
+        reader.setParser(new KarafParser(this));
 
     }
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/a26dea75/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/KarafParser.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/KarafParser.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/KarafParser.java
new file mode 100644
index 0000000..334e960
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/KarafParser.java
@@ -0,0 +1,117 @@
+/*
+ * 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.impl.console.parsing;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.felix.gogo.jline.ParsedLineImpl;
+import org.apache.felix.gogo.runtime.EOFError;
+import org.apache.felix.gogo.runtime.Parser.Program;
+import org.apache.felix.gogo.runtime.Parser.Statement;
+import org.apache.felix.gogo.runtime.SyntaxError;
+import org.apache.felix.gogo.runtime.Token;
+import org.apache.karaf.shell.api.console.Command;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Parser;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.parsing.DefaultParser;
+import org.jline.reader.ParsedLine;
+
+public class KarafParser implements org.jline.reader.Parser {
+
+    private final Session session;
+
+    public KarafParser(Session session) {
+        this.session = session;
+    }
+
+    public ParsedLine parse(String line, int cursor) throws org.jline.reader.SyntaxError {
+        try {
+            return doParse(line, cursor);
+        } catch (EOFError e) {
+            throw new org.jline.reader.EOFError(e.line(), e.column(), e.getMessage(), e.missing());
+        } catch (SyntaxError e) {
+            throw new org.jline.reader.SyntaxError(e.line(), e.column(), e.getMessage());
+        }
+    }
+
+    private ParsedLine doParse(CharSequence line, int cursor) throws SyntaxError {
+        org.apache.felix.gogo.runtime.Parser parser = new org.apache.felix.gogo.runtime.Parser(line);
+        Program program = parser.program();
+        List<Statement> statements = parser.statements();
+        // Find corresponding statement
+        Statement statement = null;
+        for (int i = statements.size() - 1; i >= 0; i--) {
+            Statement s = statements.get(i);
+            if (s.start() <= cursor) {
+                boolean isOk = true;
+                // check if there are only spaces after the previous statement
+                if (s.start() + s.length() < cursor) {
+                    for (int j = s.start() + s.length(); isOk && j < cursor; j++) {
+                        isOk = Character.isWhitespace(line.charAt(j));
+                    }
+                }
+                statement = s;
+                break;
+            }
+        }
+        if (statement != null) {
+            String cmdName = session.resolveCommand(statement.tokens().get(0).toString());
+            String[] parts = cmdName.split(":");
+            Command cmd = parts.length == 2 ? session.getRegistry().getCommand(parts[0], parts[1]) : null;
+            Parser cmdParser = cmd != null ? cmd.getParser() : null;
+            if (cmdParser != null) {
+                final CommandLine cmdLine = cmdParser.parse(session, statement.toString(), cursor - statement.start());
+                return new ParsedLine() {
+                    @Override
+                    public String word() {
+                        return cmdLine.getCursorArgument();
+                    }
+                    @Override
+                    public int wordCursor() {
+                        return cmdLine.getArgumentPosition();
+                    }
+                    @Override
+                    public int wordIndex() {
+                        return cmdLine.getCursorArgumentIndex();
+                    }
+                    @Override
+                    public List<String> words() {
+                        return Arrays.asList(cmdLine.getArguments());
+                    }
+                    @Override
+                    public String line() {
+                        return cmdLine.getBuffer();
+                    }
+                    @Override
+                    public int cursor() {
+                        return cmdLine.getBufferPosition();
+                    }
+                };
+            }
+            return new ParsedLineImpl(program, statement, cursor, statement.tokens());
+        } else {
+            // TODO:
+            return new ParsedLineImpl(program, program, cursor, Collections.<Token>singletonList(program));
+        }
+    }
+
+}