1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.codeassist.complete;
14 * Parser able to build specific completion parse nodes, given a cursorLocation.
16 * Cursor location denotes the position of the last character behind which completion
18 * -1 means completion at the very beginning of the source
19 * 0 means completion behind the first character
20 * n means completion behind the n-th character
22 import net.sourceforge.phpdt.internal.compiler.*;
23 import net.sourceforge.phpdt.internal.compiler.env.*;
25 import net.sourceforge.phpdt.internal.compiler.ast.*;
26 import net.sourceforge.phpdt.internal.compiler.parser.*;
27 import net.sourceforge.phpdt.internal.compiler.problem.*;
28 import net.sourceforge.phpdt.internal.codeassist.impl.*;
30 public class CompletionParser extends AssistParser {
34 public int cursorLocation;
35 public char[][] labels; // the visible labels up to the cursor location
36 public AstNode assistNodeParent; // the parent node of assist node
37 /* the following fields are internal flags */
39 boolean betweenNewAndLeftBraket; // whether we are between the keyword 'new' and the following left braket, ie. '[', '(' or '{'
40 boolean betweenCatchAndRightParen; // whether we are between the keyword 'catch' and the following ')'
41 boolean completionBehindDot; // true when completion identifier immediately follows a dot
43 boolean nextTypeReferenceIsClass;
44 boolean nextTypeReferenceIsException;
45 boolean nextTypeReferenceIsInterface;
48 int throwBracketDepth;
50 // the stacks of types and qualifiers for invocations (ie. method invocations, allocation expressions and
51 // explicit constructor invocations). They use the same stack pointer as the selector stack (ie. invocationPtr)
52 // the invocation type stack contains one of the invocation type constants below
53 // the qualifier stack contains pointers to the expression stack or -1 if there is no qualifier
54 // (a qualifier is the expression that qualifies a 'new', a 'super' constructor or a 'this' constructor
55 // or it is the receiver of a message send)
56 int[] invocationTypeStack = new int[StackIncrement];
57 int[] qualifierStack = new int[StackIncrement];
59 // invocation type constants
60 static final int EXPLICIT_RECEIVER = 0;
61 static final int NO_RECEIVER = -1;
62 static final int SUPER_RECEIVER = -2;
63 static final int NAME_RECEIVER = -3;
64 static final int ALLOCATION = -4;
65 static final int QUALIFIED_ALLOCATION = -5;
67 // the type of the current invocation (one of the invocation type constants)
70 // a pointer in the expression stack to the qualifier of a invocation
73 // a stack of label counters
74 // a new counter is pushed on the stack each time when a method (or a constructor) is entered,
75 // it is poped when the method (or constructor) is exited,
76 // it is incremented when a new label is defined
78 int[] labelCounterStack = new int[StackIncrement];
80 // a stack of invocationPtr: contains the first invocationPtr of a block
81 // the current invocationPtr+1 is pushed when a block is entered
82 // it is poped when a block is exited
83 int blockInvocationPtr;
84 int[] blockInvocationStack = new int[StackIncrement];
86 // last modifiers info
87 int lastModifiers = AccDefault;
88 int lastModifiersStart = -1;
90 public CompletionParser(ProblemReporter problemReporter, boolean assertMode) {
91 super(problemReporter, assertMode);
93 public char[] assistIdentifier(){
94 return ((CompletionScanner)scanner).completionIdentifier;
96 protected void attachOrphanCompletionNode(){
97 if (this.isOrphanCompletionNode) {
98 AstNode orphan = this.assistNode;
99 this.isOrphanCompletionNode = false;
101 /* if in context of a type, then persists the identifier into a fake field return type */
102 if (currentElement instanceof RecoveredType){
103 RecoveredType recoveredType = (RecoveredType)currentElement;
104 /* filter out cases where scanner is still inside type header */
105 if (recoveredType.foundOpeningBrace) {
106 /* generate a pseudo field with a completion on type reference */
107 if (orphan instanceof TypeReference){
108 CompletionOnFieldType fieldDeclaration = new CompletionOnFieldType((TypeReference)orphan, false);
110 // retrieve available modifiers if any
111 if (intPtr >= 2 && intStack[intPtr-1] == this.lastModifiersStart && intStack[intPtr-2] == this.lastModifiers){
112 fieldDeclaration.modifiersSourceStart = intStack[intPtr-1];
113 fieldDeclaration.modifiers = intStack[intPtr-2];
116 currentElement = currentElement.add(fieldDeclaration, 0);
121 /* if in context of a method, persists if inside arguments as a type */
122 if (currentElement instanceof RecoveredMethod){
123 RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
124 /* only consider if inside method header */
125 if (!recoveredMethod.foundOpeningBrace) {
126 //if (rParenPos < lParenPos){ // inside arguments
127 if (orphan instanceof TypeReference){
128 currentElement = currentElement.parent.add(
129 new CompletionOnFieldType((TypeReference)orphan, true), 0);
135 // add the completion node to the method declaration or constructor declaration
136 if (orphan instanceof Statement) {
137 /* check for completion at the beginning of method body
138 behind an invalid signature
140 RecoveredMethod method = currentElement.enclosingMethod();
142 AbstractMethodDeclaration methodDecl = method.methodDeclaration;
143 if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
144 && (scanner.getLineNumber(orphan.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
148 // add the completion node as a statement to the list of block statements
149 currentElement = currentElement.add((Statement)orphan, 0);
154 // the following code applies only in methods, constructors or initializers
155 if ((!this.inMethodStack[this.inMethodPtr] && !this.inFieldInitializationStack[this.inFieldInitializationPtr])) {
159 // push top expression on ast stack if it contains the completion node
160 Expression expression;
161 if (this.expressionPtr > -1 && containsCompletionNode(expression = this.expressionStack[this.expressionPtr])) {
162 /* check for completion at the beginning of method body
163 behind an invalid signature
165 RecoveredMethod method = currentElement.enclosingMethod();
167 AbstractMethodDeclaration methodDecl = method.methodDeclaration;
168 if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
169 && (scanner.getLineNumber(expression.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
173 if (expression instanceof AllocationExpression) {
174 // keep the context if it is an allocation expression
175 Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(expression);
176 currentElement = currentElement.add(statement, 0);
178 Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(this.assistNode);
179 currentElement = currentElement.add(statement, 0);
183 public int bodyEnd(AbstractMethodDeclaration method){
184 return cursorLocation;
186 public int bodyEnd(Initializer initializer){
187 return cursorLocation;
190 * Checks if the completion is on the exception type of a catch clause.
191 * Returns whether we found a completion node.
193 private boolean checkCatchClause() {
194 if (this.betweenCatchAndRightParen && this.identifierPtr > -1) {
195 // NB: if the cursor is on the variable, then it has been reduced (so identifierPtr is -1),
196 // thus this can only be a completion on the type of the catch clause
197 this.assistNode = getTypeReference(0);
198 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
199 this.isOrphanCompletionNode = true;
205 * Checks if the completion is on the type following a 'new'.
206 * Returns whether we found a completion node.
208 private boolean checkClassInstanceCreation() {
209 if (this.betweenNewAndLeftBraket) {
210 // completion on type inside an allocation expression
212 if(this.throwBracketDepth != -1 && this.throwBracketDepth == this.bracketDepth) {
213 this.nextTypeReferenceIsException = true;
215 TypeReference type = getTypeReference(0);
216 this.nextTypeReferenceIsException = false;
217 this.assistNode = type;
218 this.lastCheckPoint = type.sourceEnd + 1;
219 if (this.invocationType == ALLOCATION) {
220 // non qualified allocation expression
221 AllocationExpression allocExpr = new AllocationExpression();
222 allocExpr.type = type;
223 allocExpr.sourceStart = type.sourceStart;
224 allocExpr.sourceEnd = type.sourceEnd;
225 pushOnExpressionStack(allocExpr);
226 this.isOrphanCompletionNode = false;
228 // qualified allocation expression
229 QualifiedAllocationExpression allocExpr = new QualifiedAllocationExpression();
230 allocExpr.type = type;
231 allocExpr.enclosingInstance = this.expressionStack[this.qualifier];
232 allocExpr.sourceStart = this.intStack[this.intPtr--];
233 allocExpr.sourceEnd = type.sourceEnd;
234 this.expressionStack[this.qualifier] = allocExpr; // attach it now (it replaces the qualifier expression)
235 this.isOrphanCompletionNode = false;
242 * Checks if the completion is on the dot following an array type,
243 * a primitive type or an primitive array type.
244 * Returns whether we found a completion node.
246 private boolean checkClassLiteralAccess() {
247 if (this.identifierLengthPtr >= 1 && this.previousToken == TokenNameDOT) { // (NB: the top id length is 1 and it is for the completion identifier)
249 // if the penultimate id length is negative,
250 // the completion is after a primitive type or a primitive array type
251 if ((length = this.identifierLengthStack[this.identifierLengthPtr-1]) < 0) {
252 // build the primitive type node
253 int dim = this.isAfterArrayType() ? this.intStack[this.intPtr--] : 0;
254 SingleTypeReference typeRef = (SingleTypeReference)TypeReference.baseTypeReference(-length, dim);
255 typeRef.sourceStart = this.intStack[this.intPtr--];
257 typeRef.sourceEnd = this.intStack[this.intPtr--];
260 typeRef.sourceEnd = this.endPosition;
262 //typeRef.sourceEnd = typeRef.sourceStart + typeRef.token.length; // NB: It's ok to use the length of the token since it doesn't contain any unicode
264 // find the completion identifier and its source positions
265 char[] source = identifierStack[identifierPtr];
266 long pos = this.identifierPositionStack[this.identifierPtr--];
267 this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
269 // build the completion on class literal access node
270 CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
271 access.completionIdentifier = source;
272 this.identifierLengthPtr--; // pop the length that was used to say it is a primitive type
273 this.assistNode = access;
274 this.isOrphanCompletionNode = true;
278 // if the completion is after a regular array type
279 if (isAfterArrayType()) {
280 // find the completion identifier and its source positions
281 char[] source = identifierStack[identifierPtr];
282 long pos = this.identifierPositionStack[this.identifierPtr--];
283 this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
285 // get the type reference
286 TypeReference typeRef = getTypeReference(this.intPtr--);
288 // build the completion on class literal access node
289 CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
290 access.completionIdentifier = source;
291 this.assistNode = access;
292 this.isOrphanCompletionNode = true;
300 * Checks if the completion is inside a method invocation or a constructor invocation.
301 * Returns whether we found a completion node.
303 private boolean checkInvocation() {
304 Expression topExpression = this.expressionPtr >= 0 ?
305 this.expressionStack[this.expressionPtr] :
307 boolean isEmptyNameCompletion = false;
308 boolean isEmptyAssistIdentifier = false;
309 int startInvocationPtr = this.blockInvocationPtr >= 0 ? this.blockInvocationStack[this.blockInvocationPtr] : 0;
310 if (this.invocationPtr >= startInvocationPtr
311 && ((isEmptyNameCompletion = topExpression == this.assistNode && this.isEmptyNameCompletion()) // eg. it is something like "this.fred([cursor]" but it is not something like "this.fred(1 + [cursor]"
312 || (isEmptyAssistIdentifier = this.indexOfAssistIdentifier() >= 0 && this.identifierStack[this.identifierPtr].length == 0))) { // eg. it is something like "this.fred(1 [cursor]"
314 // pop empty name completion
315 if (isEmptyNameCompletion) {
316 this.expressionPtr--;
317 this.expressionLengthStack[this.expressionLengthPtr]--;
318 } else if (isEmptyAssistIdentifier) {
319 this.identifierPtr--;
320 this.identifierLengthPtr--;
323 // find receiver and qualifier
324 int invocationType = this.invocationTypeStack[this.invocationPtr];
325 int qualifierExprPtr = this.qualifierStack[this.invocationPtr];
328 int numArgs = this.expressionPtr - qualifierExprPtr;
329 int argStart = qualifierExprPtr + 1;
330 Expression[] arguments = null;
332 // remember the arguments
333 arguments = new Expression[numArgs];
334 System.arraycopy(this.expressionStack, argStart, arguments, 0, numArgs);
336 // consume the expression arguments
337 this.expressionPtr -= numArgs;
340 count -= this.expressionLengthStack[this.expressionLengthPtr--];
345 if (invocationType != ALLOCATION && invocationType != QUALIFIED_ALLOCATION) {
346 // creates completion on message send
347 CompletionOnMessageSend messageSend = new CompletionOnMessageSend();
348 messageSend.arguments = arguments;
349 switch (invocationType) {
352 messageSend.receiver = ThisReference.ThisImplicit;
355 // remove special flags for primitive types
356 while (this.identifierLengthPtr >= 0 && this.identifierLengthStack[this.identifierLengthPtr] < 0) {
357 this.identifierLengthPtr--;
361 this.identifierPtr--;
362 this.identifierLengthStack[this.identifierLengthPtr]--;
363 // consume the receiver
364 messageSend.receiver = this.getUnspecifiedReference();
367 messageSend.receiver = SuperReference.Super;
369 case EXPLICIT_RECEIVER:
370 messageSend.receiver = this.expressionStack[qualifierExprPtr];
374 int selectorPtr = this.selectorStack[this.invocationPtr];
375 messageSend.selector = this.identifierStack[selectorPtr];
377 if (this.identifierLengthPtr >=0 && this.identifierLengthStack[this.identifierLengthPtr] == 1) {
378 this.identifierPtr--;
379 this.identifierLengthPtr--;
382 // the entire message may be replaced in case qualification is needed
383 messageSend.sourceStart = (int)(this.identifierPositionStack[selectorPtr] >> 32); //this.cursorLocation + 1;
384 messageSend.sourceEnd = this.cursorLocation;
386 // remember the message send as an orphan completion node
387 this.assistNode = messageSend;
388 this.lastCheckPoint = messageSend.sourceEnd + 1;
389 this.isOrphanCompletionNode = true;
392 int selectorPtr = this.selectorStack[this.invocationPtr];
393 if (selectorPtr == THIS_CONSTRUCTOR || selectorPtr == SUPER_CONSTRUCTOR) {
394 // creates an explicit constructor call
395 CompletionOnExplicitConstructorCall call = new CompletionOnExplicitConstructorCall(
396 (selectorPtr == THIS_CONSTRUCTOR) ? ExplicitConstructorCall.This : ExplicitConstructorCall.Super);
397 call.arguments = arguments;
398 if (invocationType == QUALIFIED_ALLOCATION) {
399 call.qualification = this.expressionStack[qualifierExprPtr];
402 // no source is going to be replaced
403 call.sourceStart = this.cursorLocation + 1;
404 call.sourceEnd = this.cursorLocation;
406 // remember the explicit constructor call as an orphan completion node
407 this.assistNode = call;
408 this.lastCheckPoint = call.sourceEnd + 1;
409 this.isOrphanCompletionNode = true;
412 // creates an allocation expression
413 CompletionOnQualifiedAllocationExpression allocExpr = new CompletionOnQualifiedAllocationExpression();
414 allocExpr.arguments = arguments;
415 allocExpr.type = super.getTypeReference(0); // we don't want a completion node here, so call super
416 if (invocationType == QUALIFIED_ALLOCATION) {
417 allocExpr.enclosingInstance = this.expressionStack[qualifierExprPtr];
419 // no source is going to be replaced
420 allocExpr.sourceStart = this.cursorLocation + 1;
421 allocExpr.sourceEnd = this.cursorLocation;
423 // remember the allocation expression as an orphan completion node
424 this.assistNode = allocExpr;
425 this.lastCheckPoint = allocExpr.sourceEnd + 1;
426 this.isOrphanCompletionNode = true;
434 * Checks if the completion is on a member access (ie. in an identifier following a dot).
435 * Returns whether we found a completion node.
437 private boolean checkMemberAccess() {
438 if (this.previousToken == TokenNameDOT && this.qualifier > -1 && this.expressionPtr == this.qualifier) {
439 // the receiver is an expression
440 pushCompletionOnMemberAccessOnExpressionStack(false);
446 * Checks if the completion is on a name reference.
447 * Returns whether we found a completion node.
449 private boolean checkNameCompletion() {
451 We didn't find any other completion, but the completion identifier is on the identifier stack,
452 so it can only be a completion on name.
453 Note that we allow the completion on a name even if nothing is expected (eg. foo() b[cursor] would
454 be a completion on 'b'). This policy gives more to the user than he/she would expect, but this
455 simplifies the problem. To fix this, the recovery must be changed to work at a 'statement' granularity
456 instead of at the 'expression' granularity as it does right now.
459 // NB: at this point the completion identifier is on the identifier stack
460 this.assistNode = getUnspecifiedReferenceOptimized();
461 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
462 this.isOrphanCompletionNode = true;
466 * Checks if the completion is in the context of a method and on the type of one of its arguments
467 * Returns whether we found a completion node.
469 private boolean checkRecoveredMethod() {
470 if (currentElement instanceof RecoveredMethod){
471 /* check if current awaiting identifier is the completion identifier */
472 if (this.indexOfAssistIdentifier() < 0) return false;
474 /* check if on line with an error already - to avoid completing inside
475 illegal type names e.g. int[<cursor> */
476 if (lastErrorEndPosition <= cursorLocation+1
477 && scanner.getLineNumber(lastErrorEndPosition)
478 == scanner.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart)){
481 RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
482 /* only consider if inside method header */
483 if (!recoveredMethod.foundOpeningBrace
484 && lastIgnoredToken == -1) {
485 //if (rParenPos < lParenPos){ // inside arguments
486 this.assistNode = this.getTypeReference(0);
487 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
488 this.isOrphanCompletionNode = true;
495 * Checks if the completion is in the context of a type and on a type reference in this type.
496 * Persists the identifier into a fake field return type
497 * Returns whether we found a completion node.
499 private boolean checkRecoveredType() {
500 if (currentElement instanceof RecoveredType){
501 /* check if current awaiting identifier is the completion identifier */
502 if (this.indexOfAssistIdentifier() < 0) return false;
504 /* check if on line with an error already - to avoid completing inside
505 illegal type names e.g. int[<cursor> */
506 if ((lastErrorEndPosition <= cursorLocation+1)
507 && scanner.getLineNumber(lastErrorEndPosition)
508 == scanner.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart)){
511 RecoveredType recoveredType = (RecoveredType)currentElement;
512 /* filter out cases where scanner is still inside type header */
513 if (recoveredType.foundOpeningBrace) {
514 this.assistNode = this.getTypeReference(0);
515 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
516 this.isOrphanCompletionNode = true;
523 * Check whether about to shift beyond the completion token.
524 * If so, depending on the context, a special node might need to be created
525 * and attached to the existing recovered structure so as to be remember in the
526 * resulting parsed structure.
528 public void completionIdentifierCheck(){
530 if (checkRecoveredType()) return;
531 if (checkRecoveredMethod()) return;
533 // if not in a method in non diet mode and if not inside a field initializer, only record references attached to types
534 if (!(this.inMethodStack[this.inMethodPtr] && !this.diet)
535 && !insideFieldInitialization()) return;
538 In some cases, the completion identifier may not have yet been consumed,
540 This is because the grammar does not allow any (empty) identifier to follow
541 a base type. We thus have to manually force the identifier to be consumed
544 if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource()
545 if (cursorLocation < this.scanner.startPosition && this.scanner.currentPosition == this.scanner.startPosition){ // fake empty identifier got issued
546 this.pushIdentifier();
547 } else if (cursorLocation+1 >= this.scanner.startPosition && cursorLocation < this.scanner.currentPosition){
548 this.pushIdentifier();
552 // check for different scenarii
554 // no need to go further if we found a non empty completion node
555 // (we still need to store labels though)
556 if (this.assistNode != null) {
557 // however inside an invocation, the completion identifier may already have been consumed into an empty name
558 // completion, so this check should be before we check that we are at the cursor location
559 if (!isEmptyNameCompletion() || checkInvocation()) return;
562 // no need to check further if we are not at the cursor location
563 if (this.indexOfAssistIdentifier() < 0) return;
565 if (checkClassInstanceCreation()) return;
566 if (checkCatchClause()) return;
567 if (checkMemberAccess()) return;
568 if (checkClassLiteralAccess()) return;
570 // if the completion was not on an empty name, it can still be inside an invocation (eg. this.fred("abc"[cursor])
571 // (NB: Put this check before checkNameCompletion() because the selector of the invocation can be on the identifier stack)
572 if (checkInvocation()) return;
574 if (checkNameCompletion()) return;
576 storeLabelsIfNeeded();
579 protected void consumeCaseLabel() {
580 Expression caseExpression = this.expressionStack[this.expressionPtr];
581 if (caseExpression instanceof SingleNameReference || caseExpression instanceof QualifiedNameReference) {
582 // label counter was wrongly incremented in consumeToken
583 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
585 super.consumeCaseLabel();
587 protected void consumeClassHeaderExtends() {
588 this.nextTypeReferenceIsClass = true;
589 super.consumeClassHeaderExtends();
590 this.nextTypeReferenceIsClass = false;
592 protected void consumeClassTypeElt() {
593 this.nextTypeReferenceIsException = true;
594 super.consumeClassTypeElt();
595 this.nextTypeReferenceIsException = false;
597 protected void consumeConditionalExpression(int op) {
598 Expression valueIfTrue = this.expressionStack[this.expressionPtr - 1];
599 if (valueIfTrue instanceof SingleNameReference || valueIfTrue instanceof QualifiedNameReference) {
600 // label counter was wrongly incremented in consumeToken
601 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
603 super.consumeConditionalExpression(op);
605 protected void consumeConstructorBody() {
606 super.consumeConstructorBody();
607 this.labelCounterPtr--;
608 if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
610 protected void consumeConstructorHeader() {
611 super.consumeConstructorHeader();
612 pushBlockInvocationPtr();
614 protected void consumeConstructorHeaderName() {
616 /* no need to take action if not inside assist identifiers */
617 if (indexOfAssistIdentifier() < 0) {
618 super.consumeConstructorHeaderName();
622 /* force to start recovering in order to get fake field behavior */
623 if (currentElement == null){
624 this.hasReportedError = true; // do not report any error
626 this.restartRecovery = true;
628 protected void consumeEnterVariable() {
630 identifierLengthPtr--;
632 boolean isLocalDeclaration = nestedMethod[nestedType] != 0;
633 int variableIndex = variablesCounter[nestedType];
634 int extendedDimension = intStack[intPtr + 1];
636 if(isLocalDeclaration || indexOfAssistIdentifier() < 0 || variableIndex != 0 || extendedDimension != 0) {
638 identifierLengthPtr++;
639 super.consumeEnterVariable();
641 restartRecovery = true;
644 if (currentElement != null) {
645 int nameSourceStart = (int)(identifierPositionStack[identifierPtr] >>> 32);
648 TypeReference type = getTypeReference(intStack[intPtr--]);
651 if (!(currentElement instanceof RecoveredType)
652 && (currentToken == TokenNameDOT
653 || (scanner.getLineNumber(type.sourceStart)
654 != scanner.getLineNumber(nameSourceStart)))){
655 lastCheckPoint = nameSourceStart;
656 restartRecovery = true;
660 FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
661 completionFieldDecl.modifiers = intStack[intPtr--];
662 assistNode = completionFieldDecl;
663 lastCheckPoint = type.sourceEnd + 1;
664 currentElement = currentElement.add(completionFieldDecl, 0);
665 lastIgnoredToken = -1;
669 protected void consumeExitVariableWithInitialization() {
670 super.consumeExitVariableWithInitialization();
672 // does not keep the initialization if completion is not inside
673 AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr];
674 if (cursorLocation + 1 < variable.initialization.sourceStart ||
675 cursorLocation > variable.initialization.sourceEnd) {
676 variable.initialization = null;
681 * Copy of code from superclass with the following change:
682 * If the cursor location is on the field access, then create a
683 * CompletionOnMemberAccess instead.
685 protected void consumeFieldAccess(boolean isSuperAccess) {
686 // FieldAccess ::= Primary '.' 'Identifier'
687 // FieldAccess ::= 'super' '.' 'Identifier'
689 // potential receiver is being poped, so reset potential receiver
690 this.invocationType = NO_RECEIVER;
692 if (this.indexOfAssistIdentifier() < 0) {
693 super.consumeFieldAccess(isSuperAccess);
695 this.pushCompletionOnMemberAccessOnExpressionStack(isSuperAccess);
699 protected void consumeFormalParameter() {
700 if (this.indexOfAssistIdentifier() < 0) {
701 super.consumeFormalParameter();
704 identifierLengthPtr--;
705 char[] name = identifierStack[identifierPtr];
706 long namePositions = identifierPositionStack[identifierPtr--];
707 TypeReference type = getTypeReference(intStack[intPtr--] + intStack[intPtr--]);
710 new CompletionOnArgumentName(
714 intStack[intPtr + 1] & ~AccDeprecated); // modifiers
718 this.lastCheckPoint = (int) namePositions;
719 isOrphanCompletionNode = true;
721 /* if incomplete method header, listLength counter will not have been reset,
722 indicating that some arguments are available on the stack */
726 protected void consumeInterfaceType() {
727 this.nextTypeReferenceIsInterface = true;
728 super.consumeInterfaceType();
729 this.nextTypeReferenceIsInterface = false;
731 protected void consumeMethodHeaderName() {
732 if(this.indexOfAssistIdentifier() < 0) {
734 identifierLengthPtr--;
735 if(this.indexOfAssistIdentifier() != 0) {
737 identifierLengthPtr++;
738 super.consumeMethodHeaderName();
740 restartRecovery = true;
743 if (currentElement != null) {
745 char[] selector = identifierStack[identifierPtr + 1];
746 long selectorSource = identifierPositionStack[identifierPtr + 1];
749 TypeReference type = getTypeReference(intStack[intPtr--]);
750 ((CompletionOnSingleTypeReference)type).isCompletionNode = false;
752 int declarationSourceStart = intStack[intPtr--];
753 int modifiers = intStack[intPtr--];
755 if(scanner.getLineNumber(type.sourceStart) != scanner.getLineNumber((int) (selectorSource >>> 32))) {
756 FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
757 completionFieldDecl.modifiers = modifiers;
758 assistNode = completionFieldDecl;
759 lastCheckPoint = type.sourceEnd + 1;
760 currentElement = currentElement.add(completionFieldDecl, 0);
761 lastIgnoredToken = -1;
763 CompletionOnMethodReturnType md = new CompletionOnMethodReturnType(type, this.compilationUnit.compilationResult);
764 md.selector = selector;
765 md.declarationSourceStart = declarationSourceStart;
766 md.modifiers = modifiers;
767 md.bodyStart = lParenPos+1;
768 listLength = 0; // initialize listLength before reading parameters/throws
770 this.lastCheckPoint = md.bodyStart;
771 currentElement = currentElement.add(md, 0);
772 lastIgnoredToken = -1;
777 // MethodHeaderName ::= Modifiersopt Type 'Identifier' '('
778 CompletionOnMethodName md = new CompletionOnMethodName(this.compilationUnit.compilationResult);
781 md.selector = identifierStack[identifierPtr];
782 long selectorSource = identifierPositionStack[identifierPtr--];
784 md.returnType = getTypeReference(intStack[intPtr--]);
786 md.declarationSourceStart = intStack[intPtr--];
787 md.modifiers = intStack[intPtr--];
789 //highlight starts at selector start
790 md.sourceStart = (int) (selectorSource >>> 32);
791 md.selectorEnd = (int) selectorSource;
793 md.sourceEnd = lParenPos;
794 md.bodyStart = lParenPos+1;
795 listLength = 0; // initialize listLength before reading parameters/throws
797 this.assistNode = md;
798 this.lastCheckPoint = md.sourceEnd;
800 if (currentElement != null){
801 if (currentElement instanceof RecoveredType
802 //|| md.modifiers != 0
803 || (scanner.getLineNumber(md.returnType.sourceStart)
804 == scanner.getLineNumber(md.sourceStart))){
805 lastCheckPoint = md.bodyStart;
806 currentElement = currentElement.add(md, 0);
807 lastIgnoredToken = -1;
809 lastCheckPoint = md.sourceStart;
810 restartRecovery = true;
817 protected void consumeMethodBody() {
818 super.consumeMethodBody();
819 this.labelCounterPtr--;
820 if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
823 protected void consumeMethodHeader() {
824 super.consumeMethodHeader();
825 pushBlockInvocationPtr();
827 protected void consumeModifiers() {
828 super.consumeModifiers();
829 // save from stack values
830 this.lastModifiersStart = intStack[intPtr];
831 this.lastModifiers = intStack[intPtr-1];
833 protected void consumeNestedMethod() {
834 super.consumeNestedMethod();
835 this.pushNewLabelCounter();
837 protected void consumeStatementLabel() {
838 super.consumeStatementLabel();
839 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
841 protected void consumeToken(int token) {
842 int previous = this.previousToken;
843 int previousIdentifierPtr = this.previousIdentifierPtr;
844 super.consumeToken(token);
846 // if in field initializer (directly or not), on the completion identifier and not in recovery mode yet
847 // then position end of file at cursor location (so that we have the same behavior as
849 if (token == TokenNameIdentifier
850 && this.identifierStack[this.identifierPtr] == assistIdentifier()
851 && this.currentElement == null
852 && this.insideFieldInitialization()) {
853 this.scanner.eofPosition = cursorLocation < Integer.MAX_VALUE ? cursorLocation+1 : cursorLocation;
856 // if in a method or if in a field initializer
857 if (this.inMethodStack[this.inMethodPtr] || this.inFieldInitializationStack[this.inFieldInitializationPtr]) {
861 // case TokenNamethis: // eg. this[.]fred()
862 // this.invocationType = EXPLICIT_RECEIVER;
864 // case TokenNamesuper: // eg. super[.]fred()
865 // this.invocationType = SUPER_RECEIVER;
867 case TokenNameIdentifier: // eg. bar[.]fred()
868 if (!this.betweenNewAndLeftBraket) { // eg. not new z.y[.]X()
869 if (this.identifierPtr != previousIdentifierPtr) { // if identifier has been consumed, eg. this.x[.]fred()
870 this.invocationType = EXPLICIT_RECEIVER;
872 this.invocationType = NAME_RECEIVER;
878 case TokenNameIdentifier:
879 if (previous == TokenNameDOT) { // eg. foo().[fred]()
880 // if current identifier is the empty completion one
881 if (identifierStack[identifierPtr] == CompletionScanner.EmptyCompletionIdentifier){
882 this.completionBehindDot = true;
884 if (this.invocationType != SUPER_RECEIVER // eg. not super.[fred]()
885 && this.invocationType != NAME_RECEIVER // eg. not bar.[fred]()
886 && this.invocationType != ALLOCATION // eg. not new foo.[Bar]()
887 && this.invocationType != QUALIFIED_ALLOCATION) { // eg. not fred().new foo.[Bar]()
889 this.invocationType = EXPLICIT_RECEIVER;
890 this.qualifier = this.expressionPtr;
895 this.betweenNewAndLeftBraket = true;
896 this.qualifier = this.expressionPtr; // NB: even if there is no qualification, set it to the expression ptr so that the number of arguments are correctly computed
897 if (previous == TokenNameDOT) { // eg. fred().[new] X()
898 this.invocationType = QUALIFIED_ALLOCATION;
899 } else { // eg. [new] X()
900 this.invocationType = ALLOCATION;
903 // case TokenNamethis:
904 // if (previous == TokenNameDOT) { // eg. fred().[this]()
905 // this.invocationType = QUALIFIED_ALLOCATION;
906 // this.qualifier = this.expressionPtr;
909 // case TokenNamesuper:
910 // if (previous == TokenNameDOT) { // eg. fred().[super]()
911 // this.invocationType = QUALIFIED_ALLOCATION;
912 // this.qualifier = this.expressionPtr;
915 // case TokenNamecatch:
916 // this.betweenCatchAndRightParen = true;
918 case TokenNameLPAREN:
919 this.betweenNewAndLeftBraket = false;
921 if (this.invocationType == NO_RECEIVER || this.invocationType == NAME_RECEIVER) {
922 this.qualifier = this.expressionPtr; // remenber the last expression so that arguments are correctly computed
925 case TokenNameIdentifier: // eg. fred[(]) or foo.fred[(])
926 this.pushOnInvocationStacks(this.invocationType, this.qualifier);
927 this.invocationType = NO_RECEIVER;
929 // case TokenNamethis: // explicit constructor invocation, eg. this[(]1, 2)
930 // this.pushOnInvocationStacks(
931 // (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION,
933 // this.invocationType = NO_RECEIVER;
935 // case TokenNamesuper: // explicit constructor invocation, eg. super[(]1, 2)
936 // this.pushOnInvocationStacks(
937 // (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION,
939 // this.invocationType = NO_RECEIVER;
943 case TokenNameLBRACE:
944 this.betweenNewAndLeftBraket = false;
946 this.pushBlockInvocationPtr();
948 case TokenNameLBRACKET:
949 this.betweenNewAndLeftBraket = false;
952 case TokenNameRBRACE:
954 if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
956 case TokenNameRBRACKET:
959 case TokenNameRPAREN:
960 this.betweenCatchAndRightParen = false;
964 if (previous == TokenNameIdentifier) {
965 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]++;
968 // case TokenNamethrow:
969 // this.throwBracketDepth= bracketDepth;
975 * Return whether the given ast node contains the completion node.
977 private boolean containsCompletionNode(AstNode ast) {
978 if (this.assistNode == null || ast instanceof Literal) {
981 if (this.assistNode == ast) {
984 if (ast instanceof Reference || ast instanceof TypeReference) {
985 return ast == this.assistNode;
987 if (ast instanceof Assignment) {
988 Assignment assign = (Assignment)ast;
989 return containsCompletionNode(assign.lhs) || containsCompletionNode(assign.expression);
991 if (ast instanceof UnaryExpression) {
992 UnaryExpression unary = (UnaryExpression)ast;
993 return containsCompletionNode(unary.expression);
995 if (ast instanceof BinaryExpression) {
996 BinaryExpression binary = (BinaryExpression)ast;
997 return containsCompletionNode(binary.left) || containsCompletionNode(binary.right);
999 if (ast instanceof InstanceOfExpression) {
1000 InstanceOfExpression instanceOfExpr = (InstanceOfExpression)ast;
1001 return containsCompletionNode(instanceOfExpr.expression) || containsCompletionNode(instanceOfExpr.type);
1003 if (ast instanceof ConditionalExpression) {
1004 ConditionalExpression conditional = (ConditionalExpression)ast;
1005 return containsCompletionNode(conditional.condition) || containsCompletionNode(conditional.valueIfTrue) || containsCompletionNode(conditional.valueIfFalse);
1007 if (ast instanceof AllocationExpression) {
1008 AllocationExpression alloc = (AllocationExpression)ast;
1009 return containsCompletionNode(alloc.type);
1011 if (ast instanceof CastExpression) {
1012 CastExpression cast = (CastExpression)ast;
1013 return containsCompletionNode(cast.expression) || containsCompletionNode(cast.type);
1015 if (ast instanceof ExplicitConstructorCall) {
1016 ExplicitConstructorCall call = (ExplicitConstructorCall)ast;
1017 Expression[] arguments = call.arguments;
1018 if (arguments != null) {
1019 for (int i = 0; i < arguments.length; i++) {
1020 if (containsCompletionNode(arguments[i])) {
1029 public ImportReference createAssistImportReference(char[][] tokens, long[] positions){
1030 return new CompletionOnImportReference(tokens, positions);
1032 public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){
1033 return new CompletionOnPackageReference(tokens, positions);
1035 public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions){
1036 return new CompletionOnQualifiedNameReference(
1037 previousIdentifiers,
1041 public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions){
1042 return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario
1043 ? new CompletionOnQualifiedExceptionReference(
1044 previousIdentifiers,
1047 : this.nextTypeReferenceIsInterface
1048 ? new CompletionOnQualifiedInterfaceReference(
1049 previousIdentifiers,
1052 : this.nextTypeReferenceIsClass
1053 ? new CompletionOnQualifiedClassReference(
1054 previousIdentifiers,
1057 : new CompletionOnQualifiedTypeReference(
1058 previousIdentifiers,
1062 public NameReference createSingleAssistNameReference(char[] name, long position) {
1063 return new CompletionOnSingleNameReference(name, position);
1065 public TypeReference createSingleAssistTypeReference(char[] name, long position) {
1066 return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario
1067 ? new CompletionOnExceptionReference(name, position)
1068 : this.nextTypeReferenceIsInterface
1069 ? new CompletionOnInterfaceReference(name, position)
1070 : this.nextTypeReferenceIsClass
1071 ? new CompletionOnClassReference(name, position)
1072 : new CompletionOnSingleTypeReference(name, position);
1074 public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
1076 this.cursorLocation = cursorLocation;
1077 CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1078 completionScanner.completionIdentifier = null;
1079 completionScanner.cursorLocation = cursorLocation;
1080 return this.dietParse(sourceUnit, compilationResult);
1083 * Flush parser/scanner state regarding to code assist
1085 public void flushAssistState() {
1087 super.flushAssistState();
1088 this.isOrphanCompletionNode = false;
1089 CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1090 completionScanner.completedIdentifierStart = 0;
1091 completionScanner.completedIdentifierEnd = -1;
1093 protected NameReference getUnspecifiedReferenceOptimized() {
1094 if (this.identifierLengthStack[this.identifierLengthPtr] > 1) { // reducing a qualified name
1095 // potential receiver is being poped, so reset potential receiver
1096 this.invocationType = NO_RECEIVER;
1098 return super.getUnspecifiedReferenceOptimized();
1101 * Return whether the given ast node has information interresting for code completion.
1103 private boolean hasCompletionInformation(AstNode ast) {
1105 ast instanceof AbstractMethodDeclaration ||
1106 ast instanceof AbstractVariableDeclaration ||
1107 ast instanceof LabeledStatement ||
1108 ast instanceof TypeDeclaration);
1110 public void initialize() {
1112 this.initializeForBlockStatements();
1113 this.labelCounterPtr = -1;
1116 * Initializes the state of the parser that is about to go for BlockStatements.
1118 private void initializeForBlockStatements() {
1119 this.previousToken = -1;
1120 this.previousIdentifierPtr = -1;
1121 this.completionBehindDot = false;
1122 this.betweenNewAndLeftBraket = false;
1123 this.betweenCatchAndRightParen = false;
1124 this.bracketDepth = 0;
1125 this.throwBracketDepth = -1;
1126 this.invocationType = NO_RECEIVER;
1127 this.qualifier = -1;
1128 this.blockInvocationPtr = -1;
1130 public void initializeScanner(){
1131 this.scanner = new CompletionScanner(this.assertMode);
1134 * Returns whether the completion is just after an array type
1135 * eg. String[].[cursor]
1137 private boolean isAfterArrayType() {
1138 // TBD: The following relies on the fact that array dimensions are small: it says that if the
1139 // top of the intStack is less than 11, then it must be a dimension
1140 // (smallest position of array type in a compilation unit is 11 as in "class X{Y[]")
1141 if ((this.intPtr > -1) && (this.intStack[this.intPtr] < 11)) {
1146 private boolean isEmptyNameCompletion() {
1148 this.assistNode != null &&
1149 this.assistNode instanceof CompletionOnSingleNameReference &&
1150 (((CompletionOnSingleNameReference)this.assistNode).token.length == 0);
1152 public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
1154 this.cursorLocation = cursorLocation;
1155 CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1156 completionScanner.completionIdentifier = null;
1157 completionScanner.cursorLocation = cursorLocation;
1158 return this.parse(sourceUnit, compilationResult);
1161 * Prepares the state of the parser to go for BlockStatements.
1163 protected void prepareForBlockStatements() {
1164 super.prepareForBlockStatements();
1165 this.initializeForBlockStatements();
1167 protected void pushBlockInvocationPtr() {
1169 this.blockInvocationStack[++this.blockInvocationPtr] = this.invocationPtr+1;
1170 } catch (IndexOutOfBoundsException e) {
1171 int oldStackLength = this.blockInvocationStack.length;
1172 int[] oldStack = this.blockInvocationStack;
1173 this.blockInvocationStack = new int[oldStackLength + StackIncrement];
1174 System.arraycopy(oldStack, 0, this.blockInvocationStack, 0, oldStackLength);
1175 this.blockInvocationStack[this.blockInvocationPtr] = this.invocationPtr+1;
1179 * Creates a completion on member access node and push it
1180 * on the expression stack.
1182 private void pushCompletionOnMemberAccessOnExpressionStack(boolean isSuperAccess) {
1183 char[] source = identifierStack[identifierPtr];
1184 long pos = identifierPositionStack[identifierPtr--];
1185 CompletionOnMemberAccess fr = new CompletionOnMemberAccess(source, pos);
1186 this.assistNode = fr;
1187 this.lastCheckPoint = fr.sourceEnd + 1;
1188 identifierLengthPtr--;
1189 if (isSuperAccess) { //considerates the fieldReference beginning at the 'super' ....
1190 fr.sourceStart = intStack[intPtr--];
1191 fr.receiver = new SuperReference(fr.sourceStart, endPosition);
1192 pushOnExpressionStack(fr);
1193 } else { //optimize push/pop
1194 if ((fr.receiver = expressionStack[expressionPtr]).isThis()) { //fieldreference begins at the this
1195 fr.sourceStart = fr.receiver.sourceStart;
1197 expressionStack[expressionPtr] = fr;
1200 protected void pushNewLabelCounter() {
1202 this.labelCounterStack[++this.labelCounterPtr] = 0;
1203 } catch (IndexOutOfBoundsException e) {
1204 int oldStackLength = this.labelCounterStack.length;
1205 int[] oldStack = this.labelCounterStack;
1206 this.labelCounterStack = new int[oldStackLength + StackIncrement];
1207 System.arraycopy(oldStack, 0, this.labelCounterStack, 0, oldStackLength);
1208 this.labelCounterStack[this.labelCounterPtr] = 0;
1212 * Pushes the given invocation type (one of the invocation type constants) on the invocation type stack,
1213 * and the given qualifier (an expression pointer to the expression stack) on the qualifier stack.
1215 protected void pushOnInvocationStacks(int invocationType, int qualifierExprPtr) {
1216 // NB: invocationPtr has already been incremented by a call to pushOnSelectorStack()
1218 this.invocationTypeStack[this.invocationPtr] = invocationType;
1219 this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
1220 } catch (IndexOutOfBoundsException e) {
1221 int oldStackLength = this.invocationTypeStack.length;
1222 int oldInvocationTypeStack[] = this.invocationTypeStack;
1223 int oldQualifierStack[] = this.qualifierStack;
1224 this.invocationTypeStack = new int[oldStackLength + StackIncrement];
1225 this.qualifierStack = new int[oldStackLength + StackIncrement];
1226 System.arraycopy(oldInvocationTypeStack, 0, this.invocationTypeStack, 0, oldStackLength);
1227 System.arraycopy(oldQualifierStack, 0, this.qualifierStack, 0, oldStackLength);
1228 this.invocationTypeStack[this.invocationPtr] = invocationType;
1229 this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
1232 public void recordCompletionOnReference(){
1234 if (currentElement instanceof RecoveredType){
1235 RecoveredType recoveredType = (RecoveredType)currentElement;
1237 /* filter out cases where scanner is still inside type header */
1238 if (!recoveredType.foundOpeningBrace) return;
1240 /* generate a pseudo field with a completion on type reference */
1242 new CompletionOnFieldType(this.getTypeReference(0), false), 0);
1245 if (!diet) return; // only record references attached to types
1248 protected void reportSyntaxError(int act, int currentKind, int stateStackTop) {
1250 /* Intercept error state on EOF inside method bodies, due to
1251 cursor location being used as an EOF position.
1253 if (!diet && currentToken == TokenNameEOF) return;
1254 super.reportSyntaxError(act, currentKind, stateStackTop);
1257 * Reset internal state after completion is over
1260 public void reset() {
1262 this.cursorLocation = 0;
1265 * Reset internal state after completion is over
1268 public void resetAfterCompletion() {
1269 this.cursorLocation = 0;
1270 this.flushAssistState();
1273 * Reset context so as to resume to regular parse loop
1274 * If unable to reset for resuming, answers false.
1276 * Move checkpoint location, reset internal stacks and
1277 * decide which grammar goal is activated.
1279 protected boolean resumeAfterRecovery() {
1280 if (this.assistNode != null) {
1281 /* if reached [eof] inside method body, but still inside nested type,
1282 or inside a field initializer, should continue in diet mode until
1283 the end of the method body or compilation unit */
1284 if ((scanner.eofPosition == cursorLocation+1)
1285 && (!(referenceContext instanceof CompilationUnitDeclaration)
1286 || insideFieldInitialization())) {
1288 /* disabled since does not handle possible field/message refs, i.e. Obj[ASSIST HERE]ect.registerNatives()
1289 // consume extra tokens which were part of the qualified reference
1290 // so that the replaced source comprises them as well
1291 if (this.assistNode instanceof NameReference){
1292 int oldEof = scanner.eofPosition;
1293 scanner.eofPosition = currentElement.topElement().sourceEnd()+1;
1294 scanner.currentPosition = this.cursorLocation+1;
1298 // first token might not have to be a dot
1299 if (token >= 0 || !this.completionBehindDot){
1300 if ((token = scanner.getNextToken()) != TokenNameDOT) break;
1302 if ((token = scanner.getNextToken()) != TokenNameIdentifier) break;
1303 this.assistNode.sourceEnd = scanner.currentPosition - 1;
1304 } while (token != TokenNameEOF);
1305 } catch (InvalidInputException e){
1307 scanner.eofPosition = oldEof;
1311 /* restart in diet mode for finding sibling constructs */
1312 if (currentElement.enclosingType() != null){
1313 lastCheckPoint = this.assistNode.sourceEnd+1;
1314 int end = currentElement.topElement().sourceEnd();
1315 scanner.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end;
1322 return super.resumeAfterRecovery();
1324 public void setAssistIdentifier(char[] assistIdent){
1325 ((CompletionScanner)scanner).completionIdentifier = assistIdent;
1328 * Stores the labels left on the identifier stack if they have not been stored yet.
1330 private void storeLabelsIfNeeded() {
1331 // int counter = this.labelCounterPtr >= 0 ? this.labelCounterStack[this.labelCounterPtr] : 0;
1332 // if (this.labels == null && this.identifierPtr >= 0) {
1333 // this.labels = new char[counter][];
1334 // System.arraycopy(this.identifierStack, this.identifierPtr - counter + 1, this.labels, 0, counter);
1336 // this.identifierPtr -= counter;
1337 // this.identifierLengthPtr -= counter; // labels have not been concatenated yet
1340 * Update recovery state based on current parser/scanner state
1342 protected void updateRecoveryState() {
1344 /* expose parser state to recovery state */
1345 currentElement.updateFromParserState();
1347 /* may be able to retrieve completionNode as an orphan, and then attach it */
1348 this.completionIdentifierCheck();
1349 this.attachOrphanCompletionNode();
1351 // if an assist node has been found and a recovered element exists,
1352 // mark enclosing blocks as to be preserved
1353 if (this.assistNode != null && this.currentElement != null) {
1354 currentElement.preserveEnclosingBlocks();
1357 /* check and update recovered state based on current token,
1358 this action is also performed when shifting token after recovery
1361 this.recoveryTokenCheck();
1364 protected LocalDeclaration createLocalDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
1365 if (this.indexOfAssistIdentifier() < 0) {
1366 return super.createLocalDeclaration(initialization, name, sourceStart, sourceEnd);
1368 CompletionOnLocalName local = new CompletionOnLocalName(initialization, name, sourceStart, sourceEnd);
1369 this.assistNode = local;
1370 this.lastCheckPoint = sourceEnd + 1;
1375 protected FieldDeclaration createFieldDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
1376 if (this.indexOfAssistIdentifier() < 0) {
1377 return super.createFieldDeclaration(initialization, name, sourceStart, sourceEnd);
1379 CompletionOnFieldName field = new CompletionOnFieldName(initialization, name, sourceStart, sourceEnd);
1380 this.assistNode = field;
1381 this.lastCheckPoint = sourceEnd + 1;