--- old/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java 2015-08-19 04:57:53.738331500 +0530 +++ new/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java 2015-08-19 04:57:53.353309500 +0530 @@ -26,6 +26,9 @@ package jdk.nashorn.tools.jjs; import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import java.util.function.Function; import jdk.internal.jline.console.history.FileHistory; import jdk.internal.jline.console.history.History; @@ -38,6 +41,16 @@ * A script friendly object that exposes history of commands to scripts. */ final class HistoryObject extends AbstractJSObject { + private static final Set props; + static { + final HashSet s = new HashSet<>(); + s.add("clear"); + s.add("forEach"); + s.add("print"); + s.add("size"); + props = Collections.unmodifiableSet(s); + } + private final FileHistory hist; HistoryObject(final FileHistory hist) { @@ -72,6 +85,11 @@ return "[object history]"; } + @Override + public Set keySet() { + return props; + } + private void print() { for (History.Entry e : hist) { System.out.println(e.value()); --- old/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java 2015-08-19 04:57:56.136468700 +0530 +++ new/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java 2015-08-19 04:57:55.774448000 +0530 @@ -45,7 +45,7 @@ /** * Interactive command line Shell for Nashorn. */ -public final class Main extends Shell { +public final class Main extends Shell implements PartialParser { private Main() {} // file where history is persisted. @@ -85,6 +85,7 @@ return new Main().run(in, out, err, args); } + /** * read-eval-print loop for Nashorn shell. * @@ -98,7 +99,7 @@ final PrintWriter err = context.getErr(); final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); - final Completer completer = new NashornCompleter(context, global); + final Completer completer = new NashornCompleter(context, global, this); try (final Console in = new Console(System.in, System.out, HIST_FILE, completer)) { if (globalChanged) { @@ -152,4 +153,10 @@ return SUCCESS; } + + // implement PartialParser method by invoking superclass' protected method! + @Override + public int getLastExpressionStart(final Context context, final String code) { + return super.getLastExpressionStart(context, code); + } } --- old/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java 2015-08-19 04:57:58.830622800 +0530 +++ new/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java 2015-08-19 04:57:58.433600100 +0530 @@ -53,11 +53,13 @@ final class NashornCompleter implements Completer { private final Context context; private final Global global; + private final PartialParser partialParser; private final Parser parser; - NashornCompleter(final Context context, final Global global) { + NashornCompleter(final Context context, final Global global, final PartialParser partialParser) { this.context = context; this.global = global; + this.partialParser = partialParser; this.parser = Parser.create(); } @@ -72,14 +74,26 @@ return cursor; } + // get the start of the last expression embedded in the given code + // using the partial parsing support - so that we can complete expressions + // inside statements, function call argument lists, array index etc. + final int exprStart = partialParser.getLastExpressionStart(context, test); + if (exprStart == -1) { + return cursor; + } + + + // extract the last expression string + final String exprStr = test.substring(exprStart); + // do we have an incomplete member selection expression that misses property name? - final boolean endsWithDot = SELECT_PROP_MISSING.matcher(test).matches(); + final boolean endsWithDot = SELECT_PROP_MISSING.matcher(exprStr).matches(); - // If this is an incomplete member selection, then it is not legal code + // If this is an incomplete member selection, then it is not legal code. // Make it legal by adding a random property name "x" to it. - final String exprToEval = endsWithDot? test + "x" : test; + final String completeExpr = endsWithDot? exprStr + "x" : exprStr; - final ExpressionTree topExpr = getTopLevelExpression(parser, exprToEval); + final ExpressionTree topExpr = getTopLevelExpression(parser, completeExpr); if (topExpr == null) { // did not parse to be a top level expression, no suggestions! return cursor; @@ -89,19 +103,19 @@ // Find 'right most' expression of the top level expression final Tree rightMostExpr = getRightMostExpression(topExpr); if (rightMostExpr instanceof MemberSelectTree) { - return completeMemberSelect(test, cursor, result, (MemberSelectTree)rightMostExpr, endsWithDot); + return completeMemberSelect(exprStr, cursor, result, (MemberSelectTree)rightMostExpr, endsWithDot); } else if (rightMostExpr instanceof IdentifierTree) { - return completeIdentifier(test, cursor, result, (IdentifierTree)rightMostExpr); + return completeIdentifier(exprStr, cursor, result, (IdentifierTree)rightMostExpr); } else { // expression that we cannot handle for completion return cursor; } } - private int completeMemberSelect(final String test, final int cursor, final List result, + private int completeMemberSelect(final String exprStr, final int cursor, final List result, final MemberSelectTree select, final boolean endsWithDot) { final ExpressionTree objExpr = select.getExpression(); - final String objExprCode = test.substring((int)objExpr.getStartPosition(), (int)objExpr.getEndPosition()); + final String objExprCode = exprStr.substring((int)objExpr.getStartPosition(), (int)objExpr.getEndPosition()); // try to evaluate the object expression part as a script Object obj = null; --- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java 2015-08-19 04:58:01.244760900 +0530 +++ new/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java 2015-08-19 04:58:00.847738100 +0530 @@ -3237,6 +3237,7 @@ } /** + * {@code * MultiplicativeExpression : * UnaryExpression * MultiplicativeExpression * UnaryExpression @@ -3323,11 +3324,15 @@ * Expression , AssignmentExpression * * See 11.14 + * } * * Parse expression. * @return Expression node. */ - private Expression expression() { + protected Expression expression() { + // This method is protected so that subclass can get details + // at expression start point! + // TODO - Destructuring array. // Include commas in expression parsing. return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false); @@ -3337,7 +3342,7 @@ return new JoinPredecessorExpression(expression()); } - private Expression expression(final Expression exprLhs, final int minPrecedence, final boolean noIn) { + protected Expression expression(final Expression exprLhs, final int minPrecedence, final boolean noIn) { // Get the precedence of the next operator. int precedence = type.getPrecedence(); Expression lhs = exprLhs; @@ -3398,7 +3403,10 @@ return lhs; } - private Expression assignmentExpression(final boolean noIn) { + protected Expression assignmentExpression(final boolean noIn) { + // This method is protected so that subclass can get details + // at assignment expression start point! + // TODO - Handle decompose. // Exclude commas in expression parsing. return expression(unaryExpression(), ASSIGN.getPrecedence(), noIn); --- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java 2015-08-19 04:58:03.899912700 +0530 +++ new/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java 2015-08-19 04:58:03.506890200 +0530 @@ -43,6 +43,7 @@ import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.objects.Global; @@ -397,6 +398,41 @@ } /** + * Parse potentially partial code and keep track of the start of last expression. + * This 'partial' parsing support is meant to be used for code-completion. + * + * @param context the nashorn context + * @param code code that is to be parsed + * @return the start index of the last expression parsed in the (incomplete) code. + */ + protected int getLastExpressionStart(final Context context, final String code) { + final int[] exprStart = { -1 }; + + final Parser p = new Parser(context.getEnv(), sourceFor("", code),new Context.ThrowErrorManager()) { + @Override + protected Expression expression() { + exprStart[0] = this.start; + return super.expression(); + } + + @Override + protected Expression assignmentExpression(final boolean noIn) { + exprStart[0] = this.start; + return super.expression(); + } + }; + + try { + p.parse(); + } catch (final Exception ignored) { + // throw any parser exception, but we are partial parsing anyway + } + + return exprStart[0]; + } + + + /** * read-eval-print loop for Nashorn shell. * * @param context the nashorn context --- /dev/null 2015-08-19 04:58:06.000000000 +0530 +++ new/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/PartialParser.java 2015-08-19 04:58:05.839023600 +0530 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.tools.jjs; + +import jdk.nashorn.internal.runtime.Context; + +/** + * Partial parsing support for code completion of expressions. + */ +interface PartialParser { + /** + * Parse potentially partial code and keep track of the start of last expression. + * + * @param context the nashorn context + * @param code code that is to be parsed + * @return the start index of the last expression parsed in the (incomplete) code. + */ + int getLastExpressionStart(final Context context, final String code); +}