From 9b0c9874644e0814136cdd565aba194d10ea0d99 Mon Sep 17 00:00:00 2001 From: mattirn Date: Sat, 15 Feb 2020 18:59:40 +0100 Subject: [PATCH] Parser: added SPLIT_LINE context --- .../jline/builtins/SystemRegistryImpl.java | 54 ++++++------------- .../main/java/org/jline/reader/Parser.java | 6 +++ .../org/jline/reader/impl/DefaultParser.java | 7 ++- .../jline/reader/impl/DefaultParserTest.java | 11 ++++ 4 files changed, 38 insertions(+), 40 deletions(-) diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java index 21c11e63e..3d8b939dc 100644 --- a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java +++ b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java @@ -490,11 +490,10 @@ private boolean isPipe(String arg, Set pipes) { private List compileCommandLine(String commandLine) { List out = new ArrayList<>(); - ParsedLine pl = parser.parse(commandLine, 0, ParseContext.ACCEPT_LINE); + ParsedLine pl = parser.parse(commandLine, 0, ParseContext.SPLIT_LINE); List ws = pl.words(); List words = new ArrayList<>(); String nextRawLine = ""; - ArgsParser argsParser = new ArgsParser(); Map> customPipes = consoleId != null ? consoleEngine().getPipes() : new HashMap<>(); if (consoleId != null && pl.words().contains(pipeName.get(Pipe.NAMED))) { StringBuilder sb = new StringBuilder(); @@ -524,15 +523,15 @@ private List compileCommandLine(String commandLine) { } sb.append(pipeAlias); } else { - sb.append(argsParser.arg2Line(ws.get(i))); + sb.append(ws.get(i)); } } else { - sb.append(argsParser.arg2Line(ws.get(i))); + sb.append(ws.get(i)); } sb.append(" "); } nextRawLine = sb.toString(); - pl = parser.parse(nextRawLine, 0, ParseContext.ACCEPT_LINE); + pl = parser.parse(nextRawLine, 0, ParseContext.SPLIT_LINE); words = pl.words(); if (trace) { consoleEngine().trace(nextRawLine); @@ -553,7 +552,7 @@ private List compileCommandLine(String commandLine) { if (parser.validCommandName(command) && consoleId != null) { variable = parser.getVariable(words.get(first)); if (consoleEngine().hasAlias(command)) { - pl = parser.parse(nextRawLine.replaceFirst(command, consoleEngine().getAlias(command)), 0, ParseContext.ACCEPT_LINE); + pl = parser.parse(nextRawLine.replaceFirst(command, consoleEngine().getAlias(command)), 0, ParseContext.SPLIT_LINE); command = ConsoleEngine.plainCommand(parser.getCommand(pl.word())); words = pl.words(); first = 0; @@ -563,7 +562,7 @@ private List compileCommandLine(String commandLine) { File file = null; boolean append = false; boolean pipeStart = false; - argsParser = new ArgsParser(); + ArgsParser argsParser = new ArgsParser(); List _words = new ArrayList<>(); for (int i = first; i < last; i++) { argsParser.next(words.get(i)); @@ -634,12 +633,11 @@ private List compileCommandLine(String commandLine) { if (last == words.size()) { pipes.add("END_PIPE"); } - argsParser = new ArgsParser(); String subLine = last < words.size() || first > 0 - ? argsParser.join(_words) + ? _words.stream().collect(Collectors.joining(" ")) : pl.line(); if (last + 1 < words.size()) { - nextRawLine = argsParser.join(words.subList(last + 1, words.size())); + nextRawLine = words.subList(last + 1, words.size()).stream().collect(Collectors.joining(" ")); } boolean done = true; boolean statement = false; @@ -687,7 +685,13 @@ private List compileCommandLine(String commandLine) { rawLine = flipArgument(command, subLine, pipes, arglist); } if (done) { - String[] args = statement ? new String[] {} : arglist.toArray(new String[0]); + String[] args = new String[] {}; + if (!statement) { + ParsedLine plf = parser.parse(rawLine, 0, ParseContext.ACCEPT_LINE); + if (plf.words().size() > 1) { + args = plf.words().subList(1, plf.words().size()).toArray(new String[0]); + } + } out.add(new CommandData(rawLine, statement ? "" : command, args, variable, file, append, pipes.get(pipes.size() - 1))); rawLine = null; } @@ -702,14 +706,10 @@ private static class ArgsParser { private int square = 0; private boolean quoted; private boolean doubleQuoted; - private boolean unquotedSpace; - private boolean hasQuote; public ArgsParser() {} public void next(String arg) { - unquotedSpace = false; - hasQuote = false; char prevChar = ' '; for (int i = 0; i < arg.length(); i++) { char c = arg.charAt(i); @@ -731,9 +731,6 @@ public void next(String arg) { doubleQuoted = true; } else if (c == '\'') { quoted = true; - hasQuote = true; - } else if (c == ' ') { - unquotedSpace = true; } } else if (quoted && c == '\'') { quoted = false; @@ -749,27 +746,6 @@ public boolean isEnclosed() { return round != 0 || curly != 0 || square != 0 || quoted || doubleQuoted; } - public String join(List words) { - StringBuilder sb = new StringBuilder(); - boolean first = true; - for (String w : words) { - if (!first) { - sb.append(" "); - } - sb.append(arg2Line(w)); - first = false; - } - return sb.toString(); - } - - public String arg2Line(String arg) { - String out = arg; - next(arg); - if (unquotedSpace) { - out = hasQuote ? "\"" + out + "\"" : "'" + out + "'"; - } - return out; - } } private String flipArgument(final String command, final String subLine, final List pipes, List arglist) { diff --git a/reader/src/main/java/org/jline/reader/Parser.java b/reader/src/main/java/org/jline/reader/Parser.java index 3187fb53b..90098c1b1 100644 --- a/reader/src/main/java/org/jline/reader/Parser.java +++ b/reader/src/main/java/org/jline/reader/Parser.java @@ -70,6 +70,12 @@ enum ParseContext { */ ACCEPT_LINE, + /** Parsed words will have all characters present in input line + * including quotes and escape chars. + * May throw EOFError in which case we have incomplete input. + */ + SPLIT_LINE, + /** Parse to find completions (typically after a Tab). * We should tolerate and ignore errors. */ diff --git a/reader/src/main/java/org/jline/reader/impl/DefaultParser.java b/reader/src/main/java/org/jline/reader/impl/DefaultParser.java index e61466d59..6328882bd 100644 --- a/reader/src/main/java/org/jline/reader/impl/DefaultParser.java +++ b/reader/src/main/java/org/jline/reader/impl/DefaultParser.java @@ -238,12 +238,15 @@ public ParsedLine parse(final String line, final int cursor, ParseContext contex quoteStart = i; if (current.length()==0) { quotedWord = true; + if (context == ParseContext.SPLIT_LINE) { + current.append(line.charAt(i)); + } } else { current.append(line.charAt(i)); } } else if (quoteStart >= 0 && line.charAt(quoteStart) == line.charAt(i) && !isEscaped(line, i)) { // End quote block - if (!quotedWord) { + if (!quotedWord || context == ParseContext.SPLIT_LINE) { current.append(line.charAt(i)); } else if (rawWordCursor >= 0 && rawWordLength < 0) { rawWordLength = i - rawWordStart + 1; @@ -266,6 +269,8 @@ public ParsedLine parse(final String line, final int cursor, ParseContext contex if (quoteStart < 0) { bracketChecker.check(line, i); } + } else if (context == ParseContext.SPLIT_LINE) { + current.append(line.charAt(i)); } } } diff --git a/reader/src/test/java/org/jline/reader/impl/DefaultParserTest.java b/reader/src/test/java/org/jline/reader/impl/DefaultParserTest.java index b0b266bb5..a008c75d0 100644 --- a/reader/src/test/java/org/jline/reader/impl/DefaultParserTest.java +++ b/reader/src/test/java/org/jline/reader/impl/DefaultParserTest.java @@ -10,6 +10,7 @@ import org.jline.reader.CompletingParsedLine; import org.jline.reader.ParsedLine; +import org.jline.reader.Parser.ParseContext; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -66,4 +67,14 @@ public void testVariable() { assertEquals("variable['key']", parser.getVariable("variable['key'] = statement")); } + @Test + public void testSplitLine() { + DefaultParser parser = new DefaultParser(); + CompletingParsedLine line = (CompletingParsedLine) parser.parse("foo second\\ param \"quoted param\"", 0, ParseContext.SPLIT_LINE); + assertNotNull(line); + assertNotNull(line.words()); + assertEquals("foo", line.words().get(0)); + assertEquals("second\\ param", line.words().get(1)); + assertEquals("\"quoted param\"", line.words().get(2)); + } }