1 /*
   2  * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jshell;
  27 
  28 import java.util.List;
  29 
  30 /**
  31  * Provides analysis utilities for source code input.
  32  * Optional functionality that provides for a richer interactive experience.
  33  * Includes completion analysis:
  34  * Is the input a complete snippet of code?
  35  * Do I need to prompt for more input?
  36  * Would adding a semicolon make it complete?
  37  * Is there more than one snippet?
  38  * etc.
  39  * Also includes completion suggestions, as might be used in tab-completion.
  40  *
  41  */
  42 public abstract class SourceCodeAnalysis {
  43 
  44     /**
  45      * Given an input string, find the first snippet of code (one statement,
  46      * definition, import, or expression) and evaluate if it is complete.
  47      * @param input the input source string
  48      * @return a CompletionInfo instance with location and completeness info
  49      */
  50     public abstract CompletionInfo analyzeCompletion(String input);
  51 
  52     /**
  53      * Compute possible follow-ups for the given input.
  54      * Uses information from the current <code>JShell</code> state, including
  55      * type information, to filter the suggestions.
  56      * @param input the user input, so far
  57      * @param cursor the current position of the cursors in the given {@code input} text
  58      * @param anchor outgoing parameter - when an option will be completed, the text between
  59      *               the anchor and cursor will be deleted and replaced with the given option
  60      * @return list of candidate continuations of the given input.
  61      */
  62     public abstract List<Suggestion> completionSuggestions(String input, int cursor, int[] anchor);
  63 
  64     /**
  65      * Compute a description/help string for the given user's input.
  66      * @param input the snippet the user wrote so far
  67      * @param cursor the current position of the cursors in the given {@code input} text
  68      * @return description/help string for the given user's input
  69      */
  70     public abstract String documentation(String input, int cursor);
  71 
  72     /**
  73      * Internal only constructor
  74      */
  75     SourceCodeAnalysis() {}
  76 
  77     /**Infer the type of the given expression. Returns null if the type of the expression cannot
  78      * be inferred.
  79      *
  80      * @param code the expression for which the type should be inferred
  81      * @param cursor current cursor position in the given code
  82      * @return the inferred type, or null if it cannot be inferred
  83      */
  84     public abstract String analyzeType(String code, int cursor);
  85 
  86     /**List the possible FQNs for an identifier in the given code immediately
  87      * to the right of the given cursor position.
  88      *
  89      * @param code the expression for which the candidate FQNs should be computed
  90      * @param cursor current cursor position in the given code
  91      * @return the gathered FQNs
  92      */
  93     public abstract IndexResult getDeclaredSymbols(String code, int cursor);
  94 
  95     /**
  96      * The result of <code>analyzeCompletion(String input)</code>.
  97      * Describes the completeness and position of the first snippet in the given input.
  98      */
  99     public static class CompletionInfo {
 100 
 101         public CompletionInfo(Completeness completeness, int unitEndPos, String source, String remaining) {
 102             this.completeness = completeness;
 103             this.unitEndPos = unitEndPos;
 104             this.source = source;
 105             this.remaining = remaining;
 106         }
 107 
 108         /**
 109          * The analyzed completeness of the input.
 110          */
 111         public final Completeness completeness;
 112 
 113         /**
 114          * The end of the first unit of source.
 115          */
 116         public final int unitEndPos;
 117 
 118         /**
 119          * Source code for the first unit of code input.  For example, first
 120          * statement, or first method declaration.  Trailing semicolons will
 121          * be added, as needed
 122          */
 123         public final String source;
 124 
 125         /**
 126          * Input remaining after the source
 127          */
 128         public final String remaining;
 129     }
 130 
 131     /**
 132      * Describes the completeness of the given input.
 133      */
 134     public enum Completeness {
 135         /**
 136          * The input is a complete source snippet (declaration or statement) as is.
 137          */
 138         COMPLETE(true),
 139 
 140         /**
 141          * With this addition of a semicolon the input is a complete source snippet.
 142          * This will only be returned when the end of input is encountered.
 143          */
 144         COMPLETE_WITH_SEMI(true),
 145 
 146         /**
 147          * There must be further source beyond the given input in order for it
 148          * to be complete.  A semicolon would not complete it.
 149          * This will only be returned when the end of input is encountered.
 150          */
 151         DEFINITELY_INCOMPLETE(false),
 152 
 153         /**
 154          * A statement with a trailing (non-terminated) empty statement.
 155          * Though technically it would be a complete statement
 156          * with the addition of a semicolon, it is rare
 157          * that that assumption is the desired behavior.
 158          * The input is considered incomplete.  Comments and white-space are
 159          * still considered empty.
 160          */
 161         CONSIDERED_INCOMPLETE(false),
 162 
 163 
 164         /**
 165          * An empty input.
 166          * The input is considered incomplete.  Comments and white-space are
 167          * still considered empty.
 168          */
 169         EMPTY(false),
 170 
 171         /**
 172          * The completeness of the input could not be determined because it
 173          * contains errors. Error detection is not a goal of completeness
 174          * analysis, however errors interfered with determining its completeness.
 175          * The input is considered complete because evaluating is the best
 176          * mechanism to get error information.
 177          */
 178         UNKNOWN(true);
 179 
 180         /**
 181          * Is the first snippet of source complete. For example, "x=" is not
 182          * complete, but "x=2" is complete, even though a subsequent line could
 183          * make it "x=2+2". Already erroneous code is marked complete.
 184          */
 185         public final boolean isComplete;
 186 
 187         Completeness(boolean isComplete) {
 188             this.isComplete = isComplete;
 189         }
 190     }
 191 
 192     /**
 193      * A candidate for continuation of the given user's input.
 194      */
 195     public static class Suggestion {
 196 
 197         /**
 198          * Create a {@code Suggestion} instance.
 199          * @param continuation a candidate continuation of the user's input
 200          * @param isSmart is the candidate "smart"
 201          */
 202         public Suggestion(String continuation, boolean isSmart) {
 203             this.continuation = continuation;
 204             this.isSmart = isSmart;
 205         }
 206 
 207         /**
 208          * The candidate continuation of the given user's input.
 209          */
 210         public final String continuation;
 211 
 212         /**
 213          * Is it an input continuation that matches the target type and is thus more
 214          * likely to be the desired continuation. A smart continuation
 215          * is preferred.
 216          */
 217         public final boolean isSmart;
 218     }
 219 
 220     /**List of possible qualified names.
 221      */
 222     public static final class IndexResult {
 223 
 224         private final List<String> fqns;
 225         private final int simpleNameLength;
 226         private final boolean upToDate;
 227         private final boolean resolvable;
 228 
 229         public IndexResult(List<String> fqns, int simpleNameLength, boolean upToDate, boolean resolvable) {
 230             this.fqns = fqns;
 231             this.simpleNameLength = simpleNameLength;
 232             this.upToDate = upToDate;
 233             this.resolvable = resolvable;
 234         }
 235 
 236         /**Candidate fully qualified names (FQNs).
 237          *
 238          * @return possible fully qualified names
 239          */
 240         public List<String> getFqns() {
 241             return fqns;
 242         }
 243 
 244         /**The length of the unresolvable simple name in the original code for which the
 245          * FQNs where computed.
 246          *
 247          * @return the length of the simple name; -1 if there is no name right left to the cursor for
 248          *         which the candidates could be computed
 249          */
 250         public int getSimpleNameLength() {
 251             return simpleNameLength;
 252         }
 253 
 254         /**Whether the result is based on up to date data.
 255          *
 256          * @return true iff the results is based on up-to-date data
 257          */
 258         public boolean isUpToDate() {
 259             return upToDate;
 260         }
 261 
 262         /**The given identifier refers to a resolvable element.
 263          *
 264          * @return true iff the given identifier refers to a resolvable element
 265          */
 266         public boolean isResolvable() {
 267             return resolvable;
 268         }
 269 
 270     }
 271 }