| Anonymous | Login | Signup for a new account | 2013-05-21 19:43 CEST | ![]() |
| All Projects | FPC | Lazarus: Packages, Patches | Lazarus CCR | Mantis | fpGUI | fpcprojects: fpprofiler |
| Main | My View | View Issues | Change Log | Roadmap |
| View Issue Details [ Jump to Notes ] | [ Issue History ] [ Print ] | ||||||||||||
| ID | Project | Category | View Status | Date Submitted | Last Update | ||||||||
| 0020623 | Lazarus | IDE | public | 2011-11-04 18:12 | 2013-05-01 16:05 | ||||||||
| Reporter | Alexander Shishkin | ||||||||||||
| Assigned To | Mattias Gaertner | ||||||||||||
| Priority | normal | Severity | minor | Reproducibility | N/A | ||||||||
| Status | feedback | Resolution | open | ||||||||||
| Platform | OS | OS Version | |||||||||||
| Product Version | Product Build | ||||||||||||
| Target Version | Fixed in Version | ||||||||||||
| Summary | 0020623: [Codetools] support for helpres | ||||||||||||
| Description | Codetools can not parse class|record helper syntax so they are unusable if class helpers are defined somewhere in the code. | ||||||||||||
| Tags | No tags attached. | ||||||||||||
| Fixed in Revision | |||||||||||||
| LazTarget | - | ||||||||||||
| Widgetset | |||||||||||||
| Attached Files | Index: components/codetools/codetree.pas
===================================================================
--- components/codetools/codetree.pas (revision 38181)
+++ components/codetools/codetree.pas (working copy)
@@ -166,7 +166,7 @@
AllClassBaseSections+AllClassSubSections;
AllClassInterfaces = [ctnClassInterface,ctnDispinterface,ctnObjCProtocol];
AllClassObjects = [ctnClass,ctnObject,ctnRecordType,
- ctnObjCClass,ctnObjCCategory,ctnCPPClass];
+ ctnObjCClass,ctnObjCCategory,ctnCPPClass,ctnClassHelper];
AllClasses = AllClassObjects+AllClassInterfaces;
AllClassModifiers = [ctnClassAbstract, ctnClassSealed, ctnClassExternal];
AllDefinitionSections =
Index: components/codetools/finddeclarationtool.pas
===================================================================
--- components/codetools/finddeclarationtool.pas (revision 38181)
+++ components/codetools/finddeclarationtool.pas (working copy)
@@ -2268,8 +2268,8 @@
Result += ': ';
end;
case TypeNode.Desc of
- ctnIdentifier, ctnSpecialize, ctnSpecializeType,
- ctnPointertype, ctnRangeType, ctnFileType, ctnclassOfType:
+ ctnIdentifier, ctnSpecialize, ctnSpecializeType, ctnSetType,
+ ctnPointertype, ctnRangeType, ctnFileType, ctnClassOfType:
begin
Result += ExtractNode(TypeNode, [phpCommentsToSpace]);
end;
@@ -2284,6 +2284,20 @@
and (TypeNode.FirstChild.Desc = ctnClassInheritance) then
Result += ExtractNode(TypeNode.FirstChild, []);
end;
+ ctnClassHelper:
+ begin
+ MoveCursorToNodeStart(TypeNode);
+ ReadNextAtom;
+ Result += GetAtom + ' helper '; // 'class/record helper'
+ ANode := TypeNode.FirstChild;
+ if (ANode <> nil)
+ and (ANode.Desc = ctnClassInheritance) then begin
+ Result += ExtractNode(ANode, []); // inheritance
+ ANode := ANode.NextBrother;
+ end;
+ if (ANode <> nil) and (ANode.Desc = ctnClassHelperFor) then
+ Result += ' ' + ExtractNode(ANode, []); // for ..
+ end;
ctnConstant:
begin
NodeStr:=ExtractNode(TypeNode,[phpCommentsToSpace]);
@@ -2879,6 +2893,18 @@
if NameNode=nil then exit;
NameNode:=NameNode.FirstChild;
if NameNode=nil then exit;
+ end else
+ if (ContextNode.Desc=ctnTypeDefinition)
+ and Assigned(ContextNode.FirstChild)
+ and (ContextNode.FirstChild.Desc=ctnClassHelper) then begin
+ // check finding the helper itself
+ if not CompareSrcIdentifiers(ContextNode.StartPos,Params.Identifier) then begin
+ // check 'helper for'
+ NameNode:=FindHelperForNode(ContextNode.FirstChild);
+ if NameNode=nil then exit;
+ NameNode:=NameNode.FirstChild;
+ if NameNode=nil then exit;
+ end;
end;
if (fdfCollect in Params.Flags)
@@ -3184,7 +3210,7 @@
ctnClass, ctnClassInterface, ctnDispinterface, ctnObject,
ctnObjCClass, ctnObjCCategory, ctnObjCProtocol, ctnCPPClass,
- ctnRecordType, ctnRecordCase,
+ ctnRecordType, ctnRecordCase, ctnClassHelper, ctnClassHelperFor,
ctnEnumerationType:
// do not search again in this node, go on ...
;
@@ -3298,7 +3324,7 @@
ctnClassClassVar,
ctnClass, ctnClassInterface, ctnDispinterface, ctnObject,
ctnObjCClass, ctnObjCCategory, ctnObjCProtocol, ctnCPPClass,
- ctnRecordType, ctnRecordVariant,
+ ctnRecordType, ctnRecordVariant, ctnClassHelper,
ctnEnumerationType,
ctnParameterList:
// these nodes build a parent-child relationship. But in pascal
@@ -3379,7 +3405,9 @@
end;
end else begin
- Exclude(Params.Flags,fdfIgnoreCurContextNode);
+ // to allow 'Find Declaration' outside of helper
+ if ContextNode.GetNodeOfType(ctnClassHelper)=nil then
+ Exclude(Params.Flags,fdfIgnoreCurContextNode);
{$IFDEF ShowTriedContexts}
DebugLn('[TFindDeclarationTool.FindIdentifierInContext] IgnoreCurContext ');
{$ENDIF}
@@ -5554,7 +5582,7 @@
if (IdentifierNode=nil)
or (not (IdentifierNode.Desc in [ctnIdentifier,ctnSpecialize]))
or (IdentifierNode.Parent=nil)
- or (IdentifierNode.Parent.Desc<>ctnClassInheritance)
+ or not (IdentifierNode.Parent.Desc in [ctnClassInheritance,ctnClassHelperFor])
then
RaiseException('[TFindDeclarationTool.FindAncestorOfClass] '
+' not an inheritance node');
@@ -6283,7 +6311,16 @@
ParentNode,ParentNode.StartPos);
end;
end;
- { TODO : class helpers }
+ ctnClassHelper:
+ begin
+ Node:=FindHelperForNode(Node);
+ if Assigned(Node) then begin
+ Node:=Node.FirstChild;
+ if Assigned(Node) then
+ FInterfaceIdentifierCache.Add(@Src[Node.StartPos],
+ ParentNode,ParentNode.StartPos);
+ end;
+ end;
end;
end;
Index: components/codetools/methodjumptool.pas
===================================================================
--- components/codetools/methodjumptool.pas (revision 38181)
+++ components/codetools/methodjumptool.pas (working copy)
@@ -353,7 +353,7 @@
ClassNode:=CursorNode.GetNodeOfTypes([ctnClass,ctnClassInterface,
ctnDispinterface,ctnObject,ctnRecordType,
ctnObjCClass,ctnObjCCategory,ctnObjCProtocol,
- ctnCPPClass]);
+ ctnCPPClass,ctnClassHelper]);
if ClassNode<>nil then begin
// cursor is in class/object/interface definition
// Interfaces have no method bodies, but if the class was refactored it has
Index: components/codetools/pascalparsertool.pas
===================================================================
--- components/codetools/pascalparsertool.pas (revision 38181)
+++ components/codetools/pascalparsertool.pas (working copy)
@@ -3963,10 +3963,7 @@
end;
end else if UpAtomIs('HELPER') then begin
IsHelper:=true;
- CreateChildNode;
CurNode.Desc:=ctnClassHelper;
- CurNode.EndPos:=CurPos.EndPos;
- EndChildNode;
ReadNextAtom;
end;
end;
@@ -3981,13 +3978,21 @@
SaveRaiseStringExpectedButAtomFound('for');
CreateChildNode;
CurNode.Desc:=ctnClassHelperFor;
- repeat
+ ReadNextAtom;
+ AtomIsIdentifierSaveE;
+ CreateChildNode;
+ CurNode.Desc:=ctnIdentifier;
+ CurNode.EndPos:=CurPos.EndPos;
+ ReadNextAtom;
+ while CurPos.Flag=cafPoint do begin
ReadNextAtom;
AtomIsIdentifierSaveE;
CurNode.EndPos:=CurPos.EndPos;
ReadNextAtom;
- until CurPos.Flag<>cafPoint;
+ end;
EndChildNode;
+ CurNode.EndPos:=CurNode.LastChild.EndPos;
+ EndChildNode;
end;
end;
if CurPos.Flag=cafSemicolon then begin
Index: components/codetools/pascalreadertool.pas
===================================================================
--- components/codetools/pascalreadertool.pas (revision 38181)
+++ components/codetools/pascalreadertool.pas (working copy)
@@ -176,6 +176,7 @@
function ClassSectionNodeStartsWithWord(ANode: TCodeTreeNode): boolean;
function IsClassNode(Node: TCodeTreeNode): boolean; // class, not object
function FindInheritanceNode(ClassNode: TCodeTreeNode): TCodeTreeNode;
+ function FindHelperForNode(ClassNode: TCodeTreeNode): TCodeTreeNode;
// records
function ExtractRecordCaseType(RecordCaseNode: TCodeTreeNode): string;
@@ -2004,10 +2005,18 @@
Result:=ClassNode.FirstChild;
while (Result<>nil) and (Result.Desc in [ctnClassSealed,ctnClassAbstract,ctnClassExternal]) do
Result:=Result.NextBrother;
- if (Result<>nil) and (Result.Desc<>ctnClassInheritance) then
- Result:=nil;
+ if (Result<>nil)
+ and not (Result.Desc in [ctnClassInheritance,ctnClassHelperFor]) then
+ Result:=nil
end;
+function TPascalReaderTool.FindHelperForNode(ClassNode: TCodeTreeNode): TCodeTreeNode;
+begin
+ Result:=ClassNode.FirstChild;
+ while Assigned(Result) and (Result.Desc<>ctnClassHelperFor) do
+ Result:=Result.NextBrother;
+end;
+
function TPascalReaderTool.ExtractRecordCaseType(RecordCaseNode: TCodeTreeNode): string;
// case a:b.c of
// case a:(b,c) of
Index: ide/sourceeditprocs.pas
===================================================================
--- ide/sourceeditprocs.pas (revision 38181)
+++ ide/sourceeditprocs.pas (working copy)
@@ -411,9 +411,9 @@
s:=' = ';
if (ANode<>nil) then begin
case ANode.Desc of
- ctnClass,ctnObject,ctnObjCClass,ctnObjCCategory,
- ctnCPPClass,
- ctnClassInterface,ctnObjCProtocol,ctnDispinterface:
+ ctnClass,ctnObject,ctnClassHelper,ctnCPPClass,
+ ctnClassInterface,ctnDispinterface,
+ ctnObjCClass,ctnObjCCategory,ctnObjCProtocol:
begin
case ANode.Desc of
ctnClass: s:=s+'class';
@@ -424,6 +424,12 @@
ctnClassInterface: s:=s+'interface';
ctnObjCProtocol: s:=s+'objcprotocol';
ctnDispinterface: s:=s+'dispinterface';
+ ctnClassHelper:
+ with IdentItem.Tool do begin
+ MoveCursorToNodeStart(ANode);
+ ReadNextAtom;
+ s:=s+GetAtom + ' helper ';
+ end;
end;
try
IdentItem.Tool.BuildSubTree(ANode);
@@ -431,8 +437,15 @@
on ECodeToolError do ;
end;
SubNode:=IdentItem.Tool.FindInheritanceNode(ANode);
- if SubNode<>nil then
+ if SubNode<>nil then begin
s:=s+IdentItem.Tool.ExtractNode(SubNode,[]);
+ if (ANode.Desc=ctnClassHelper)
+ and (SubNode.Desc<>ctnClassHelperFor) then begin
+ SubNode:=IdentItem.Tool.FindHelperForNode(ANode);
+ if SubNode<>nil then
+ s:=s+' '+IdentItem.Tool.ExtractNode(SubNode,[]);
+ end;
+ end;
end;
ctnRecordType:
s:=s+'record';
Index: components/codetools/finddeclarationcache.pas
===================================================================
--- components/codetools/finddeclarationcache.pas (revision 38262)
+++ components/codetools/finddeclarationcache.pas (working copy)
@@ -185,6 +185,7 @@
NextTool: TPascalParserTool;
NextCache: TBaseTypeCache; // used for mem manager
Owner: TCodeTreeNode;
+ Helpers: TFPList; // list of available helpers <tool,node>
procedure BindToOwner(NewOwner: TCodeTreeNode);
procedure UnbindFromOwner;
constructor Create(AnOwner: TCodeTreeNode);
@@ -1321,6 +1322,7 @@
destructor TBaseTypeCache.Destroy;
begin
+ Helpers.Free;
UnbindFromOwner;
inherited Destroy;
end;
Index: components/codetools/finddeclarationtool.pas
===================================================================
--- components/codetools/finddeclarationtool.pas (revision 38262)
+++ components/codetools/finddeclarationtool.pas (working copy)
@@ -167,6 +167,7 @@
fdfTopLvlResolving, // set, when searching for an identifier of the
// top lvl variable. Calling DoOnIdentifierFound.
fdfDoNotCache, // result will not be cached
+ fdfSearchInExtendedClass,// for helpers: search also in extended class
fdfExtractOperand, // operand will be extracted
fdfPropertyResolving // used with fdfExtractOperand to resolve properties to getters
);
@@ -455,6 +456,14 @@
FFoundProcStackFirst: PFoundProc;//list of all saved PFoundProc
FFoundProcStackLast: PFoundProc;
FExtractedOperand: string;
+ FAvailableHelpers: TFPList;
+ procedure AddHelper(ATool: TFindDeclarationTool; HelperNode: TCodeTreeNode);
+ procedure AddInterfaceHelpers(Tool: TFindDeclarationTool;
+ HelperList: TFPList; AfterPos: Integer=-1);
+ function FindInHelpers(ATool: TFindDeclarationTool;
+ ClassNode: TCodeTreeNode): boolean;
+ procedure GetHelpers(Cache: TBaseTypeCache);
+ procedure RetrieveHelpers(FromParams: TFindDeclarationParams);
procedure ClearFoundProc;
procedure FreeFoundProc(aFoundProc: PFoundProc; FreeNext: boolean);
procedure RemoveFoundProcFromList(aFoundProc: PFoundProc);
@@ -557,6 +566,7 @@
FAdjustTopLineDueToComment: boolean;
FDirectoryCache: TCTDirectoryCache;
FInterfaceIdentifierCache: TInterfaceIdentifierCache;
+ FInterfaceHelpers: TFPList;
FOnFindUsedUnit: TOnFindUsedUnit;
FOnGetCodeToolForBuffer: TOnGetCodeToolForBuffer;
FOnGetDirectoryCache: TOnGetDirectoryCache;
@@ -606,6 +616,8 @@
Params: TFindDeclarationParams; ErrorPos: integer): boolean;
function FindIdentifierInTypeOfConstant(VarConstNode: TCodeTreeNode;
Params: TFindDeclarationParams): boolean;
+ function FindProcInHelpers(ClassNode: TCodeTreeNode;
+ Params: TFindDeclarationParams): boolean;
protected
WordIsPredefinedIdentifier: TKeyWordFunctionList;
procedure RaiseUsesExpected;
@@ -620,9 +632,10 @@
procedure AddToolDependency(DependOnTool: TFindDeclarationTool);
function CreateNewNodeCache(Node: TCodeTreeNode): TCodeTreeNodeCache;
function CreateNewBaseTypeCache(Tool: TFindDeclarationTool;
- Node: TCodeTreeNode): TBaseTypeCache;
+ Node: TCodeTreeNode;
+ listOfHelpers: TFPList): TBaseTypeCache;
procedure CreateBaseTypeCaches(NodeStack: PCodeTreeNodeStack;
- const Result: TFindContext);
+ const Result: TFindContext; listOfHelpers: TFPList);
function GetNodeCache(Node: TCodeTreeNode;
CreateIfNotExists: boolean): TCodeTreeNodeCache;
procedure AddResultToNodeCaches(
@@ -687,7 +700,8 @@
function FindContextNodeAtCursor(
Params: TFindDeclarationParams): TFindContext;
function FindClassOfMethod(ProcNode: TCodeTreeNode;
- FindClassContext, ExceptionOnNotFound: boolean): TCodeTreeNode;
+ FindClassContext, ExceptionOnNotFound: boolean;
+ Params: TFindDeclarationParams): TCodeTreeNode;
function FindClassMember(aClassNode: TCodeTreeNode; Identifier: PChar): TCodeTreeNode;
function FindForwardIdentifier(Params: TFindDeclarationParams;
var IsForward: boolean): boolean;
@@ -2251,8 +2265,8 @@
Result += ': ';
end;
case TypeNode.Desc of
- ctnIdentifier, ctnSpecialize, ctnSpecializeType,
- ctnPointertype, ctnRangeType, ctnFileType, ctnclassOfType:
+ ctnIdentifier, ctnSpecialize, ctnSpecializeType, ctnSetType,
+ ctnPointertype, ctnRangeType, ctnFileType, ctnClassOfType:
begin
Result += ExtractNode(TypeNode, [phpCommentsToSpace]);
end;
@@ -2263,9 +2277,17 @@
MoveCursorToNodeStart(TypeNode);
ReadNextAtom;
Result+=GetAtom;
- if (TypeNode.FirstChild<>nil)
- and (TypeNode.FirstChild.Desc = ctnClassInheritance) then
- Result += ExtractNode(TypeNode.FirstChild, []);
+ ANode:=TypeNode.FirstChild;
+ if (ANode<>nil) and (ANode.Desc=ctnClassHelper) then begin
+ Result+=' helper';
+ ANode:=ANode.NextBrother;
+ end;
+ if (ANode<>nil) and (ANode.Desc=ctnClassInheritance) then begin
+ Result+=ExtractNode(ANode, []);
+ ANode:=ANode.NextBrother;
+ end;
+ if (ANode<>nil) and (ANode.Desc=ctnClassHelperFor) then
+ Result+=' '+ExtractNode(ANode, []);
end;
ctnConstant:
begin
@@ -2861,6 +2883,16 @@
if NameNode=nil then exit;
NameNode:=NameNode.FirstChild;
if NameNode=nil then exit;
+ end else
+ if (ContextNode.Desc=ctnTypeDefinition)
+ and Assigned(ContextNode.FirstChild)
+ and (ContextNode.FirstChild.Desc in [ctnClass,ctnRecordType])
+ and Assigned(ContextNode.FirstChild.FirstChild)
+ and (ContextNode.FirstChild.FirstChild.Desc=ctnClassHelper) then begin
+ // check finding the helper itself
+ if not CompareSrcIdentifiers(NameNode.StartPos,Params.Identifier) then begin
+ Params.AddHelper(Self,ContextNode.FirstChild);
+ end;
end;
if (fdfCollect in Params.Flags)
@@ -3064,7 +3096,10 @@
end;
end;
- if (ContextNode.Desc in (AllClasses-[ctnRecordType]))
+ if ((ContextNode.Desc in (AllClasses-[ctnRecordType]))
+ or ((ContextNode.Desc=ctnRecordType)
+ and (ContextNode.FirstChild<>nil)
+ and (ContextNode.FirstChild.Desc=ctnClassHelper)))
and (fdfSearchInAncestors in Params.Flags) then begin
// after searching in a class definition, search in its ancestors
@@ -3278,9 +3313,9 @@
ctnProgram, ctnLibrary,
ctnClassPublic, ctnClassPrivate, ctnClassProtected, ctnClassPublished,
ctnClassClassVar,
- ctnClass, ctnClassInterface, ctnDispinterface, ctnObject,
+ ctnClassInterface, ctnDispinterface, ctnObject,
ctnObjCClass, ctnObjCCategory, ctnObjCProtocol, ctnCPPClass,
- ctnRecordType, ctnRecordVariant,
+ ctnRecordVariant,
ctnEnumerationType,
ctnParameterList:
// these nodes build a parent-child relationship. But in pascal
@@ -3288,6 +3323,16 @@
// -> search in all children
MoveContextNodeToChildren;
+ ctnClass,ctnRecordType:
+ begin
+ if Assigned(ContextNode.FirstChild)
+ and (ContextNode.FirstChild.Desc<>ctnClassHelper)
+ and FindProcInHelpers(ContextNode,Params) then begin
+ if CheckResult(true,true) then exit;
+ end;
+ MoveContextNodeToChildren;
+ end;
+
ctnTypeDefinition, ctnVarDefinition, ctnConstDefinition,
ctnGenericType, ctnGlobalProperty:
if SearchInTypeVarConstGlobPropDefinition then exit;
@@ -3544,6 +3589,7 @@
SubParams:=TFindDeclarationParams.Create;
try
SubParams.GenParams := Params.GenParams;
+ SubParams.RetrieveHelpers(Params);
IdentStart:=CleanPos;
{$IFDEF ShowTriedBaseContexts}
debugln(['TFindDeclarationTool.FindBaseTypeOfNode.SearchIdentifier Identifier=',GetIdentifier(@Src[IdentStart])]);
@@ -3563,6 +3609,7 @@
// skip search in proc parameters
SubParams.ContextNode:=SubParams.ContextNode.Parent;
TypeFound:=FindIdentifierInContext(SubParams);
+ Params.RetrieveHelpers(SubParams);
if TypeFound and (SubParams.NewNode.Desc=ctnUseUnit)
and (SubParams.NewCodeTool=Self)
then begin
@@ -3726,6 +3773,7 @@
if (Node<>nil) and (Node.Cache is TBaseTypeCache) then begin
// base type already cached
Result:=CreateFindContext(TBaseTypeCache(Node.Cache));
+ Params.GetHelpers(TBaseTypeCache(Node.Cache));
CheckResult(Result);
exit;
end;
@@ -3745,6 +3793,7 @@
if NodeStack^.StackPtr>=0 then
AddNodeToStack(NodeStack,Result.Tool,Result.Node);
Result:=CreateFindContext(TBaseTypeCache(Result.Node.Cache));
+ Params.GetHelpers(TBaseTypeCache(Result.Node.Cache));
break;
end;
{$IFDEF ShowTriedBaseContexts}
@@ -3942,7 +3991,7 @@
// cache the result in all nodes
// do not cache the result of generic type
if not Assigned(Params.GenParams.ParamValuesTool) then
- CreateBaseTypeCaches(NodeStack,Result);
+ CreateBaseTypeCaches(NodeStack,Result,Params.FAvailableHelpers);
// free node stack
FinalizeNodeStack(NodeStack);
end;
@@ -4328,6 +4377,10 @@
debugln(['TFindDeclarationTool.FindDefaultAncestorOfClass not a type']);
exit;
end;
+ if (ClassNode.Desc=ctnClass) and (ClassNode.FirstChild<>nil)
+ and (ClassNode.FirstChild.Desc=ctnClassHelper) then begin
+ exit; // ther is no default ancestor for 'class helpers'
+ end;
BaseClassName:=nil;
if ClassNode.Desc=ctnClass then begin
if Scanner.Values.IsDefined('CPUJVM') then
@@ -5285,12 +5338,18 @@
end else begin
// search the identifier in the class first
// 1. search the class in the same unit
- CurClassNode:=FindClassOfMethod(ProcContextNode,true,true);
+ CurClassNode:=FindClassOfMethod(ProcContextNode,true,true,Params);
// 2. -> search identifier in class
Params.Save(OldInput);
Params.Flags:=[fdfSearchInAncestors]
+(fdfGlobalsSameIdent*Params.Flags)
-[fdfExceptionOnNotFound];
+ if (CurClassNode.Desc in [ctnClass,ctnRecordType])
+ and (CurClassNode.FirstChild<>nil)
+ and (CurClassNode.FirstChild.Desc=ctnClassHelper) then begin
+ // the searching was started in 'helper' so we should search in extended class too
+ Include(Params.Flags,fdfSearchInExtendedClass);
+ end;
Params.ContextNode:=CurClassNode;
{$IFDEF ShowTriedContexts}
DebugLn('[TFindDeclarationTool.FindIdentifierInClassOfMethod] searching identifier in class of method Identifier=',GetIdentifier(Params.Identifier));
@@ -5320,7 +5379,8 @@
end;
function TFindDeclarationTool.FindClassOfMethod(ProcNode: TCodeTreeNode;
- FindClassContext, ExceptionOnNotFound: boolean): TCodeTreeNode;
+ FindClassContext, ExceptionOnNotFound: boolean;
+ Params: TFindDeclarationParams): TCodeTreeNode;
var
ClassNameAtom: TAtomPosition;
Node: TCodeTreeNode;
@@ -5391,6 +5451,11 @@
{$IFDEF ShowTriedIdentifiers}
debugln(['TFindDeclarationTool.FindClassOfMethod ',TypeNode.DescAsString,' ',dbgstr(ExtractNode(TypeNode,[]),1,40)]);
{$ENDIF}
+ if (TypeNode.Desc=ctnTypeDefinition) and (TypeNode.FirstChild<>nil)
+ and (TypeNode.FirstChild.Desc in [ctnClass,ctnRecordType])
+ and (TypeNode.FirstChild.FirstChild<>nil)
+ and (TypeNode.FirstChild.FirstChild.Desc=ctnClassHelper) then
+ Params.AddHelper(Self,TypeNode.FirstChild);
if ((TypeNode.Desc=ctnTypeDefinition)
and (CompareIdentifierPtrs(CurClassName,@Src[TypeNode.StartPos])=0))
or ((TypeNode.Desc=ctnGenericType)
@@ -5548,7 +5613,7 @@
if (IdentifierNode=nil)
or (not (IdentifierNode.Desc in [ctnIdentifier,ctnSpecialize]))
or (IdentifierNode.Parent=nil)
- or (IdentifierNode.Parent.Desc<>ctnClassInheritance)
+ or not (IdentifierNode.Parent.Desc in [ctnClassInheritance,ctnClassHelperFor])
then
RaiseException('[TFindDeclarationTool.FindAncestorOfClass] '
+' not an inheritance node');
@@ -5809,6 +5874,8 @@
SearchDefaultAncestor:=true;
InheritanceNode:=FindInheritanceNode(ClassNode);
+ if (InheritanceNode=nil) and (fdfSearchInExtendedClass in Params.Flags) then
+ InheritanceNode:=FindHelperForNode(ClassNode);
if (InheritanceNode<>nil) then begin
Node:=InheritanceNode.FirstChild;
while Node<>nil do begin
@@ -6231,8 +6298,11 @@
end;
end else begin
CacheEntry:=FInterfaceIdentifierCache.FindIdentifier(Params.Identifier);
- if CacheEntry=nil then
+ if CacheEntry=nil then begin
+ Params.AddInterfaceHelpers(Self,FInterfaceHelpers);
exit(false);
+ end;
+ Params.AddInterfaceHelpers(Self,FInterfaceHelpers,CacheEntry^.Node.StartPos);
case CheckEntry(CacheEntry) of
ifrSuccess: exit(true);
ifrAbortSearch: exit(false);
@@ -6283,7 +6353,12 @@
ParentNode,ParentNode.StartPos);
end;
end;
- { TODO : class helpers }
+ ctnClass,ctnRecordType:
+ begin
+ if Assigned(Node.FirstChild)
+ and (Node.FirstChild.Desc=ctnClassHelper) then
+ FInterfaceHelpers.Add(Node);
+ end;
end;
end;
@@ -6512,6 +6587,12 @@
end;
end;
+function TFindDeclarationTool.FindProcInHelpers(ClassNode: TCodeTreeNode;
+ Params: TFindDeclarationParams): boolean;
+begin
+ Result:=Params.FindInHelpers(Self,ClassNode);
+end;
+
procedure TFindDeclarationTool.RaiseUsesExpected;
begin
RaiseExceptionFmt(ctsStrExpectedButAtomFound,['"uses"',GetAtom]);
@@ -7018,7 +7099,7 @@
if (ProcNode.Desc=ctnProcedure) and NodeIsMethodBody(ProcNode) then
begin
ResultNode:=FindClassOfMethod(ProcNode,not IsEnd,
- fdfExceptionOnNotFound in Params.Flags);
+ fdfExceptionOnNotFound in Params.Flags,Params);
ExprType.Desc:=xtContext;
ExprType.Context.Tool:=Self;
ExprType.Context.Node:=ResultNode;
@@ -7545,7 +7626,7 @@
{$ENDIF}
// find class of method
- ClassNodeOfMethod:=FindClassOfMethod(ProcNode,true,true);
+ ClassNodeOfMethod:=FindClassOfMethod(ProcNode,true,true,Params);
// find class ancestor
OldInput.Flags:=Params.Flags;
@@ -9298,6 +9379,7 @@
FSourcesChangeStep:=CTInvalidChangeStamp64;
FFilesChangeStep:=CTInvalidChangeStamp64;
FInitValuesChangeStep:=CTInvalidChangeStamp;
+ FInterfaceHelpers:=TFPList.Create;
end;
procedure TFindDeclarationTool.DoDeleteNodes(StartNode: TCodeTreeNode);
@@ -9372,6 +9454,7 @@
destructor TFindDeclarationTool.Destroy;
begin
+ FInterfaceHelpers.Free;
FInterfaceIdentifierCache.Free;
FInterfaceIdentifierCache:=nil;
FDependsOnCodeTools.Free;
@@ -9699,7 +9782,11 @@
// start with parent of deepest node and end parent of highest
Node:=StartNode;
repeat
- if (Node.Desc in AllNodeCacheDescs) then begin
+ if (Node.Desc in AllNodeCacheDescs)
+ and ((Node.Desc<>ctnClass)
+ or (Node.FirstChild=nil)
+ or (Node.FirstChild.Desc<>ctnClassHelper)
+ or (fdfSearchInExtendedClass in Params.Flags)) then begin
if (Node.Cache=nil) then
CreateNewNodeCache(Node);
if (Node.Cache is TCodeTreeNodeCache) then begin
@@ -9740,17 +9827,26 @@
FFirstNodeCache:=Result;
end;
-function TFindDeclarationTool.CreateNewBaseTypeCache(
- Tool: TFindDeclarationTool; Node: TCodeTreeNode): TBaseTypeCache;
+function TFindDeclarationTool.CreateNewBaseTypeCache(Tool: TFindDeclarationTool;
+ Node: TCodeTreeNode; listOfHelpers: TFPList): TBaseTypeCache;
+var i: Integer;
begin
{$IFDEF CheckNodeTool}Tool.CheckNodeTool(Node);{$ENDIF}
Result:=BaseTypeCacheMemManager.NewBaseTypeCache(Node);
Result.NextCache:=Tool.FFirstBaseTypeCache;
+ if listOfHelpers<>nil then begin
+ Result.Helpers:=TFPList.Create;
+ for i:=0 to listOfHelpers.Count-1 do
+ with PFindContext(listOfHelpers[i])^ do begin
+ Result.Helpers.Add(Tool);
+ Result.Helpers.Add(Node);
+ end;
+ end;
Tool.FFirstBaseTypeCache:=Result;
end;
-procedure TFindDeclarationTool.CreateBaseTypeCaches(
- NodeStack: PCodeTreeNodeStack; const Result: TFindContext);
+procedure TFindDeclarationTool.CreateBaseTypeCaches(NodeStack: PCodeTreeNodeStack;
+ const Result: TFindContext; listOfHelpers: TFPList);
var i: integer;
Entry: PCodeTreeNodeStackEntry;
BaseTypeCache: TBaseTypeCache;
@@ -9776,7 +9872,8 @@
DebugLn(' i=',DbgS(i),' Node=',Entry^.Node.DescAsString,' "',copy(Entry^.Tool.Src,Entry^.Node.StartPos,15),'"');
{$ENDIF}
BaseTypeCache:=
- CreateNewBaseTypeCache(TFindDeclarationTool(Entry^.Tool),Entry^.Node);
+ CreateNewBaseTypeCache(TFindDeclarationTool(Entry^.Tool),Entry^.Node,
+ listOfHelpers);
if BaseTypeCache<>nil then begin
BaseTypeCache.BaseNode:=Result.Node;
BaseTypeCache.BaseTool:=Result.Tool;
@@ -10731,6 +10828,110 @@
{ TFindDeclarationParams }
+procedure TFindDeclarationParams.AddHelper(ATool: TFindDeclarationTool;
+ HelperNode: TCodeTreeNode);
+var
+ i: integer;
+ toRemove, inheritanceNode: TCodeTreeNode;
+begin
+ if Assigned(FAvailableHelpers) then begin
+ // 1. do not add new helper if already there is a descendant of it
+ // 2. remove helper ancestor before addition new one
+ toRemove:=ATool.FindInheritanceNode(HelperNode);
+ if Assigned(toRemove) then
+ toRemove:=toRemove.FirstChild;
+ for i:=0 to FAvailableHelpers.Count-1 do
+ with PFindContext(FAvailableHelpers[i])^ do begin
+ if (ATool=Tool) and (HelperNode=Node) then
+ exit; // this helper already exists in list
+ if Assigned(toRemove) and Assigned(Node.Parent)
+ and (CompareIdentifiers(@Tool.Src[Node.Parent.StartPos],
+ @ATool.Src[toRemove.StartPos])=0) then begin
+ // replace current helper with new descendant
+ Tool:=ATool;
+ Node:=HelperNode;
+ exit;
+ end;
+ inheritanceNode:=Tool.FindInheritanceNode(Node);
+ if Assigned(inheritanceNode) then
+ inheritanceNode:=inheritanceNode.FirstChild;
+ if Assigned(inheritanceNode) and Assigned(HelperNode.Parent)
+ and (CompareIdentifiers(@Tool.Src[inheritanceNode.StartPos],
+ @ATool.Src[HelperNode.Parent.StartPos])=0) then
+ // there is already the descendant of new helper, so skip it
+ exit;
+ end;
+ end;
+ AddFindContext(FAvailableHelpers,CreateFindContext(ATool,HelperNode));
+end;
+
+procedure TFindDeclarationParams.AddInterfaceHelpers(Tool: TFindDeclarationTool;
+ HelperList: TFPList; AfterPos: Integer=-1);
+var i: Integer;
+begin
+ for i:=0 to HelperList.Count-1 do
+ with TCodeTreeNode(HelperList[i]) do
+ if StartPos>AfterPos then
+ AddHelper(Tool,TCodeTreeNode(HelperList[i]));
+end;
+
+function TFindDeclarationParams.FindInHelpers(ATool: TFindDeclarationTool;
+ ClassNode: TCodeTreeNode): boolean;
+var
+ OldInput: TFindDeclarationInput;
+ HelperFor: TCodeTreeNode;
+ i: Integer;
+begin
+ Result:=false;
+ if (fdfSearchInExtendedClass in Flags) // we started in helper => do not search in helpers
+ or (FAvailableHelpers=nil)
+ or (FAvailableHelpers.Count=0)
+ or (ClassNode.Parent=nil) then
+ exit;
+ Save(OldInput);
+ for i:=0 to FAvailableHelpers.Count-1 do begin
+ with PFindContext(FAvailableHelpers[i])^ do begin
+ if Node=nil then Continue;
+ HelperFor:=Tool.FindHelperForNode(Node);
+ if (HelperFor=nil) or (HelperFor.FirstChild=nil) then Continue;
+ with HelperFor.FirstChild do
+ if CompareIdentifiers(@Tool.Src[StartPos],
+ @ATool.Src[ClassNode.Parent.StartPos])<>0 then
+ Continue;
+ ContextNode:=Node;
+ IdentifierTool:=Tool;
+ Flags:=Flags-[fdfExceptionOnNotFound,fdfSearchInParentNodes]
+ +[fdfIgnoreUsedUnits];
+ FoundProc:=nil;
+ if Tool.FindIdentifierInContext(Self) then begin
+ Result:=true;
+ Break;
+ end;
+ end;
+ end;
+ Load(OldInput,true);
+end;
+
+procedure TFindDeclarationParams.GetHelpers(Cache: TBaseTypeCache);
+var i: integer;
+begin
+ if Cache.Helpers=nil then exit;
+ for i:=0 to (Cache.Helpers.Count div 2)-1 do
+ AddHelper(TFindDeclarationTool(Cache.Helpers[2*i]),
+ TCodeTreeNode(Cache.Helpers[2*i+1]));
+end;
+
+procedure TFindDeclarationParams.RetrieveHelpers(FromParams: TFindDeclarationParams);
+begin
+ if FAvailableHelpers=nil then begin
+ FAvailableHelpers:=FromParams.FAvailableHelpers;
+ FromParams.FAvailableHelpers:=nil;
+ end else begin
+ FAvailableHelpers.AddList(FromParams.FAvailableHelpers);
+ FromParams.FAvailableHelpers.Clear;
+ end;
+end;
+
procedure TFindDeclarationParams.ClearFoundProc;
begin
if FoundProc=nil then exit;
@@ -10810,6 +11011,7 @@
begin
Clear;
FreeFoundProc(FFoundProcStackFirst,true);
+ FreeListOfPFindContext(FAvailableHelpers);
inherited Destroy;
end;
Index: components/codetools/identcompletiontool.pas
===================================================================
--- components/codetools/identcompletiontool.pas (revision 38262)
+++ components/codetools/identcompletiontool.pas (working copy)
@@ -342,7 +342,7 @@
private
FLastGatheredIdentParent: TCodeTreeNode;
FLastGatheredIdentLevel: integer;
- FICTClassAndAncestors: TFPList;// list of PCodeXYPosition
+ FICTClassAndAncestors: TFPList;// list of PFindContext
FIDCTFoundPublicProperties: TAVLTree;// tree of PChar (pointing to the
// property names in source)
FIDTFoundMethods: TAVLTree;// tree of TCodeTreeNodeExtension Txt=clean text
Index: components/codetools/pascalparsertool.pas
===================================================================
--- components/codetools/pascalparsertool.pas (revision 38262)
+++ components/codetools/pascalparsertool.pas (working copy)
@@ -3961,7 +3961,9 @@
CurNode.EndPos:=CurPos.StartPos;
EndChildNode;
end;
- end else if UpAtomIs('HELPER') then begin
+ end else if UpAtomIs('HELPER') and ((ClassNode.Desc=ctnClass)
+ or (ClassNode.Desc=ctnRecordType)
+ and (cmsAdvancedRecords in Scanner.CompilerModeSwitches)) then begin
IsHelper:=true;
CreateChildNode;
CurNode.Desc:=ctnClassHelper;
@@ -3981,13 +3983,21 @@
SaveRaiseStringExpectedButAtomFound('for');
CreateChildNode;
CurNode.Desc:=ctnClassHelperFor;
- repeat
+ ReadNextAtom;
+ AtomIsIdentifierSaveE;
+ CreateChildNode;
+ CurNode.Desc:=ctnIdentifier; // ctnClassHelperFor contains as a child ctnIdentifier node
+ CurNode.EndPos:=CurPos.EndPos;
+ ReadNextAtom;
+ while CurPos.Flag=cafPoint do begin
ReadNextAtom;
AtomIsIdentifierSaveE;
CurNode.EndPos:=CurPos.EndPos;
ReadNextAtom;
- until CurPos.Flag<>cafPoint;
+ end;
EndChildNode;
+ CurNode.EndPos:=CurNode.LastChild.EndPos;
+ EndChildNode;
end;
end;
if CurPos.Flag=cafSemicolon then begin
Index: components/codetools/pascalreadertool.pas
===================================================================
--- components/codetools/pascalreadertool.pas (revision 38262)
+++ components/codetools/pascalreadertool.pas (working copy)
@@ -176,6 +176,7 @@
function ClassSectionNodeStartsWithWord(ANode: TCodeTreeNode): boolean;
function IsClassNode(Node: TCodeTreeNode): boolean; // class, not object
function FindInheritanceNode(ClassNode: TCodeTreeNode): TCodeTreeNode;
+ function FindHelperForNode(HelperNode: TCodeTreeNode): TCodeTreeNode;
// records
function ExtractRecordCaseType(RecordCaseNode: TCodeTreeNode): string;
@@ -2016,12 +2017,20 @@
function TPascalReaderTool.FindInheritanceNode(ClassNode: TCodeTreeNode): TCodeTreeNode;
begin
Result:=ClassNode.FirstChild;
- while (Result<>nil) and (Result.Desc in [ctnClassSealed,ctnClassAbstract,ctnClassExternal]) do
+ while (Result<>nil)
+ and (Result.Desc in [ctnClassSealed,ctnClassAbstract,ctnClassExternal,ctnClassHelper]) do
Result:=Result.NextBrother;
if (Result<>nil) and (Result.Desc<>ctnClassInheritance) then
Result:=nil;
end;
+function TPascalReaderTool.FindHelperForNode(HelperNode: TCodeTreeNode): TCodeTreeNode;
+begin
+ Result:=HelperNode.FirstChild;
+ while Assigned(Result) and (Result.Desc<>ctnClassHelperFor) do
+ Result:=Result.NextBrother;
+end;
+
function TPascalReaderTool.ExtractRecordCaseType(RecordCaseNode: TCodeTreeNode): string;
// case a:b.c of
// case a:(b,c) of
Index: ide/sourceeditprocs.pas
===================================================================
--- ide/sourceeditprocs.pas (revision 38262)
+++ ide/sourceeditprocs.pas (working copy)
@@ -411,11 +411,12 @@
s:=' = ';
if (ANode<>nil) then begin
case ANode.Desc of
- ctnClass,ctnObject,ctnObjCClass,ctnObjCCategory,
- ctnCPPClass,
- ctnClassInterface,ctnObjCProtocol,ctnDispinterface:
+ ctnClass,ctnObject,ctnRecordType,ctnCPPClass,
+ ctnClassInterface,ctnDispinterface,
+ ctnObjCClass,ctnObjCCategory,ctnObjCProtocol:
begin
case ANode.Desc of
+ ctnRecordType: s:=s+'record';
ctnClass: s:=s+'class';
ctnObject: s:=s+'object';
ctnObjCClass: s:=s+'objcclass';
@@ -430,12 +431,18 @@
except
on ECodeToolError do ;
end;
- SubNode:=IdentItem.Tool.FindInheritanceNode(ANode);
- if SubNode<>nil then
+ SubNode:=ANode.FirstChild;
+ if (SubNode<>nil) and (SubNode.Desc=ctnClassHelper) then begin
+ s:=s+' helper';
+ SubNode:=SubNode.NextBrother;
+ end;
+ if (SubNode<>nil) and (SubNode.Desc=ctnClassInheritance) then begin
s:=s+IdentItem.Tool.ExtractNode(SubNode,[]);
+ SubNode:=SubNode.NextBrother;
+ end;
+ if (SubNode<>nil) and (SubNode.Desc=ctnClassHelperFor) then
+ s:=s+' '+IdentItem.Tool.ExtractNode(SubNode,[]);
end;
- ctnRecordType:
- s:=s+'record';
else
s:=s+IdentItem.Tool.ExtractNode(ANode,[]);
end;
| ||||||||||||
Relationships |
|||||||||||
|
|||||||||||
Notes |
|
|
(0053808) Mattias Gaertner (manager) 2011-11-04 22:20 |
Can you provide an example? Maybe the FPC tests have some. |
|
(0053809) Alexander Shishkin (reporter) 2011-11-04 22:41 |
Yes FPC has some tests. f.e. program tchlp25; {$mode delphi} type TObjectHelper = class helper for TObject end; TTest = class end; TTestHelper = class helper(TObjectHelper) for TTest end; begin end. program trhlp23; {$ifdef fpc} {$mode objfpc} {$modeswitch advancedrecords} {$endif} type TTest = record end; TTestHelper = record helper for TTest end; TTestHelperSub = record helper(TTestHelper) for TTest end; begin end. |
|
(0056179) Mattias Gaertner (manager) 2012-01-31 03:28 |
Parsing now works. |
|
(0056189) Sven Barth (manager) 2012-01-31 11:56 |
@Mattias: The FPC tests are named thlp*.pp, tchlp*.pp and trhlp*.pp in the tests\test directory (just in case). Regards, Sven |
|
(0061701) Anton (reporter) 2012-08-18 19:14 |
ignore class_helpers.patch, review class_helpers_final.patch |
|
(0067327) Juha Manninen (developer) 2013-05-01 15:55 edited on: 2013-05-01 16:05 |
I am going through patches in reports. Anton, I guess your patch is not valid any more. One chunk failed and other chunks succeeded with huge offsets. Mattias has improved parsing without patches. Code completion works pretty well, too. |
Issue History |
|||
| Date Modified | Username | Field | Change |
| 2011-11-04 18:12 | Alexander Shishkin | New Issue | |
| 2011-11-04 22:20 | Mattias Gaertner | Note Added: 0053808 | |
| 2011-11-04 22:20 | Mattias Gaertner | Status | new => assigned |
| 2011-11-04 22:20 | Mattias Gaertner | Assigned To | => Mattias Gaertner |
| 2011-11-04 22:41 | Alexander Shishkin | Note Added: 0053809 | |
| 2012-01-31 01:35 | Mattias Gaertner | Relationship added | has duplicate 0020179 |
| 2012-01-31 03:28 | Mattias Gaertner | Note Added: 0056179 | |
| 2012-01-31 11:56 | Sven Barth | Note Added: 0056189 | |
| 2012-08-06 22:47 | Anton | File Added: class_helpers.patch | |
| 2012-08-18 19:14 | Anton | File Added: class_helpers_final.patch | |
| 2012-08-18 19:14 | Anton | Note Added: 0061701 | |
| 2013-05-01 15:22 | Juha Manninen | Relationship added | related to 0024164 |
| 2013-05-01 15:55 | Juha Manninen | Note Added: 0067327 | |
| 2013-05-01 15:55 | Juha Manninen | LazTarget | => - |
| 2013-05-01 15:55 | Juha Manninen | Status | assigned => feedback |
| 2013-05-01 15:56 | Juha Manninen | Note Edited: 0067327 | View Revisions |
| 2013-05-01 16:05 | Juha Manninen | Note Edited: 0067327 | View Revisions |
| Main | My View | View Issues | Change Log | Roadmap |



