diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleCompleter.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleCompleter.java index dc79ee71d212978d766ecfdf60de89138cac0f99..99777055f92d9dd36c21adc023f195104977af56 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleCompleter.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleCompleter.java @@ -73,7 +73,7 @@ public class JLineConsoleCompleter implements Completer { setFunction.execute(completionEnv, "start", start); setFunction.execute(completionEnv, "end", cursor); setFunction.execute(completionEnv, "linebuffer", buffer); - setFunction.execute(completionEnv, "token", buffer.substring(start, cursor)); + setFunction.execute(completionEnv, "token", start > -1 && start < buffer.length() && cursor > -1 && cursor <= buffer.length() ? buffer.substring(start, cursor).trim() : ""); completionFunction.execute(); @@ -98,13 +98,33 @@ public class JLineConsoleCompleter implements Completer { private static int getStart(String buffer, Value env, int cursor) { int start = 0; + + // are we in quotes? + int lastQuoteIdx = isInQuotes(buffer, cursor); + if (lastQuoteIdx != -1) { + return lastQuoteIdx; + } + Value opt = env.getMember("options"); if (opt.hasMembers()) { start = lastIdxOf(buffer, opt, "funarg.suffix", start, cursor); start = lastIdxOf(buffer, opt, "function.suffix", start, cursor); } - start = lastIdxOf(buffer, "\"", start, cursor); - start = lastIdxOf(buffer, "'", start, cursor); + + // are we just after a ',' or ' ' + if (cursor > 0 && cursor <= buffer.length() && (buffer.charAt(cursor - 1) == ',' || buffer.charAt(cursor - 1) == ' ')) { + return cursor; + } + + // is there any next closest ',' or ' '? + int idx = cursor >= buffer.length() ? buffer.length() - 1 : cursor; + while (idx >= start && (buffer.charAt(idx) != ',' && buffer.charAt(idx) != ' ')) { + --idx; + } + if (idx > -1) { + return ++idx; + } + return start; } @@ -120,6 +140,19 @@ public class JLineConsoleCompleter implements Completer { return start; } + private static int isInQuotes(String buffer, int cursor) { + int idx = -1; + int qidx = -1; + int c = 0; + while (++idx <= cursor && idx < buffer.length()) { + if (buffer.charAt(idx) == '\'' || buffer.charAt(idx) == '\"') { + qidx = idx; + c++; + } + } + return c % 2 == 0 ? -1 : qidx; + } + private static int lastIdxOf(String buffer, String subs, int start, int cursor) { if (!subs.isEmpty()) { int idx = buffer.lastIndexOf(subs, cursor); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java index 2be3b641a8ec39f2d0d2188abe89eac7cdbc4c41..2df8eb603794c158a712ea6ca84146946623c2cd 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java @@ -793,7 +793,7 @@ public class GrepFunctions { throw RInternalError.unimplemented("multi-element patterns in regexpr not implemented yet"); } String pattern = patternArg.getDataAt(0); - if (!perl) { + if (!perl && !fixed) { pattern = RegExp.checkPreDefinedClasses(pattern); } // TODO: useBytes normally depends on the value of the parameter and (if false) on @@ -1090,7 +1090,7 @@ public class GrepFunctions { throw RInternalError.unimplemented("multi-element patterns in gregexpr not implemented yet"); } String pattern = patternArg.getDataAt(0); - if (!perl) { + if (!perl && !fixed) { pattern = RegExp.checkPreDefinedClasses(pattern); } // TODO: useBytes normally depends on the value of the parameter and (if false) on diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 35e626cff237e38f505e8f81d7783ab4d59a2614..6ece459577d039a5b1fc206a9d530e9c0e187206 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -25389,6 +25389,61 @@ attr(,"useBytes") [1] TRUE +##com.oracle.truffle.r.test.builtins.TestBuiltin_gregexpr.testRegExpr#Output.IgnoreErrorMessage# +#gregexpr('(', 'abc()', fixed = FALSE) +Error in gregexpr("(", "abc()", fixed = FALSE) : + invalid regular expression '(', reason 'Missing ')'' + +##com.oracle.truffle.r.test.builtins.TestBuiltin_gregexpr.testRegExpr# +#gregexpr('(', 'abc()', fixed = TRUE) +[[1]] +[1] 4 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_gregexpr.testRegExpr# +#gregexpr(')', 'abc()', fixed = FALSE) +[[1]] +[1] 5 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_gregexpr.testRegExpr# +#gregexpr(')', 'abc()', fixed = TRUE) +[[1]] +[1] 5 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_gregexpr.testRegExpr# +#gregexpr('\\(', 'abc()', fixed = FALSE) +[[1]] +[1] 4 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_gregexpr.testRegExpr# +#gregexpr('\\)', 'abc()', fixed = FALSE) +[[1]] +[1] 5 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_gregexpr.testRegExpr# #{ .Internal(gregexpr("7", 42, F, F, F, F)) } Error: invalid 'text' argument @@ -47602,6 +47657,51 @@ attr(,"match.length") attr(,"useBytes") [1] TRUE +##com.oracle.truffle.r.test.builtins.TestBuiltin_regexpr.testRegExpr#Output.IgnoreErrorMessage# +#regexpr('(', 'abc()', fixed = FALSE) +Error in regexpr("(", "abc()", fixed = FALSE) : + invalid regular expression '(', reason 'Missing ')'' + +##com.oracle.truffle.r.test.builtins.TestBuiltin_regexpr.testRegExpr# +#regexpr('(', 'abc()', fixed = TRUE) +[1] 4 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + +##com.oracle.truffle.r.test.builtins.TestBuiltin_regexpr.testRegExpr# +#regexpr(')', 'abc()', fixed = FALSE) +[1] 5 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + +##com.oracle.truffle.r.test.builtins.TestBuiltin_regexpr.testRegExpr# +#regexpr(')', 'abc()', fixed = TRUE) +[1] 5 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + +##com.oracle.truffle.r.test.builtins.TestBuiltin_regexpr.testRegExpr# +#regexpr('\\(', 'abc()', fixed = FALSE) +[1] 4 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + +##com.oracle.truffle.r.test.builtins.TestBuiltin_regexpr.testRegExpr# +#regexpr('\\)', 'abc()', fixed = FALSE) +[1] 5 +attr(,"match.length") +[1] 1 +attr(,"useBytes") +[1] TRUE + ##com.oracle.truffle.r.test.builtins.TestBuiltin_regexpr.testRegExpr# #{ .Internal(regexpr("7", 42, F, F, F, F)) } Error: invalid 'text' argument diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_gregexpr.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_gregexpr.java index 1d55ebbc8d048b622032a4ea4790025fdfebdb9e..08a34adfe09cbfd428691e2662983018c2d2eede 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_gregexpr.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_gregexpr.java @@ -120,5 +120,12 @@ public class TestBuiltin_gregexpr extends TestBase { assertEval("{ x<-c(\"Aaa bbb Aaa Bbb\", \"Aaa bbb Aaa bbb\", \"Aaa bbb Aaa Bbb\"); p<-\"(?<first>[[:upper:]][[:lower:]]+) (?<last>[[:upper:]][[:lower:]]+)\"; gregexpr(p, x, perl=TRUE) }"); assertEval("{ x<-c(\"Aaa bbb Aaa bbb\", \"Aaa Bbb Aaa Bbb\", \"Aaa bbb Aaa bbb\"); p<-\"(?<first>[[:upper:]][[:lower:]]+) (?<last>[[:upper:]][[:lower:]]+)\"; gregexpr(p, x, perl=TRUE) }"); + assertEval("gregexpr(')', 'abc()', fixed = TRUE)"); + assertEval("gregexpr('(', 'abc()', fixed = TRUE)"); + assertEval("gregexpr(')', 'abc()', fixed = FALSE)"); + assertEval("gregexpr('\\\\)', 'abc()', fixed = FALSE)"); + assertEval(Output.IgnoreErrorMessage, "gregexpr('(', 'abc()', fixed = FALSE)"); + assertEval("gregexpr('\\\\(', 'abc()', fixed = FALSE)"); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_regexpr.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_regexpr.java index a4ed9501b845b8a1d136c6dc81d157fb66f2d481..a9e76b8ee72c34108539805352efdaffc24c6933 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_regexpr.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_regexpr.java @@ -118,5 +118,11 @@ public class TestBuiltin_regexpr extends TestBase { assertEval("{ x<-c(\"Aaa bbb Aaa bbb\", \"Aaa Bbb Aaa Bbb\"); p<-\"(?<first>[[:upper:]][[:lower:]]+) (?<last>[[:upper:]][[:lower:]]+)\"; regexpr(p, x, perl=TRUE) }"); assertEval("{ x<-c(\"Aaa bbb Aaa bbb\", \"Aaa Bbb Aaa Bbb\", \"Aaa bbb Aaa bbb\"); p<-\"(?<first>[[:upper:]][[:lower:]]+) (?<last>[[:upper:]][[:lower:]]+)\"; regexpr(p, x, perl=TRUE) }"); + assertEval("regexpr(')', 'abc()', fixed = TRUE)"); + assertEval("regexpr('(', 'abc()', fixed = TRUE)"); + assertEval("regexpr(')', 'abc()', fixed = FALSE)"); + assertEval("regexpr('\\\\)', 'abc()', fixed = FALSE)"); + assertEval(Output.IgnoreErrorMessage, "regexpr('(', 'abc()', fixed = FALSE)"); + assertEval("regexpr('\\\\(', 'abc()', fixed = FALSE)"); } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/shell/TestJLineConsoleCompleter.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/shell/TestJLineConsoleCompleter.java index eaabb32efebf6e106e3d3b75a0b94911d2edf486..d42b412385fbd6c7b1d67a625bd7bc4dea428eca 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/shell/TestJLineConsoleCompleter.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/shell/TestJLineConsoleCompleter.java @@ -32,10 +32,10 @@ import org.graalvm.polyglot.Context; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import com.oracle.truffle.r.launcher.JLineConsoleCompleter; +import org.junit.Ignore; public class TestJLineConsoleCompleter { @@ -70,10 +70,27 @@ public class TestJLineConsoleCompleter { assertCompl("$", 1); assertCompl("strt", 4, "strtoi", "strtrim"); + assertCompl("strt", 5); + assertCompl("strt", 6); + assertCompl("strto", 5, "strtoi"); assertCompl("strtoi", 5, "strtoi"); assertCompl("strtoi", 4, "strtoi", "strtrim"); assertCompl("strto ", 6); + assertCompl("strto,", 6); + assertCompl("blabla,strt", 11, "strtoi", "strtrim"); + assertCompl("blabla strt", 11, "strtoi", "strtrim"); + assertCompl("blabla,,strt", 12, "strtoi", "strtrim"); + assertCompl("blabla strt", 12, "strtoi", "strtrim"); + assertCompl("blabla, strt", 12, "strtoi", "strtrim"); + // Checkstyle: stop + assertCompl("blabla ,strt", 12, "strtoi", "strtrim"); + // Checkstyle: resume + + assertCompl("source('a')", 10); + assertCompl("source('a')", 11); + assertCompl("source('a') ", 12); + assertCompl("source('a') ", 13); assertCompl("base::strt", 10, "base::strtoi", "base::strtrim"); assertCompl("base:::strt", 11, "base:::strtoi", "base:::strtrim"); @@ -88,6 +105,30 @@ public class TestJLineConsoleCompleter { assertCompl("f(strt(strto", 11, "strtoi", "strtrim"); assertCompl("f(strt(strto", 12, "strtoi"); + assertCompl("grep(", 5, "fixed=", "ignore.case=", "invert=", "pattern=", "perl=", "useBytes=", "value=", "x="); + assertCompl("grep(pattern=\"a\",", 17, "fixed=", "ignore.case=", "invert=", "pattern=", "perl=", "useBytes=", "value=", "x="); + assertCompl("grep(pattern=\"a\"", 16); + assertCompl("grep(pattern=\"a\", fixe", 22, "fixed="); + + assertCompl("grep (patt", 10, "pattern="); + assertCompl("grep,(patt", 10, "pattern="); + assertCompl("grep (patt", 11, "pattern="); + assertCompl("grep,,(patt", 11, "pattern="); + // Checkstyle: stop + assertCompl("grep ,(patt", 11, "pattern="); + // Checkstyle: resume + assertCompl("grep, (patt", 11, "pattern="); + + assertCompl("grep(patt ", 10, "fixed=", "ignore.case=", "invert=", "pattern=", "perl=", "useBytes=", "value=", "x="); + assertCompl("grep (patt ", 11, "fixed=", "ignore.case=", "invert=", "pattern=", "perl=", "useBytes=", "value=", "x="); + assertCompl("grep (patt ", 12, "fixed=", "ignore.case=", "invert=", "pattern=", "perl=", "useBytes=", "value=", "x="); + + // show only arguments for 'cor', and not also those for 'cor.test', 'cor.test.name' etc. + assertCompl("cor(", 3, "cor", "cor.test"); + assertCompl("cor(", 4, "method=", "use=", "x=", "y="); + assertCompl("cor(", 5, "method=", "use=", "x=", "y="); + assertCompl("cor( ", 5, "method=", "use=", "x=", "y="); + String noName = "_f_f_f_"; assertCompl(noName + ".", 7); assertCompl(noName + ".", 8);