Skip to content

Commit

Permalink
DefaultParser: manage eofOnUnclosedBracket
Browse files Browse the repository at this point in the history
  • Loading branch information
mattirn committed Dec 24, 2018
1 parent 7783c03 commit dd4e507
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 0 deletions.
8 changes: 8 additions & 0 deletions builtins/src/test/java/org/jline/example/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.jline.keymap.KeyMap;
import org.jline.reader.*;
import org.jline.reader.impl.DefaultParser;
import org.jline.reader.impl.DefaultParser.Bracket;
import org.jline.reader.impl.LineReaderImpl;
import org.jline.reader.impl.completer.ArgumentCompleter;
import org.jline.reader.impl.completer.StringsCompleter;
Expand Down Expand Up @@ -130,6 +131,12 @@ public static void main(String[] args) throws IOException {
p.setEofOnUnclosedQuote(true);
parser = p;
break label;
case "brackets":
prompt = "long-prompt> ";
DefaultParser p2 = new DefaultParser();
p2.eofOnUnclosedBracket(Bracket.CURLY,Bracket.ROUND,Bracket.SQUARE);
parser = p2;
break label;
case "foo":
completer = new ArgumentCompleter(
new StringsCompleter("foo11", "foo12", "foo13"),
Expand Down Expand Up @@ -228,6 +235,7 @@ public void complete(LineReader reader, ParsedLine line, List<Candidate> candida
.terminal(terminal)
.completer(completer)
.parser(parser)
.variable(LineReader.SECONDARY_PROMPT_PATTERN, "%M%P > ")
.build();

if (timer) {
Expand Down
113 changes: 113 additions & 0 deletions reader/src/main/java/org/jline/reader/impl/DefaultParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
import org.jline.reader.Parser;

public class DefaultParser implements Parser {

public enum Bracket {
ROUND, // ()
CURLY, // {}
SQUARE, // []
ANGLE; // <>
}

private char[] quoteChars = {'\'', '"'};

Expand All @@ -25,7 +32,11 @@ public class DefaultParser implements Parser {
private boolean eofOnUnclosedQuote;

private boolean eofOnEscapedNewLine;

private char[] openingBrackets = null;

private char[] closingBrackets = null;

//
// Chainable setters
//
Expand All @@ -45,6 +56,11 @@ public DefaultParser eofOnUnclosedQuote(boolean eofOnUnclosedQuote) {
return this;
}

public DefaultParser eofOnUnclosedBracket(Bracket... brackets){
setEofOnUnclosedBracket(brackets);
return this;
}

public DefaultParser eofOnEscapedNewLine(boolean eofOnEscapedNewLine) {
this.eofOnEscapedNewLine = eofOnEscapedNewLine;
return this;
Expand Down Expand Up @@ -86,6 +102,40 @@ public boolean isEofOnEscapedNewLine() {
return eofOnEscapedNewLine;
}

public void setEofOnUnclosedBracket(Bracket... brackets){
if (brackets == null) {
openingBrackets = null;
closingBrackets = null;
} else {
Set<Bracket> bs = new HashSet<>();
bs.addAll(Arrays.asList(brackets));
openingBrackets = new char[bs.size()];
closingBrackets = new char[bs.size()];
int i = 0;
for (Bracket b : bs) {
switch (b) {
case ROUND:
openingBrackets[i] = '(';
closingBrackets[i] = ')';
break;
case CURLY:
openingBrackets[i] = '{';
closingBrackets[i] = '}';
break;
case SQUARE:
openingBrackets[i] = '[';
closingBrackets[i] = ']';
break;
case ANGLE:
openingBrackets[i] = '<';
closingBrackets[i] = '>';
break;
}
i++;
}
}
}

public ParsedLine parse(final String line, final int cursor, ParseContext context) {
List<String> words = new LinkedList<>();
StringBuilder current = new StringBuilder();
Expand All @@ -95,6 +145,7 @@ public ParsedLine parse(final String line, final int cursor, ParseContext contex
int rawWordCursor = -1;
int rawWordLength = -1;
int rawWordStart = 0;
BracketChecker bracketChecker = new BracketChecker();

for (int i = 0; (line != null) && (i < line.length()); i++) {
// once we reach the cursor, set the
Expand Down Expand Up @@ -129,6 +180,7 @@ public ParsedLine parse(final String line, final int cursor, ParseContext contex
} else {
if (!isEscapeChar(line, i)) {
current.append(line.charAt(i));
bracketChecker.check(line, i);
}
}
}
Expand All @@ -154,6 +206,12 @@ public ParsedLine parse(final String line, final int cursor, ParseContext contex
throw new EOFError(-1, -1, "Missing closing quote", line.charAt(quoteStart) == '\''
? "quote" : "dquote");
}
if (bracketChecker.isOpeningBracketMissing() && context != ParseContext.COMPLETE) {
throw new EOFError(-1, -1, "Missing opening bracket", "missing: " + bracketChecker.getMissingOpeningBracket());
}
if (bracketChecker.isClosingBracketMissing() && context != ParseContext.COMPLETE) {
throw new EOFError(-1, -1, "Missing closing brackets", "+: " + bracketChecker.getMissingClosingBrackets());
}

String openingQuote = quoteStart >= 0 ? line.substring(quoteStart, quoteStart + 1) : null;
return new ArgumentList(line, words, wordIndex, wordCursor, cursor, openingQuote, rawWordCursor, rawWordLength);
Expand Down Expand Up @@ -274,6 +332,61 @@ private boolean isRawQuoteChar(char key) {
return false;
}

private class BracketChecker {
private int missingOpeningBracket = -1;
private List<Integer> nested = new ArrayList<>();

public BracketChecker(){}

public void check(final CharSequence buffer, final int pos){
if (openingBrackets == null || pos < 0) return;
int bid = bracketId(openingBrackets, buffer, pos);
if (bid >= 0) {
nested.add(bid);
} else {
bid = bracketId(closingBrackets, buffer, pos);
if (bid >= 0) {
if (!nested.isEmpty() && bid == nested.get(nested.size()-1)) {
nested.remove(nested.size()-1);
} else {
missingOpeningBracket = bid;
}
}
}
}

public boolean isOpeningBracketMissing(){
return missingOpeningBracket != -1;
}

public String getMissingOpeningBracket(){
if (!isOpeningBracketMissing()) return null;
return Character.toString(openingBrackets[missingOpeningBracket]);
}

public boolean isClosingBracketMissing(){
return !nested.isEmpty();
}

public String getMissingClosingBrackets(){
if (!isClosingBracketMissing()) return null;
String out="";
for (int i=nested.size()-1; i>-1; i--) {
out += Character.toString(closingBrackets[nested.get(i)]);
}
return out;
}

private int bracketId(final char[] brackets, final CharSequence buffer, final int pos){
for (int i=0; i < brackets.length; i++) {
if (buffer.charAt(pos) == brackets[i]) {
return i;
}
}
return -1;
}
}

/**
* The result of a delimited buffer.
*
Expand Down

0 comments on commit dd4e507

Please sign in to comment.