| /******************************************************************************* |
| * Copyright (c) 2000, 2022 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Technical University Berlin - extended API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.codeassist.complete; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.compiler.InvalidInputException; |
| import org.eclipse.jdt.internal.codeassist.CompletionEngine; |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.parser.JavadocParser; |
| import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; |
| import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; |
| |
| /** |
| * Parser specialized for decoding javadoc comments which includes cursor location for code completion. |
| */ |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public class CompletionJavadocParser extends JavadocParser { |
| |
| boolean parsingSnippet=false; |
| |
| // Initialize lengthes for block and inline tags tables |
| public final static int INLINE_ALL_TAGS_LENGTH; |
| public final static int BLOCK_ALL_TAGS_LENGTH; |
| public final static int SNIPPET_ALL_TAGS_LENGTH; |
| |
| static { |
| int length = 0; |
| for (int i=0; i<INLINE_TAGS_LENGTH; i++) { |
| length += INLINE_TAGS[i].length; |
| } |
| INLINE_ALL_TAGS_LENGTH = length; |
| length = 0; |
| for (int i=0; i<BLOCK_TAGS_LENGTH; i++) { |
| length += BLOCK_TAGS[i].length; |
| } |
| BLOCK_ALL_TAGS_LENGTH = length; |
| length = 0; |
| for (int i=0; i<SNIPPET_TAGS_LENGTH; i++) { |
| length += IN_SNIPPET_TAGS[i].length; |
| } |
| SNIPPET_ALL_TAGS_LENGTH = length; |
| //IN_SNIPPET_TAGS |
| } |
| |
| // Level tags are array of inline/block tags depending on compilation source level |
| char[][][] levelTags = new char[3][][]; |
| int[] levelTagsLength = new int[3]; |
| |
| // Completion specific info |
| int cursorLocation; |
| CompletionOnJavadoc completionNode = null; |
| boolean pushText = false; |
| boolean allPossibleTags = false; |
| |
| public CompletionJavadocParser(CompletionParser sourceParser) { |
| super(sourceParser); |
| this.scanner = new CompletionScanner(ClassFileConstants.JDK1_3); |
| this.kind = COMPLETION_PARSER | TEXT_PARSE; |
| initLevelTags(); |
| setSourceComplianceLevel(); |
| } |
| |
| /* |
| * Do not parse comment if completion location is not included. |
| */ |
| @Override |
| public boolean checkDeprecation(int commentPtr) { |
| boolean isDeprecated = false; |
| |
| this.cursorLocation = ((CompletionParser)this.sourceParser).cursorLocation; |
| CompletionScanner completionScanner = (CompletionScanner)this.scanner; |
| completionScanner.cursorLocation = this.cursorLocation; |
| this.javadocStart = this.sourceParser.scanner.commentStarts[commentPtr]; |
| this.javadocEnd = this.sourceParser.scanner.commentStops[commentPtr]; |
| if (this.javadocStart <= this.cursorLocation && this.cursorLocation <= this.javadocEnd) { |
| if (CompletionEngine.DEBUG) { |
| System.out.println("COMPLETION in Javadoc:"); //$NON-NLS-1$ |
| } |
| completionScanner.completionIdentifier = null; |
| super.checkDeprecation(commentPtr); |
| } else { |
| if (this.sourceParser.scanner.commentTagStarts[commentPtr] != 0) { |
| boolean previousValue = this.checkDocComment; |
| this.checkDocComment = false; |
| isDeprecated = super.checkDeprecation(commentPtr); |
| this.checkDocComment = previousValue; |
| } |
| this.docComment = null; |
| } |
| return isDeprecated; |
| } |
| |
| /* |
| * Replace stored Javadoc node with specific completion one. |
| */ |
| @Override |
| protected boolean commentParse() { |
| this.docComment = new CompletionJavadoc(this.javadocStart, this.javadocEnd); |
| this.firstTagPosition = 1; // bug 429340: completion parser needs to parse the whole comment |
| return super.commentParse(); |
| } |
| |
| /* |
| * Create argument expression. If it includes completion location, create and store completion node. |
| */ |
| @Override |
| protected Object createArgumentReference(char[] name, int dim, boolean isVarargs, Object typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException { |
| // Create argument as we may need it after |
| char[] argName = name==null ? CharOperation.NO_CHAR : name; |
| Expression expression = (Expression) super.createArgumentReference(argName, dim, isVarargs, typeRef, dimPositions, argNamePos); |
| // See if completion location is in argument |
| int refStart = ((TypeReference)typeRef).sourceStart; |
| int refEnd = ((TypeReference)typeRef).sourceEnd; |
| boolean inCompletion = (refStart <= this.cursorLocation && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers |
| || ((refStart == (refEnd+1) && refEnd == this.cursorLocation)); // or it's a completion on empty token |
| if (this.completionNode == null && inCompletion) { |
| JavadocArgumentExpression javadocArgument = (JavadocArgumentExpression) expression; |
| TypeReference expressionType = javadocArgument.argument.type; |
| if (expressionType instanceof JavadocSingleTypeReference) { |
| this.completionNode = new CompletionOnJavadocSingleTypeReference((JavadocSingleTypeReference) expressionType); |
| } else if (expressionType instanceof JavadocQualifiedTypeReference) { |
| this.completionNode = new CompletionOnJavadocQualifiedTypeReference((JavadocQualifiedTypeReference) expressionType); |
| } |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" completion argument="+this.completionNode); //$NON-NLS-1$ |
| } |
| return this.completionNode; |
| } |
| return expression; |
| } |
| |
| /* |
| * Create field reference. If it includes completion location, create and store completion node. |
| */ |
| @Override |
| protected Object createFieldReference(Object receiver) throws InvalidInputException { |
| int refStart = (int) (this.identifierPositionStack[0] >>> 32); |
| int refEnd = (int) this.identifierPositionStack[0]; |
| boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers |
| || ((refStart == (refEnd+1) && refEnd == this.cursorLocation)) // or it's a completion on empty token |
| || (this.memberStart == this.cursorLocation); // or it's a completion just after the member separator with an identifier after the cursor |
| if (inCompletion) { |
| JavadocFieldReference fieldRef = (JavadocFieldReference) super.createFieldReference(receiver); |
| char[] name = this.sourceParser.compilationUnit.getMainTypeName(); |
| TypeDeclaration typeDecl = getParsedTypeDeclaration(); |
| if (typeDecl != null) { |
| name = typeDecl.name; |
| } |
| this.completionNode = new CompletionOnJavadocFieldReference(fieldRef, this.memberStart, name); |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" completion field="+this.completionNode); //$NON-NLS-1$ |
| } |
| return this.completionNode; |
| } |
| return super.createFieldReference(receiver); |
| } |
| |
| /* |
| * Verify if method identifier positions include completion location. |
| * If so, create method reference and store it. |
| * Otherwise return null as we do not need this reference. |
| */ |
| @Override |
| protected Object createMethodReference(Object receiver, List arguments) throws InvalidInputException { |
| int memberPtr = this.identifierLengthStack[0] - 1; // may be > 0 for inner class constructor reference |
| int refStart = (int) (this.identifierPositionStack[memberPtr] >>> 32); |
| int refEnd = (int) this.identifierPositionStack[memberPtr]; |
| boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers |
| || ((refStart == (refEnd+1) && refEnd == this.cursorLocation)) // or it's a completion on empty token |
| || (this.memberStart == this.cursorLocation); // or it's a completion just after the member separator with an identifier after the cursor |
| if (inCompletion) { |
| ASTNode node = (ASTNode) super.createMethodReference(receiver, arguments); |
| if (node instanceof JavadocMessageSend) { |
| JavadocMessageSend messageSend = (JavadocMessageSend) node; |
| int nameStart = (int) (messageSend.nameSourcePosition >>> 32); |
| int nameEnd = (int) messageSend.nameSourcePosition; |
| if ((nameStart <= (this.cursorLocation+1) && this.cursorLocation <= nameEnd)) { |
| this.completionNode = new CompletionOnJavadocFieldReference(messageSend, this.memberStart); |
| } else { |
| this.completionNode = new CompletionOnJavadocMessageSend(messageSend, this.memberStart); |
| } |
| } else if (node instanceof JavadocAllocationExpression) { |
| this.completionNode = new CompletionOnJavadocAllocationExpression((JavadocAllocationExpression)node, this.memberStart); |
| } |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" completion method="+this.completionNode); //$NON-NLS-1$ |
| } |
| return this.completionNode; |
| } |
| return super.createMethodReference(receiver, arguments); |
| } |
| |
| /* |
| * Create type reference. If it includes completion location, create and store completion node. |
| */ |
| @Override |
| protected Object createTypeReference(int primitiveToken, boolean canBeModule) { |
| // Need to create type ref in case it was needed by members |
| int nbIdentifiers = this.identifierLengthStack[this.identifierLengthPtr]; |
| int startPtr = this.identifierPtr - (nbIdentifiers-1); |
| int refStart = (int) (this.identifierPositionStack[startPtr] >>> 32); |
| int refEnd = (int) this.identifierPositionStack[this.identifierPtr]; |
| boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers |
| || ((refStart == (refEnd+1) && refEnd == this.cursorLocation)); // or it's a completion on empty token |
| if (!inCompletion) { |
| return super.createTypeReference(primitiveToken, canBeModule); |
| } |
| this.identifierLengthPtr--; |
| if (nbIdentifiers == 1) { // Single Type ref |
| this.completionNode = new CompletionOnJavadocSingleTypeReference( |
| this.identifierStack[this.identifierPtr], |
| this.identifierPositionStack[this.identifierPtr], |
| this.tagSourceStart, |
| this.tagSourceEnd); |
| } else if (nbIdentifiers > 1) { // Qualified Type ref |
| for (int i=startPtr; i<this.identifierPtr; i++) { |
| int start = (int) (this.identifierPositionStack[i] >>> 32); |
| int end = (int) this.identifierPositionStack[i]; |
| if (start <= this.cursorLocation && this.cursorLocation <= end) { |
| if (i == startPtr) { |
| this.completionNode = new CompletionOnJavadocSingleTypeReference( |
| this.identifierStack[startPtr], |
| this.identifierPositionStack[startPtr], |
| this.tagSourceStart, |
| this.tagSourceEnd); |
| } else { |
| char[][] tokens = new char[i][]; |
| System.arraycopy(this.identifierStack, startPtr, tokens, 0, i); |
| long[] positions = new long[i+1]; |
| System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, i+1); |
| this.completionNode = new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[i], positions, this.tagSourceStart, this.tagSourceEnd); |
| } |
| break; |
| } |
| } |
| if (this.completionNode == null) { |
| char[][] tokens = new char[nbIdentifiers-1][]; |
| System.arraycopy(this.identifierStack, startPtr, tokens, 0, nbIdentifiers-1); |
| long[] positions = new long[nbIdentifiers]; |
| System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, nbIdentifiers); |
| this.completionNode = new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[this.identifierPtr], positions, this.tagSourceStart, this.tagSourceEnd); |
| } |
| } |
| |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" completion partial qualified type="+this.completionNode); //$NON-NLS-1$ |
| } |
| return this.completionNode; |
| } |
| |
| @Override |
| protected Object createModuleTypeReference(int primitiveToken, int moduleRefTokenCount) { |
| |
| // Need to create type ref in case it was needed by members |
| int nbIdentifiers = this.identifierLengthStack[this.identifierLengthPtr]; |
| int startPtr = this.identifierPtr - (nbIdentifiers-1); |
| int refStart = (int) (this.identifierPositionStack[startPtr] >>> 32); |
| int refEnd = (int) this.identifierPositionStack[this.identifierPtr]; |
| boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers |
| || ((refStart == (refEnd+1) && refEnd == this.cursorLocation)); // or it's a completion on empty token |
| if (!inCompletion) { |
| return super.createModuleTypeReference(primitiveToken, moduleRefTokenCount); |
| } |
| |
| JavadocModuleReference moduleRef= createModuleReference(moduleRefTokenCount); |
| |
| TypeReference typeRef = null; |
| int size = this.identifierLengthStack[this.identifierLengthPtr]; |
| int newSize= size-moduleRefTokenCount; |
| if (newSize == 1) { // Single Type ref |
| typeRef = new CompletionOnJavadocSingleTypeReference( |
| this.identifierStack[moduleRefTokenCount], |
| this.identifierPositionStack[moduleRefTokenCount], |
| this.tagSourceStart, |
| this.tagSourceEnd); |
| } else if (newSize > 1) { // Qualified Type ref |
| for (int i=moduleRefTokenCount+1; i<this.identifierPtr; i++) { |
| int start = (int) (this.identifierPositionStack[i] >>> 32); |
| int end = (int) this.identifierPositionStack[i]; |
| if (start <= this.cursorLocation && this.cursorLocation <= end) { |
| if (i == moduleRefTokenCount) { |
| this.completionNode = new CompletionOnJavadocSingleTypeReference( |
| this.identifierStack[moduleRefTokenCount+1], |
| this.identifierPositionStack[moduleRefTokenCount+1], |
| this.tagSourceStart, |
| this.tagSourceEnd); |
| } else { |
| char[][] tokens = new char[i][]; |
| System.arraycopy(this.identifierStack, moduleRefTokenCount, tokens, 0, i); |
| long[] positions = new long[i+1]; |
| System.arraycopy(this.identifierPositionStack, moduleRefTokenCount, positions, 0, i+1); |
| typeRef = new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[i], positions, this.tagSourceStart, this.tagSourceEnd); |
| } |
| break; |
| } |
| } |
| if (this.completionNode == null) { |
| char[][] tokens = new char[newSize-1][]; |
| System.arraycopy(this.identifierStack, moduleRefTokenCount, tokens, 0, newSize-1); |
| long[] positions = new long[newSize]; |
| System.arraycopy(this.identifierPositionStack, moduleRefTokenCount, positions, 0, newSize); |
| typeRef = new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[this.identifierPtr], positions, this.tagSourceStart, this.tagSourceEnd); |
| } |
| |
| } |
| |
| moduleRef.setTypeReference(typeRef); |
| this.completionNode = (CompletionOnJavadocModuleReference)moduleRef; |
| return moduleRef; |
| } |
| |
| @Override |
| protected JavadocModuleReference createModuleReference(int moduleRefTokenCount) { |
| JavadocModuleReference moduleRef = null; |
| char[][] tokens = new char[moduleRefTokenCount][]; |
| System.arraycopy(this.identifierStack, 0, tokens, 0, moduleRefTokenCount); |
| long[] positions = new long[moduleRefTokenCount+1]; |
| System.arraycopy(this.identifierPositionStack, 0, positions, 0, moduleRefTokenCount+1); |
| moduleRef = new CompletionOnJavadocModuleReference(tokens, this.identifierStack[this.identifierPtr], positions, this.tagSourceStart, this.tagSourceEnd); |
| return moduleRef; |
| } |
| |
| /* |
| * Get possible tags for a given prefix. |
| */ |
| private char[][][] possibleTags(char[] prefix, boolean newLine, boolean inSnippet) { |
| char[][][] possibleTags = new char[3][][]; |
| if (newLine && inSnippet == false) { |
| System.arraycopy(this.levelTags[BLOCK_IDX], 0, possibleTags[BLOCK_IDX] = new char[this.levelTagsLength[BLOCK_IDX]][], 0, this.levelTagsLength[BLOCK_IDX]); |
| } else { |
| possibleTags[BLOCK_IDX] = CharOperation.NO_CHAR_CHAR; |
| } |
| if(inSnippet==false) { |
| System.arraycopy(this.levelTags[INLINE_IDX], 0, possibleTags[INLINE_IDX] = new char[this.levelTagsLength[INLINE_IDX]][], 0, this.levelTagsLength[INLINE_IDX]); |
| } |
| else { |
| possibleTags[INLINE_IDX] = CharOperation.NO_CHAR_CHAR; |
| } |
| |
| if(inSnippet) { |
| System.arraycopy(this.levelTags[SNIPPET_IDX], 0, possibleTags[SNIPPET_IDX] = new char[this.levelTagsLength[SNIPPET_IDX]][], 0, this.levelTagsLength[SNIPPET_IDX]); |
| |
| } |
| else { |
| possibleTags[SNIPPET_IDX] = CharOperation.NO_CHAR_CHAR; |
| } |
| if (prefix == null || prefix.length == 0) return possibleTags; |
| int kinds = this.levelTags.length; |
| for (int k=0; k<kinds; k++) { |
| int length = possibleTags[k].length, size = 0; |
| int indexes[] = new int[length]; |
| for (int i=0; i<length; i++) { |
| if (CharOperation.prefixEquals(prefix, possibleTags[k][i], false)) { |
| indexes[size++] = i; |
| } |
| } |
| char[][] tags = new char[size][]; |
| for (int i=0; i<size; i++) { |
| tags[i] = possibleTags[k][indexes[i]]; |
| } |
| possibleTags[k] = tags; |
| } |
| return possibleTags; |
| } |
| |
| private CompletionJavadoc getCompletionJavadoc() { |
| return (CompletionJavadoc)this.docComment; |
| } |
| |
| private CompletionParser getCompletionParser() { |
| return (CompletionParser)this.sourceParser; |
| } |
| |
| /* |
| * Init tags arrays for current source level. |
| */ |
| private void initLevelTags() { |
| int level = ((int)(this.complianceLevel >>> 16)) - ClassFileConstants.MAJOR_VERSION_1_1 + 1; |
| if ( level >= BLOCK_TAGS_LENGTH) |
| return; // To support future JDKs |
| // Init block tags |
| this.levelTags[BLOCK_IDX] = new char[BLOCK_ALL_TAGS_LENGTH][]; |
| this.levelTagsLength[BLOCK_IDX] = 0; |
| for (int i=0; i<=level; i++) { |
| int length = BLOCK_TAGS[i].length; |
| System.arraycopy(BLOCK_TAGS[i], 0, this.levelTags[BLOCK_IDX], this.levelTagsLength[BLOCK_IDX], length); |
| this.levelTagsLength[BLOCK_IDX] += length; |
| } |
| if (this.levelTagsLength[BLOCK_IDX] < BLOCK_ALL_TAGS_LENGTH) { |
| System.arraycopy(this.levelTags[BLOCK_IDX], 0, this.levelTags[BLOCK_IDX] = new char[this.levelTagsLength[BLOCK_IDX]][], 0, this.levelTagsLength[BLOCK_IDX]); |
| } |
| // Init inline tags |
| this.levelTags[INLINE_IDX] = new char[INLINE_ALL_TAGS_LENGTH][]; |
| this.levelTagsLength[INLINE_IDX]= 0; |
| for (int i=0; i<=level; i++) { |
| int length = INLINE_TAGS[i].length; |
| System.arraycopy(INLINE_TAGS[i], 0, this.levelTags[INLINE_IDX], this.levelTagsLength[INLINE_IDX], length); |
| this.levelTagsLength[INLINE_IDX] += length; |
| } |
| if (this.levelTagsLength[INLINE_IDX] < INLINE_ALL_TAGS_LENGTH) { |
| System.arraycopy(this.levelTags[INLINE_IDX], 0, this.levelTags[INLINE_IDX] = new char[this.levelTagsLength[INLINE_IDX]][], 0, this.levelTagsLength[INLINE_IDX]); |
| } |
| // Init inline tags |
| this.levelTags[SNIPPET_IDX] = new char[SNIPPET_ALL_TAGS_LENGTH][]; |
| this.levelTagsLength[SNIPPET_IDX]= 0; |
| for (int i=0; i< 1; i++) {// since only in java 18 |
| int length = IN_SNIPPET_TAGS[i].length; |
| System.arraycopy(IN_SNIPPET_TAGS[i], 0, this.levelTags[SNIPPET_IDX], this.levelTagsLength[SNIPPET_IDX], length); |
| this.levelTagsLength[SNIPPET_IDX] += length; |
| } |
| if (this.levelTagsLength[SNIPPET_IDX] < INLINE_ALL_TAGS_LENGTH) { |
| System.arraycopy(this.levelTags[SNIPPET_IDX], 0, this.levelTags[SNIPPET_IDX] = new char[this.levelTagsLength[SNIPPET_IDX]][], 0, this.levelTagsLength[SNIPPET_IDX]); |
| } |
| } |
| @Override |
| protected Object parseArguments(Object receiver) throws InvalidInputException { |
| return parseArguments(receiver, true); |
| } |
| /* |
| * Parse argument in @see tag method reference |
| */ |
| @Override |
| protected Object parseArguments(Object receiver, boolean verifySpaceOrEndComment) throws InvalidInputException { |
| |
| if (this.tagSourceStart>this.cursorLocation) { |
| return super.parseArguments(receiver, verifySpaceOrEndComment); |
| } |
| |
| // Init |
| int modulo = 0; // should be 2 for (Type,Type,...) or 3 for (Type arg,Type arg,...) |
| int iToken = 0; |
| char[] argName = null; |
| List arguments = new ArrayList(10); |
| Object typeRef = null; |
| int dim = 0; |
| boolean isVarargs = false; |
| long[] dimPositions = new long[20]; // assume that there won't be more than 20 dimensions... |
| char[] name = null; |
| long argNamePos = -1; |
| boolean tokenWhiteSpace = this.scanner.tokenizeWhiteSpace; |
| this.scanner.tokenizeWhiteSpace = false; |
| |
| try { |
| // Parse arguments declaration if method reference |
| nextArg : while (this.index < this.scanner.eofPosition) { |
| |
| // Read argument type reference |
| try { |
| typeRef = parseQualifiedName(false); |
| if (this.abort) return null; // May be aborted by specialized parser |
| } catch (InvalidInputException e) { |
| break nextArg; |
| } |
| boolean firstArg = modulo == 0; |
| if (firstArg) { // verify position |
| if (iToken != 0) |
| break nextArg; |
| } else if ((iToken % modulo) != 0) { |
| break nextArg; |
| } |
| if (typeRef == null) { |
| if (firstArg && getCurrentTokenType() == TerminalTokens.TokenNameRPAREN) { |
| this.lineStarted = true; |
| return createMethodReference(receiver, null); |
| } |
| Object methodRef = createMethodReference(receiver, arguments); |
| return syntaxRecoverEmptyArgumentType(methodRef); |
| } |
| if (this.index >= this.scanner.eofPosition) { |
| int argumentStart = ((ASTNode)typeRef).sourceStart; |
| Object argument = createArgumentReference(this.scanner.getCurrentIdentifierSource(), 0, false, typeRef, null, (((long)argumentStart)<<32)+this.tokenPreviousPosition-1); |
| return syntaxRecoverArgumentType(receiver, arguments, argument); |
| } |
| if (this.index >= this.cursorLocation) { |
| if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { |
| CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode; |
| if (singleTypeReference.token == null || singleTypeReference.token.length == 0) { |
| Object methodRef = createMethodReference(receiver, arguments); |
| return syntaxRecoverEmptyArgumentType(methodRef); |
| } |
| } |
| if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) { |
| CompletionOnJavadocQualifiedTypeReference qualifiedTypeReference = (CompletionOnJavadocQualifiedTypeReference) this.completionNode; |
| if (qualifiedTypeReference.tokens == null || qualifiedTypeReference.tokens.length < qualifiedTypeReference.sourcePositions.length) { |
| Object methodRef = createMethodReference(receiver, arguments); |
| return syntaxRecoverEmptyArgumentType(methodRef); |
| } |
| } |
| } |
| iToken++; |
| |
| // Read possible additional type info |
| dim = 0; |
| isVarargs = false; |
| if (readToken() == TerminalTokens.TokenNameLBRACKET) { |
| // array declaration |
| int dimStart = this.scanner.getCurrentTokenStartPosition(); |
| while (readToken() == TerminalTokens.TokenNameLBRACKET) { |
| consumeToken(); |
| if (readToken() != TerminalTokens.TokenNameRBRACKET) { |
| break nextArg; |
| } |
| consumeToken(); |
| dimPositions[dim++] = (((long) dimStart) << 32) + this.scanner.getCurrentTokenEndPosition(); |
| } |
| } else if (readToken() == TerminalTokens.TokenNameELLIPSIS) { |
| // ellipsis declaration |
| int dimStart = this.scanner.getCurrentTokenStartPosition(); |
| dimPositions[dim++] = (((long) dimStart) << 32) + this.scanner.getCurrentTokenEndPosition(); |
| consumeToken(); |
| isVarargs = true; |
| } |
| |
| // Read argument name |
| argNamePos = -1; |
| if (readToken() == TerminalTokens.TokenNameIdentifier) { |
| consumeToken(); |
| if (firstArg) { // verify position |
| if (iToken != 1) |
| break nextArg; |
| } else if ((iToken % modulo) != 1) { |
| break nextArg; |
| } |
| if (argName == null) { // verify that all arguments name are declared |
| if (!firstArg) { |
| break nextArg; |
| } |
| } |
| argName = this.scanner.getCurrentIdentifierSource(); |
| argNamePos = (((long)this.scanner.getCurrentTokenStartPosition())<<32)+this.scanner.getCurrentTokenEndPosition(); |
| iToken++; |
| } else if (argName != null) { // verify that no argument name is declared |
| break nextArg; |
| } |
| |
| // Verify token position |
| if (firstArg) { |
| modulo = iToken + 1; |
| } else { |
| if ((iToken % modulo) != (modulo - 1)) { |
| break nextArg; |
| } |
| } |
| |
| // Read separator or end arguments declaration |
| int token = readToken(); |
| name = argName == null ? CharOperation.NO_CHAR : argName; |
| if (token == TerminalTokens.TokenNameCOMMA) { |
| // Create new argument |
| Object argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos); |
| if (this.abort) return null; // May be aborted by specialized parser |
| arguments.add(argument); |
| consumeToken(); |
| iToken++; |
| } else if (token == TerminalTokens.TokenNameRPAREN) { |
| // Create new argument |
| Object argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos); |
| if (this.abort) return null; // May be aborted by specialized parser |
| arguments.add(argument); |
| consumeToken(); |
| return createMethodReference(receiver, arguments); |
| } else { |
| Object argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos); |
| return syntaxRecoverArgumentType(receiver, arguments, argument); |
| } |
| } |
| |
| // Something wrong happened => Invalid input |
| throw new InvalidInputException(); |
| } finally { |
| // we have to make sure that this is reset to the previous value even if an exception occurs |
| this.scanner.tokenizeWhiteSpace = tokenWhiteSpace; |
| } |
| } |
| |
| @Override |
| protected boolean parseParam() throws InvalidInputException { |
| int startPosition = this.index; |
| int endPosition = this.index; |
| long namePosition = (((long)startPosition)<<32) + endPosition; |
| this.identifierPtr = -1; |
| boolean valid = super.parseParam(); |
| if (this.identifierPtr > 2) return valid; |
| // See if expression is concerned by completion |
| char[] name = null; |
| CompletionScanner completionScanner = (CompletionScanner) this.scanner; |
| boolean isTypeParam = false; |
| if (this.identifierPtr >= 0) { |
| char[] identifier = null; |
| switch (this.identifierPtr) { |
| case 2: |
| if (!valid && completionScanner.completionIdentifier != null && completionScanner.completionIdentifier.length == 0) { |
| valid = pushParamName(true); |
| } |
| // $FALL-THROUGH$ - fall through next case to verify and get identifiers stack contents |
| case 1: |
| isTypeParam = this.identifierStack[0][0] == '<'; |
| identifier = this.identifierStack[1]; |
| namePosition = this.identifierPositionStack[1]; |
| break; |
| case 0: |
| identifier = this.identifierStack[0]; |
| namePosition = this.identifierPositionStack[0]; |
| isTypeParam = identifier.length > 0 && identifier[0] == '<'; |
| break; |
| } |
| if (identifier != null && identifier.length > 0 && ScannerHelper.isJavaIdentifierPart(this.complianceLevel, identifier[0])) { |
| name = identifier; |
| } |
| startPosition = (int)(this.identifierPositionStack[0]>>32); |
| endPosition = (int)this.identifierPositionStack[this.identifierPtr]; |
| } |
| boolean inCompletion = (startPosition <= (this.cursorLocation+1) && this.cursorLocation <= endPosition) // completion cursor is between first and last stacked identifiers |
| || ((startPosition == (endPosition+1) && endPosition == this.cursorLocation)); // or it's a completion on empty token |
| if (inCompletion) { |
| if (this.completionNode == null) { |
| if (isTypeParam) { |
| this.completionNode = new CompletionOnJavadocTypeParamReference(name, namePosition, startPosition, endPosition); |
| } else { |
| this.completionNode = new CompletionOnJavadocParamNameReference(name, namePosition, startPosition, endPosition); |
| } |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" completion param="+this.completionNode); //$NON-NLS-1$ |
| } |
| } else if (this.completionNode instanceof CompletionOnJavadocParamNameReference) { |
| CompletionOnJavadocParamNameReference paramNameRef = (CompletionOnJavadocParamNameReference)this.completionNode; |
| int nameStart = (int) (namePosition>>32); |
| paramNameRef.sourceStart = nameStart; |
| int nameEnd = (int) namePosition; |
| if (nameStart<this.cursorLocation && this.cursorLocation<nameEnd) { |
| paramNameRef.sourceEnd = this.cursorLocation + 1; |
| } else { |
| paramNameRef.sourceEnd = nameEnd; |
| } |
| paramNameRef.tagSourceStart = startPosition; |
| paramNameRef.tagSourceEnd = endPosition; |
| } else if (this.completionNode instanceof CompletionOnJavadocTypeParamReference) { |
| CompletionOnJavadocTypeParamReference typeParamRef = (CompletionOnJavadocTypeParamReference)this.completionNode; |
| int nameStart = (int) (namePosition>>32); |
| typeParamRef.sourceStart = nameStart; |
| int nameEnd = (int) namePosition; |
| if (nameStart<this.cursorLocation && this.cursorLocation<nameEnd) { |
| typeParamRef.sourceEnd = this.cursorLocation + 1; |
| } else { |
| typeParamRef.sourceEnd = nameEnd; |
| } |
| typeParamRef.tagSourceStart = startPosition; |
| typeParamRef.tagSourceEnd = endPosition; |
| } |
| } |
| return valid; |
| } |
| |
| @Override |
| protected boolean parseReference() throws InvalidInputException { |
| return parseReference(false); |
| } |
| |
| @Override |
| protected boolean parseReference(boolean allowModule) throws InvalidInputException { |
| boolean completed = this.completionNode != null; |
| boolean valid = super.parseReference(allowModule); |
| if (!completed && this.completionNode != null) { |
| this.completionNode.addCompletionFlags(CompletionOnJavadoc.FORMAL_REFERENCE); |
| } |
| return valid; |
| } |
| |
| @Override |
| protected boolean parseTag(int previousPosition) throws InvalidInputException { |
| int startPosition = this.inlineTagStarted ? this.inlineTagStart : previousPosition; |
| boolean newLine = !this.lineStarted; |
| boolean valid = false; |
| try { |
| valid = super.parseTag(previousPosition); |
| } |
| catch (InvalidCursorLocation e) { |
| // catch exception and get a javadoc tag in snippet if possible |
| } |
| boolean inCompletion = (this.tagSourceStart <= (this.cursorLocation+1) && this.cursorLocation <= this.tagSourceEnd) // completion cursor is between first and last stacked identifiers |
| || ((this.tagSourceStart == (this.tagSourceEnd+1) && this.tagSourceEnd == this.cursorLocation)); // or it's a completion on empty token |
| if (inCompletion) { |
| int end = this.tagSourceEnd; |
| if (this.inlineTagStarted && this.scanner.currentCharacter == '}') { |
| end = this.scanner.currentPosition; |
| } |
| long position = (((long)startPosition)<<32) + end; |
| int length = this.cursorLocation+1-this.tagSourceStart; |
| char[] tag = new char[length]; |
| System.arraycopy(this.source, this.tagSourceStart, tag, 0, length); |
| char[][][] tags = this.parsingSnippet ? possibleTags(tag, false,true) : possibleTags(tag, newLine,false); |
| if (tags != null) { |
| this.completionNode = new CompletionOnJavadocTag(tag, position, startPosition, end, tags, this.allPossibleTags); |
| } |
| } |
| return valid; |
| } |
| |
| @Override |
| protected boolean parseSnippet() throws InvalidInputException { |
| this.parsingSnippet = true; |
| return super.parseSnippet(); |
| } |
| |
| @Override |
| protected boolean lookForTagsInSnippets() { |
| return this.parsingSnippet; |
| } |
| @Override |
| protected boolean parseThrows() { |
| try { |
| Object typeRef = parseQualifiedName(true); |
| if (this.completionNode != null) { |
| this.completionNode.addCompletionFlags(CompletionOnJavadoc.EXCEPTION); |
| } |
| return pushThrowName(typeRef); |
| } catch (InvalidInputException ex) { |
| // ignore |
| } |
| return false; |
| } |
| |
| //{ObjectTeams: @role tag |
| @Override |
| protected boolean pushRoleName(Object typeRef) { |
| if (typeRef == this.completionNode) |
| this.completionNode.addCompletionFlags(CompletionOnJavadoc.ROLE); |
| return super.pushRoleName(typeRef); |
| } |
| // SH} |
| |
| /* |
| * Push param name reference. If it includes completion location, create and store completion node. |
| */ |
| @Override |
| protected boolean pushParamName(boolean isTypeParam) { |
| if (super.pushParamName(isTypeParam)) { |
| Expression expression = (Expression) this.astStack[this.astPtr]; |
| // See if expression is concerned by completion |
| if (expression.sourceStart <= (this.cursorLocation+1) && this.cursorLocation <= expression.sourceEnd) { |
| if (isTypeParam) { |
| this.completionNode = new CompletionOnJavadocTypeParamReference((JavadocSingleTypeReference)expression); |
| } else { |
| this.completionNode = new CompletionOnJavadocParamNameReference((JavadocSingleNameReference)expression); |
| } |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" completion param="+this.completionNode); //$NON-NLS-1$ |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Push text. If it includes completion location, then rescan line to see if there's a possible |
| * reference under the cursor location. |
| * |
| * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#pushText(int, int) |
| */ |
| @Override |
| protected void pushText(int start, int end) { |
| if (start <= this.cursorLocation && this.cursorLocation <= end) { |
| this.scanner.resetTo(start, end); |
| boolean tokenizeWhiteSpace = this.scanner.tokenizeWhiteSpace; |
| this.scanner.tokenizeWhiteSpace = true; |
| try { |
| Object typeRef = null; |
| this.pushText = true; |
| |
| // Get reference tokens |
| int previousToken = TerminalTokens.TokenNameWHITESPACE; |
| while (!this.scanner.atEnd() && this.completionNode == null && !this.abort) { |
| int token = readTokenSafely(); |
| switch (token) { |
| case TerminalTokens.TokenNameStringLiteral : |
| int strStart = 0, strEnd = 0; |
| if ((strStart=this.scanner.getCurrentTokenStartPosition()+1) <= this.cursorLocation && |
| this.cursorLocation <= (strEnd=this.scanner.getCurrentTokenEndPosition()-1)) |
| { |
| this.scanner.resetTo(strStart, strEnd); |
| } |
| consumeToken(); |
| break; |
| case TerminalTokens.TokenNameERROR : |
| consumeToken(); |
| if (this.scanner.currentCharacter == '#') { // @see ...#member |
| Object member = null; |
| try { |
| this.scanner.tokenizeWhiteSpace = false; |
| member = parseMember(typeRef); |
| } catch (InvalidInputException e) { |
| consumeToken(); |
| } |
| this.scanner.tokenizeWhiteSpace = true; |
| if (this.completionNode != null) { |
| int flags = this.inlineTagStarted ? 0 : CompletionOnJavadoc.TEXT|CompletionOnJavadoc.ONLY_INLINE_TAG; |
| if (member instanceof JavadocMessageSend) { |
| JavadocMessageSend msgSend = (JavadocMessageSend) member; |
| this.completionNode = new CompletionOnJavadocMessageSend(msgSend, this.memberStart, flags); |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" new completion method="+this.completionNode); //$NON-NLS-1$ |
| } |
| } else if (member instanceof JavadocAllocationExpression) { |
| JavadocAllocationExpression alloc = (JavadocAllocationExpression) member; |
| this.completionNode = new CompletionOnJavadocAllocationExpression(alloc, this.memberStart, flags); |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" new completion method="+this.completionNode); //$NON-NLS-1$ |
| } |
| } else { |
| this.completionNode.addCompletionFlags(flags); |
| } |
| } |
| } |
| break; |
| case TerminalTokens.TokenNameIdentifier : |
| try { |
| this.scanner.tokenizeWhiteSpace = false; |
| typeRef = parseQualifiedName(true); |
| if (this.completionNode == null) { |
| consumeToken(); |
| this.scanner.resetTo(this.tokenPreviousPosition, end); |
| this.index = this.tokenPreviousPosition; |
| } |
| } |
| catch (InvalidInputException e) { |
| consumeToken(); |
| } |
| finally { |
| this.scanner.tokenizeWhiteSpace = true; |
| } |
| if (previousToken != TerminalTokens.TokenNameWHITESPACE) { |
| typeRef = null; |
| this.completionNode = null; |
| } |
| break; |
| case TerminalTokens.TokenNameAT: |
| consumeToken(); |
| try { |
| this.scanner.tokenizeWhiteSpace = false; |
| int startPosition = this.scanner.getCurrentTokenStartPosition(); |
| parseTag(startPosition); |
| if (this.completionNode != null) { |
| if (this.inlineTagStarted) { |
| /* May be to replace invalid @value tag inside text? |
| if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { |
| CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode; |
| singleTypeReference.tagSourceStart = startPosition; |
| switch (this.tagValue) { |
| case TAG_VALUE_VALUE: |
| // singleTypeReference.completionFlags |= ONLY_INLINE_TAG; |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) singleTypeReference.completionFlags |= REPLACE_TAG; |
| break; |
| } |
| } else if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) { |
| CompletionOnJavadocQualifiedTypeReference qualifiedTypeRef = (CompletionOnJavadocQualifiedTypeReference) this.completionNode; |
| qualifiedTypeRef.tagSourceStart = startPosition; |
| switch (this.tagValue) { |
| case TAG_VALUE_VALUE: |
| singleTypeReference.completionFlags |= ONLY_INLINE_TAG; |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) qualifiedTypeRef.completionFlags |= REPLACE_TAG; |
| break; |
| } |
| } |
| // */ |
| } else { |
| /* May be to replace non-inline tag inside text? |
| if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { |
| CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode; |
| singleTypeReference.tagSourceStart = startPosition; |
| switch (this.tagValue) { |
| case TAG_LINK_VALUE: |
| case TAG_LINKPLAIN_VALUE: |
| singleTypeReference.completionFlags |= ONLY_INLINE_TAG; |
| case TAG_SEE_VALUE: |
| singleTypeReference.completionFlags |= REPLACE_TAG; |
| break; |
| } |
| } else if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) { |
| CompletionOnJavadocQualifiedTypeReference qualifiedTypeRef = (CompletionOnJavadocQualifiedTypeReference) this.completionNode; |
| qualifiedTypeRef.tagSourceStart = startPosition; |
| switch (this.tagValue) { |
| case TAG_LINK_VALUE: |
| case TAG_LINKPLAIN_VALUE: |
| qualifiedTypeRef.completionFlags |= ONLY_INLINE_TAG; |
| case TAG_SEE_VALUE: |
| qualifiedTypeRef.completionFlags |= REPLACE_TAG; |
| break; |
| } |
| } |
| // */ |
| } |
| } |
| } catch (InvalidInputException e) { |
| consumeToken(); |
| } |
| this.scanner.tokenizeWhiteSpace = true; |
| break; |
| default : |
| consumeToken(); |
| typeRef = null; |
| break; |
| } |
| previousToken = token; |
| } |
| } |
| finally { |
| this.scanner.tokenizeWhiteSpace = tokenizeWhiteSpace; |
| this.pushText = false; |
| } |
| |
| // Reset position to avoid missing tokens when new line was encountered |
| this.index = end; |
| this.scanner.currentPosition = end; |
| consumeToken(); |
| |
| if (this.completionNode != null) { |
| if (this.inlineTagStarted) { |
| this.completionNode.addCompletionFlags(CompletionOnJavadoc.FORMAL_REFERENCE); |
| } else { |
| this.completionNode.addCompletionFlags(CompletionOnJavadoc.TEXT); |
| } |
| } |
| } |
| } |
| |
| @Override |
| protected int readToken() throws InvalidInputException { |
| int token = super.readToken(); |
| if (token == TerminalTokens.TokenNameIdentifier && this.scanner.currentPosition == this.scanner.startPosition) { |
| // Scanner is looping on empty token => read it... |
| this.scanner.getCurrentIdentifierSource(); |
| } |
| return token; |
| } |
| |
| /* |
| * Recover syntax on invalid qualified name. |
| */ |
| @Override |
| protected Object syntaxRecoverQualifiedName(int primitiveToken) throws InvalidInputException { |
| if (this.cursorLocation == ((int)this.identifierPositionStack[this.identifierPtr])) { |
| // special case of completion just before the dot. |
| return createTypeReference(primitiveToken); |
| } |
| int idLength = this.identifierLengthStack[this.identifierLengthPtr]; |
| char[][] tokens = new char[idLength][]; |
| int startPtr = this.identifierPtr-idLength+1; |
| System.arraycopy(this.identifierStack, startPtr, tokens, 0, idLength); |
| long[] positions = new long[idLength+1]; |
| System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, idLength); |
| positions[idLength] = (((long)this.tokenPreviousPosition)<<32) + this.tokenPreviousPosition; |
| this.completionNode = new CompletionOnJavadocQualifiedTypeReference(tokens, CharOperation.NO_CHAR, positions, this.tagSourceStart, this.tagSourceEnd); |
| |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" completion partial qualified type="+this.completionNode); //$NON-NLS-1$ |
| } |
| return this.completionNode; |
| } |
| |
| /* |
| * Recover syntax on type argument in invalid method/constructor reference |
| */ |
| protected Object syntaxRecoverArgumentType(Object receiver, List arguments, Object argument) throws InvalidInputException { |
| if (this.completionNode != null && !this.pushText) { |
| this.completionNode.addCompletionFlags(CompletionOnJavadoc.BASE_TYPES); |
| if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { |
| char[] token = ((CompletionOnJavadocSingleTypeReference)this.completionNode).token; |
| if (token != null && token.length > 0) { |
| return this.completionNode; |
| } |
| } else { |
| return this.completionNode; |
| } |
| } |
| // Filter empty token |
| if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) { |
| CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode; |
| if (singleTypeReference.token != null && singleTypeReference.token.length > 0) { |
| arguments.add(argument); |
| } |
| } else if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) { |
| CompletionOnJavadocQualifiedTypeReference qualifiedTypeReference = (CompletionOnJavadocQualifiedTypeReference) this.completionNode; |
| if (qualifiedTypeReference.tokens != null && qualifiedTypeReference.tokens.length == qualifiedTypeReference.sourcePositions.length) { |
| arguments.add(argument); |
| } |
| } else { |
| arguments.add(argument); |
| } |
| Object methodRef = super.createMethodReference(receiver, arguments); |
| if (methodRef instanceof JavadocMessageSend) { |
| JavadocMessageSend msgSend = (JavadocMessageSend) methodRef; |
| if (this.index > this.cursorLocation) { |
| msgSend.sourceEnd = this.tokenPreviousPosition-1; |
| } |
| int nameStart = (int) (msgSend.nameSourcePosition >>> 32); |
| int nameEnd = (int) msgSend.nameSourcePosition; |
| if ((nameStart <= (this.cursorLocation+1) && this.cursorLocation <= nameEnd)) { |
| this.completionNode = new CompletionOnJavadocFieldReference(msgSend, this.memberStart); |
| } else { |
| this.completionNode = new CompletionOnJavadocMessageSend(msgSend, this.memberStart); |
| } |
| } else if (methodRef instanceof JavadocAllocationExpression) { |
| JavadocAllocationExpression allocExp = (JavadocAllocationExpression) methodRef; |
| if (this.index > this.cursorLocation) { |
| allocExp.sourceEnd = this.tokenPreviousPosition-1; |
| } |
| this.completionNode = new CompletionOnJavadocAllocationExpression(allocExp, this.memberStart); |
| } |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" completion method="+this.completionNode); //$NON-NLS-1$ |
| } |
| return this.completionNode; |
| } |
| |
| /* |
| * Recover syntax on empty type argument in invalid method/constructor reference |
| */ |
| protected Object syntaxRecoverEmptyArgumentType(Object methodRef) throws InvalidInputException { |
| if (methodRef instanceof JavadocMessageSend) { |
| JavadocMessageSend msgSend = (JavadocMessageSend) methodRef; |
| if (this.index > this.cursorLocation) { |
| msgSend.sourceEnd = this.tokenPreviousPosition-1; |
| } |
| this.completionNode = new CompletionOnJavadocMessageSend(msgSend, this.memberStart); |
| } else if (methodRef instanceof JavadocAllocationExpression) { |
| JavadocAllocationExpression allocExp = (JavadocAllocationExpression) methodRef; |
| if (this.index > this.cursorLocation) { |
| allocExp.sourceEnd = this.tokenPreviousPosition-1; |
| } |
| this.completionNode = new CompletionOnJavadocAllocationExpression(allocExp, this.memberStart); |
| } |
| if (CompletionEngine.DEBUG) { |
| System.out.println(" completion method="+this.completionNode); //$NON-NLS-1$ |
| } |
| return this.completionNode; |
| } |
| |
| /* |
| * Store completion node into doc comment. |
| */ |
| @Override |
| protected void updateDocComment() { |
| super.updateDocComment(); |
| if (this.completionNode instanceof Expression && ((Expression) this.completionNode).isTrulyExpression()) { |
| getCompletionParser().assistNodeParent = this.docComment; |
| getCompletionParser().assistNode = (ASTNode) this.completionNode; |
| getCompletionJavadoc().completionNode = (Expression) this.completionNode; |
| } |
| } |
| |
| @Override |
| protected boolean verifySpaceOrEndComment() { |
| CompletionScanner completionScanner = (CompletionScanner) this.scanner; |
| if (completionScanner.completionIdentifier != null && completionScanner.completedIdentifierStart <= this.cursorLocation && this.cursorLocation <= completionScanner.completedIdentifierEnd) { |
| // if we're on completion location do not verify end... |
| return true; |
| } |
| return super.verifySpaceOrEndComment(); |
| } |
| |
| } |