codetools: simplified gathering helpers, patch #28794 from Ondrej Pokorny

git-svn-id: trunk@49991 -
This commit is contained in:
mattias 2015-10-08 10:09:12 +00:00
parent 3cabc3eba0
commit bd020c787f
2 changed files with 244 additions and 242 deletions

View File

@ -560,7 +560,6 @@ begin
end;
Params:=TFindDeclarationParams.Create(Self,ContextNode);
try
FindHelpersInContext(Params);
Params.SetIdentifier(Self,@TypeName[1],nil);
Params.Flags:=[fdfExceptionOnNotFound,fdfSearchInParentNodes];
//DebugLn(['TEventsCodeTool.FindMethodTypeInfo TypeName=',TypeName,' MainFilename=',MainFilename]);

View File

@ -2524,234 +2524,236 @@ begin
Result:=false;
ActivateGlobalWriteLock;
InitCollectIdentifiers(CursorPos,IdentifierList);
IdentStartXY:=FindIdentifierStartPos(CursorPos);
if CheckCursorInCompilerDirective(IdentStartXY) then exit(true);
ParseSourceTillCollectionStart(IdentStartXY,CleanCursorPos,CursorNode,
IdentStartPos,IdentEndPos);
Params:=TFindDeclarationParams.Create(Self,CursorNode);
try
FindHelpersInContext(Params);
if CleanCursorPos=0 then ;
if IdentStartPos>0 then begin
MoveCursorToCleanPos(IdentStartPos);
ReadNextAtom;
CurrentIdentifierList.StartAtom:=CurPos;
end;
InitCollectIdentifiers(CursorPos,IdentifierList);
IdentStartXY:=FindIdentifierStartPos(CursorPos);
if CheckCursorInCompilerDirective(IdentStartXY) then exit(true);
// find context
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers B',
' CleanCursorPos=',CleanPosToStr(CleanCursorPos),
' IdentStartPos=',CleanPosToStr(IdentStartPos),' IdentEndPos=',CleanPosToStr(IdentEndPos),
' Ident=',copy(Src,IdentStartPos,IdentEndPos-IdentStartPos));
{$ENDIF}
GatherContext:=CreateFindContext(Self,CursorNode);
CurrentIdentifierList.NewMemberVisibility:=GetClassVisibility(CursorNode);
if CursorNode.Desc in [ctnUsesSection,ctnUseUnit] then begin
GatherUnitNames;
MoveCursorToCleanPos(IdentEndPos);
ReadNextAtom;
if (CurPos.Flag=cafWord) and (not UpAtomIs('IN')) then begin
// add comma
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfNeedsEndComma];
end;
end else if (CursorNode.Desc in AllSourceTypes)
and (PositionsInSameLine(Src,CursorNode.StartPos,IdentStartPos)) then begin
GatherSourceNames(GatherContext);
end else begin
FindCollectionContext(Params,IdentStartPos,CursorNode,
ExprType,ContextExprStartPos,StartInSubContext);
GatherContext := ExprType.Context;
// find class and ancestors if existing (needed for protected identifiers)
if GatherContext.Tool = Self then
FindContextClassAndAncestors(IdentStartXY, FICTClassAndAncestors);
CursorContext:=CreateFindContext(Self,CursorNode);
GatherContextKeywords(CursorContext,IdentStartPos,Beautifier);
// check for incomplete context
// context bracket level
CurrentIdentifierList.StartBracketLvl:=
GetBracketLvl(Src,CursorNode.StartPos,IdentStartPos,
Scanner.NestedComments);
if CursorNode.Desc in AllPascalStatements then begin
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartInStatement];
end;
// context in front of
StartPosOfVariable:=FindStartOfTerm(IdentStartPos,NodeTermInType(CursorNode));
if StartPosOfVariable>0 then begin
if StartPosOfVariable=IdentStartPos then begin
// cursor is at start of an operand
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartOfOperand];
end else begin
MoveCursorToCleanPos(IdentStartPos);
ReadPriorAtom;
if CurPos.Flag=cafPoint then
// cursor is behind a point
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartIsSubIdent];
end;
MoveCursorToCleanPos(StartPosOfVariable);
ReadPriorAtom;
CurrentIdentifierList.StartAtomInFront:=CurPos;
if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags)
then begin
// check if LValue
if (CurPos.Flag in [cafSemicolon,cafEnd,cafColon])
or UpAtomIs('BEGIN')
or UpAtomIs('TRY') or UpAtomIs('FINALLY') or UpAtomIs('EXCEPT')
or UpAtomIs('FOR') or UpAtomIs('DO') or UpAtomIs('THEN')
or UpAtomIs('REPEAT') or UpAtomIs('ASM') or UpAtomIs('ELSE')
then begin
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartOfStatement];
end;
// check if expression
if UpAtomIs('IF') or UpAtomIs('CASE') or UpAtomIs('WHILE')
or UpAtomIs('UNTIL')
then begin
// todo: check at start of expression, not only in front of variable
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfIsExpression, ilcfDontAllowProcedures];
end;
// check if procedure is allowed
if CurPos.Flag in [cafComma, cafRoundBracketOpen, cafEdgedBracketOpen, cafEqual, cafOtherOperator] then
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfDontAllowProcedures];
end;
end;
// context behind
if (IdentEndPos<SrcLen) then begin
MoveCursorToCleanPos(IdentEndPos);
//debugln(['TIdentCompletionTool.GatherIdentifiers "',dbgstr(Src,IdentStartPos,IdentEndPos-IdentStartPos),'"']);
InFrontOfDirective:=(CurPos.StartPos<SrcLen) and (Src[CurPos.StartPos]='{')
and (Src[CurPos.StartPos+1]='$');
ParseSourceTillCollectionStart(IdentStartXY,CleanCursorPos,CursorNode,
IdentStartPos,IdentEndPos);
Params:=TFindDeclarationParams.Create(Self,CursorNode);
try
if CleanCursorPos=0 then ;
if IdentStartPos>0 then begin
MoveCursorToCleanPos(IdentStartPos);
ReadNextAtom;
CurrentIdentifierList.StartAtom:=CurPos;
end;
// check end of line
if (not InFrontOfDirective)
and (CursorPos.Code.LineColIsOutside(CursorPos.Y,CursorPos.X)
or (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
then
// find context
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers B',
' CleanCursorPos=',CleanPosToStr(CleanCursorPos),
' IdentStartPos=',CleanPosToStr(IdentStartPos),' IdentEndPos=',CleanPosToStr(IdentEndPos),
' Ident=',copy(Src,IdentStartPos,IdentEndPos-IdentStartPos));
{$ENDIF}
GatherContext:=CreateFindContext(Self,CursorNode);
CurrentIdentifierList.NewMemberVisibility:=GetClassVisibility(CursorNode);
if CursorNode.Desc in [ctnUsesSection,ctnUseUnit] then begin
GatherUnitNames;
MoveCursorToCleanPos(IdentEndPos);
ReadNextAtom;
if (CurPos.Flag=cafWord) and (not UpAtomIs('IN')) then begin
// add comma
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfEndOfLine];
CurrentIdentifierList.StartAtomBehind:=CurPos;
// check if a semicolon is needed or forbidden at the end
if InFrontOfDirective
or (CurrentIdentifierList.StartBracketLvl>0)
or (CurPos.Flag in [cafSemicolon, cafEqual, cafColon, cafComma,
cafPoint, cafRoundBracketOpen, cafRoundBracketClose,
cafEdgedBracketOpen, cafEdgedBracketClose])
or ((CurPos.Flag in [cafWord,cafNone])
and (UpAtomIs('ELSE')
or UpAtomIs('THEN')
or UpAtomIs('DO')
or UpAtomIs('TO')
or UpAtomIs('OF')
or WordIsBinaryOperator.DoItCaseInsensitive(Src,
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
then begin
// do not add semicolon
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfNoEndSemicolon];
CurrentIdentifierList.ContextFlags+[ilcfNeedsEndComma];
end;
// check if in statement
if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags) then
begin
// check if a semicolon is needed at the end
if (not (ilcfNoEndSemicolon in CurrentIdentifierList.ContextFlags))
then begin
// check if a semicolon is needed at the end
if (CurPos.Flag in [cafEnd])
or WordIsBlockKeyWord.DoItCaseInsensitive(Src,
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
or ((CurPos.Flag=cafWord)
and (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
then begin
// add semicolon
end else if (CursorNode.Desc in AllSourceTypes)
and (PositionsInSameLine(Src,CursorNode.StartPos,IdentStartPos)) then begin
GatherSourceNames(GatherContext);
end else begin
FindCollectionContext(Params,IdentStartPos,CursorNode,
ExprType,ContextExprStartPos,StartInSubContext);
GatherContext := ExprType.Context;
// find class and ancestors if existing (needed for protected identifiers)
if GatherContext.Tool = Self then
FindContextClassAndAncestors(IdentStartXY, FICTClassAndAncestors);
CursorContext:=CreateFindContext(Self,CursorNode);
GatherContextKeywords(CursorContext,IdentStartPos,Beautifier);
// check for incomplete context
// context bracket level
CurrentIdentifierList.StartBracketLvl:=
GetBracketLvl(Src,CursorNode.StartPos,IdentStartPos,
Scanner.NestedComments);
if CursorNode.Desc in AllPascalStatements then begin
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartInStatement];
end;
// context in front of
StartPosOfVariable:=FindStartOfTerm(IdentStartPos,NodeTermInType(CursorNode));
if StartPosOfVariable>0 then begin
if StartPosOfVariable=IdentStartPos then begin
// cursor is at start of an operand
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartOfOperand];
end else begin
MoveCursorToCleanPos(IdentStartPos);
ReadPriorAtom;
if CurPos.Flag=cafPoint then
// cursor is behind a point
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfNeedsEndSemicolon];
CurrentIdentifierList.ContextFlags+[ilcfStartIsSubIdent];
end;
MoveCursorToCleanPos(StartPosOfVariable);
ReadPriorAtom;
CurrentIdentifierList.StartAtomInFront:=CurPos;
if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags)
then begin
// check if LValue
if (CurPos.Flag in [cafSemicolon,cafEnd,cafColon])
or UpAtomIs('BEGIN')
or UpAtomIs('TRY') or UpAtomIs('FINALLY') or UpAtomIs('EXCEPT')
or UpAtomIs('FOR') or UpAtomIs('DO') or UpAtomIs('THEN')
or UpAtomIs('REPEAT') or UpAtomIs('ASM') or UpAtomIs('ELSE')
then begin
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartOfStatement];
end;
// check if expression
if UpAtomIs('IF') or UpAtomIs('CASE') or UpAtomIs('WHILE')
or UpAtomIs('UNTIL')
then begin
// todo: check at start of expression, not only in front of variable
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfIsExpression, ilcfDontAllowProcedures];
end;
// check if procedure is allowed
if CurPos.Flag in [cafComma, cafRoundBracketOpen, cafEdgedBracketOpen, cafEqual, cafOtherOperator] then
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfDontAllowProcedures];
end;
end;
// context behind
if (IdentEndPos<SrcLen) then begin
MoveCursorToCleanPos(IdentEndPos);
//debugln(['TIdentCompletionTool.GatherIdentifiers "',dbgstr(Src,IdentStartPos,IdentEndPos-IdentStartPos),'"']);
InFrontOfDirective:=(CurPos.StartPos<SrcLen) and (Src[CurPos.StartPos]='{')
and (Src[CurPos.StartPos+1]='$');
ReadNextAtom;
// check end of line
if (not InFrontOfDirective)
and (CursorPos.Code.LineColIsOutside(CursorPos.Y,CursorPos.X)
or (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
then
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfEndOfLine];
CurrentIdentifierList.StartAtomBehind:=CurPos;
// check if a semicolon is needed or forbidden at the end
if InFrontOfDirective
or (CurrentIdentifierList.StartBracketLvl>0)
or (CurPos.Flag in [cafSemicolon, cafEqual, cafColon, cafComma,
cafPoint, cafRoundBracketOpen, cafRoundBracketClose,
cafEdgedBracketOpen, cafEdgedBracketClose])
or ((CurPos.Flag in [cafWord,cafNone])
and (UpAtomIs('ELSE')
or UpAtomIs('THEN')
or UpAtomIs('DO')
or UpAtomIs('TO')
or UpAtomIs('OF')
or WordIsBinaryOperator.DoItCaseInsensitive(Src,
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
then begin
// do not add semicolon
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfNoEndSemicolon];
end;
// check if in statement
if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags) then
begin
// check if a semicolon is needed at the end
if (not (ilcfNoEndSemicolon in CurrentIdentifierList.ContextFlags))
then begin
// check if a semicolon is needed at the end
if (CurPos.Flag in [cafEnd])
or WordIsBlockKeyWord.DoItCaseInsensitive(Src,
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
or ((CurPos.Flag=cafWord)
and (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
then begin
// add semicolon
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfNeedsEndSemicolon];
end;
end;
end;
end;
// check missing 'do' after 'with'
if CurrentIdentifierList.StartUpAtomInFrontIs('WITH')
and (not CurrentIdentifierList.StartUpAtomBehindIs('DO'))
and (CurrentIdentifierList.StartBracketLvl=0)
and (not (CurrentIdentifierList.StartAtomBehind.Flag in
[cafComma,cafPoint,cafRoundBracketOpen,cafEdgedBracketOpen]))
and (not CurrentIdentifierList.StartUpAtomBehindIs('^'))
then
// check missing 'do' after 'with'
if CurrentIdentifierList.StartUpAtomInFrontIs('WITH')
and (not CurrentIdentifierList.StartUpAtomBehindIs('DO'))
and (CurrentIdentifierList.StartBracketLvl=0)
and (not (CurrentIdentifierList.StartAtomBehind.Flag in
[cafComma,cafPoint,cafRoundBracketOpen,cafEdgedBracketOpen]))
and (not CurrentIdentifierList.StartUpAtomBehindIs('^'))
then
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfNeedsDo];
end else begin
// end of source
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfNeedsDo];
end else begin
// end of source
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfEndOfLine];
CurrentIdentifierList.ContextFlags+[ilcfEndOfLine];
end;
// search and gather identifiers in context
if (GatherContext.Tool<>nil) and (GatherContext.Node<>nil) then begin
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers D CONTEXT: ',
GatherContext.Tool.MainFilename,
' ',GatherContext.Node.DescAsString,
' "',StringToPascalConst(copy(GatherContext.Tool.Src,GatherContext.Node.StartPos,50)),'"');
{$ENDIF}
// gather all identifiers in context
Params.ContextNode:=GatherContext.Node;
Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
if (Params.ContextNode.Desc=ctnInterface) and StartInSubContext then
Include(Params.Flags,fdfIgnoreUsedUnits);
if not StartInSubContext then
Include(Params.Flags,fdfSearchInParentNodes);
if Params.ContextNode.Desc in AllClasses then
Exclude(Params.Flags,fdfSearchInParentNodes);
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers F');
{$ENDIF}
CurrentIdentifierList.Context:=GatherContext;
if GatherContext.Node.Desc=ctnIdentifier then
Params.Flags:=Params.Flags+[fdfIgnoreCurContextNode];
GatherContext.Tool.FindIdentifierInContext(Params);
end else
if ExprType.Desc in xtAllIdentPredefinedTypes then
begin
// gather all identifiers in cursor context for basic types (strings etc.)
Params.ContextNode:=CursorNode;
Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
CurrentIdentifierList.Context:=GatherContext;
FindIdentifierInBasicTypeHelpers(ExprType.Desc, Params);
end;
// check for procedure/method declaration context
CheckProcedureDeclarationContext;
// add useful identifiers
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers G');
{$ENDIF}
GatherUsefulIdentifiers(IdentStartPos,CursorContext,GatherContext);
end;
// search and gather identifiers in context
if (GatherContext.Tool<>nil) and (GatherContext.Node<>nil) then begin
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers D CONTEXT: ',
GatherContext.Tool.MainFilename,
' ',GatherContext.Node.DescAsString,
' "',StringToPascalConst(copy(GatherContext.Tool.Src,GatherContext.Node.StartPos,50)),'"');
{$ENDIF}
// gather all identifiers in context
Params.ContextNode:=GatherContext.Node;
Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
if (Params.ContextNode.Desc=ctnInterface) and StartInSubContext then
Include(Params.Flags,fdfIgnoreUsedUnits);
if not StartInSubContext then
Include(Params.Flags,fdfSearchInParentNodes);
if Params.ContextNode.Desc in AllClasses then
Exclude(Params.Flags,fdfSearchInParentNodes);
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers F');
{$ENDIF}
CurrentIdentifierList.Context:=GatherContext;
if GatherContext.Node.Desc=ctnIdentifier then
Params.Flags:=Params.Flags+[fdfIgnoreCurContextNode];
GatherContext.Tool.FindIdentifierInContext(Params);
end else
if ExprType.Desc in xtAllIdentPredefinedTypes then
begin
// gather all identifiers in cursor context for basic types (strings etc.)
Params.ContextNode:=CursorNode;
Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
CurrentIdentifierList.Context:=GatherContext;
FindIdentifierInBasicTypeHelpers(ExprType.Desc, Params);
end;
// check for procedure/method declaration context
CheckProcedureDeclarationContext;
// add useful identifiers
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers G');
{$ENDIF}
GatherUsefulIdentifiers(IdentStartPos,CursorContext,GatherContext);
Result:=true;
finally
FreeListOfPFindContext(FICTClassAndAncestors);
FreeAndNil(FIDCTFoundPublicProperties);
Params.Free;
ClearIgnoreErrorAfter;
end;
Result:=true;
finally
FreeListOfPFindContext(FICTClassAndAncestors);
FreeAndNil(FIDCTFoundPublicProperties);
Params.Free;
ClearIgnoreErrorAfter;
DeactivateGlobalWriteLock;
CurrentIdentifierList:=nil;
end;
@ -2937,43 +2939,44 @@ begin
CurrentIdentifierContexts:=CodeContexts;
ActivateGlobalWriteLock;
Params:=TFindDeclarationParams.Create;//FindHelpersInContext called later
try
InitCollectIdentifiers(CursorPos,IdentifierList);
ParseSourceTillCollectionStart(CursorPos,CleanCursorPos,CursorNode,
IdentStartPos,IdentEndPos);
Params.ContextNode:=CursorNode;
FindHelpersInContext(Params);
if IdentStartPos=0 then ;
if IdentEndPos=0 then ;
Params:=TFindDeclarationParams.Create(Self, CursorNode);
try
if IdentStartPos=0 then ;
if IdentEndPos=0 then ;
// find class and ancestors if existing (needed for protected identifiers)
FindContextClassAndAncestors(CursorPos,FICTClassAndAncestors);
// find class and ancestors if existing (needed for protected identifiers)
FindContextClassAndAncestors(CursorPos,FICTClassAndAncestors);
if CursorNode<>nil then begin
if not CheckContextIsParameter(Result) then begin
DebugLn(['TIdentCompletionTool.FindCodeContext cursor not at parameter']);
exit;
if CursorNode<>nil then begin
if not CheckContextIsParameter(Result) then begin
DebugLn(['TIdentCompletionTool.FindCodeContext cursor not at parameter']);
exit;
end;
end;
end;
if CurrentIdentifierContexts=nil then begin
// create default
AddCollectionContext(Self,CursorNode);
end;
if CurrentIdentifierContexts=nil then begin
// create default
AddCollectionContext(Self,CursorNode);
end;
Result:=true;
Result:=true;
finally
if Result then begin
CodeContexts:=CurrentIdentifierContexts;
CurrentIdentifierContexts:=nil;
end else begin
FreeAndNil(CurrentIdentifierContexts);
end;
FreeListOfPFindContext(FICTClassAndAncestors);
FreeAndNil(FIDCTFoundPublicProperties);
Params.Free;
ClearIgnoreErrorAfter;
end;
finally
if Result then begin
CodeContexts:=CurrentIdentifierContexts;
CurrentIdentifierContexts:=nil;
end else begin
FreeAndNil(CurrentIdentifierContexts);
end;
FreeListOfPFindContext(FICTClassAndAncestors);
FreeAndNil(FIDCTFoundPublicProperties);
Params.Free;
ClearIgnoreErrorAfter;
DeactivateGlobalWriteLock;
FreeAndNil(CurrentIdentifierList);
end;