/* * Copyright (c) 2014, 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.jshell; import java.util.List; /** * Provides analysis utilities for source code input. * Optional functionality that provides for a richer interactive experience. * Includes completion analysis: * Is the input a complete snippet of code? * Do I need to prompt for more input? * Would adding a semicolon make it complete? * Is there more than one snippet? * etc. * Also includes completion suggestions, as might be used in tab-completion. * */ public abstract class SourceCodeAnalysis { /** * Given an input string, find the first snippet of code (one statement, * definition, import, or expression) and evaluate if it is complete. * @param input the input source string * @return a CompletionInfo instance with location and completeness info */ public abstract CompletionInfo analyzeCompletion(String input); /** * Compute possible follow-ups for the given input. * Uses information from the current JShell state, including * type information, to filter the suggestions. * @param input the user input, so far * @param cursor the current position of the cursors in the given {@code input} text * @param anchor outgoing parameter - when an option will be completed, the text between * the anchor and cursor will be deleted and replaced with the given option * @return list of candidate continuations of the given input. */ public abstract List completionSuggestions(String input, int cursor, int[] anchor); /** * Compute a description/help string for the given user's input. * @param input the snippet the user wrote so far * @param cursor the current position of the cursors in the given {@code input} text * @return description/help string for the given user's input */ public abstract String documentation(String input, int cursor); /** * Internal only constructor */ SourceCodeAnalysis() {} /**Infer the type of the given expression. Returns null if the type of the expression cannot * be inferred. * * @param code the expression for which the type should be inferred * @param cursor current cursor position in the given code * @return the inferred type, or null if it cannot be inferred */ public abstract String analyzeType(String code, int cursor); /**List the possible FQNs for an identifier in the given code immediately * to the right of the given cursor position. * * @param code the expression for which the candidate FQNs should be computed * @param cursor current cursor position in the given code * @return the gathered FQNs */ public abstract IndexResult getDeclaredSymbols(String code, int cursor); /** * The result of analyzeCompletion(String input). * Describes the completeness and position of the first snippet in the given input. */ public static class CompletionInfo { public CompletionInfo(Completeness completeness, int unitEndPos, String source, String remaining) { this.completeness = completeness; this.unitEndPos = unitEndPos; this.source = source; this.remaining = remaining; } /** * The analyzed completeness of the input. */ public final Completeness completeness; /** * The end of the first unit of source. */ public final int unitEndPos; /** * Source code for the first unit of code input. For example, first * statement, or first method declaration. Trailing semicolons will * be added, as needed */ public final String source; /** * Input remaining after the source */ public final String remaining; } /** * Describes the completeness of the given input. */ public enum Completeness { /** * The input is a complete source snippet (declaration or statement) as is. */ COMPLETE(true), /** * With this addition of a semicolon the input is a complete source snippet. * This will only be returned when the end of input is encountered. */ COMPLETE_WITH_SEMI(true), /** * There must be further source beyond the given input in order for it * to be complete. A semicolon would not complete it. * This will only be returned when the end of input is encountered. */ DEFINITELY_INCOMPLETE(false), /** * A statement with a trailing (non-terminated) empty statement. * Though technically it would be a complete statement * with the addition of a semicolon, it is rare * that that assumption is the desired behavior. * The input is considered incomplete. Comments and white-space are * still considered empty. */ CONSIDERED_INCOMPLETE(false), /** * An empty input. * The input is considered incomplete. Comments and white-space are * still considered empty. */ EMPTY(false), /** * The completeness of the input could not be determined because it * contains errors. Error detection is not a goal of completeness * analysis, however errors interfered with determining its completeness. * The input is considered complete because evaluating is the best * mechanism to get error information. */ UNKNOWN(true); /** * Is the first snippet of source complete. For example, "x=" is not * complete, but "x=2" is complete, even though a subsequent line could * make it "x=2+2". Already erroneous code is marked complete. */ public final boolean isComplete; Completeness(boolean isComplete) { this.isComplete = isComplete; } } /** * A candidate for continuation of the given user's input. */ public static class Suggestion { /** * Create a {@code Suggestion} instance. * @param continuation a candidate continuation of the user's input * @param isSmart is the candidate "smart" */ public Suggestion(String continuation, boolean isSmart) { this.continuation = continuation; this.isSmart = isSmart; } /** * The candidate continuation of the given user's input. */ public final String continuation; /** * Is it an input continuation that matches the target type and is thus more * likely to be the desired continuation. A smart continuation * is preferred. */ public final boolean isSmart; } /**List of possible qualified names. */ public static final class IndexResult { private final List fqns; private final int simpleNameLength; private final boolean upToDate; private final boolean resolvable; public IndexResult(List fqns, int simpleNameLength, boolean upToDate, boolean resolvable) { this.fqns = fqns; this.simpleNameLength = simpleNameLength; this.upToDate = upToDate; this.resolvable = resolvable; } /**Candidate fully qualified names (FQNs). * * @return possible fully qualified names */ public List getFqns() { return fqns; } /**The length of the unresolvable simple name in the original code for which the * FQNs where computed. * * @return the length of the simple name; -1 if there is no name right left to the cursor for * which the candidates could be computed */ public int getSimpleNameLength() { return simpleNameLength; } /**Whether the result is based on up to date data. * * @return true iff the results is based on up-to-date data */ public boolean isUpToDate() { return upToDate; } /**The given identifier refers to a resolvable element. * * @return true iff the given identifier refers to a resolvable element */ public boolean isResolvable() { return resolvable; } } }