1 {
2  ***************************************************************************
3  *                                                                         *
4  *   This source is free software; you can redistribute it and/or modify   *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This code is distributed in the hope that it will be useful, but      *
10  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
12  *   General Public License for more details.                              *
13  *                                                                         *
14  *   A copy of the GNU General Public License is available on the World    *
15  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
16  *   obtain it by writing to the Free Software Foundation,                 *
17  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
18  *                                                                         *
19  ***************************************************************************
20 
21   Author: Mattias Gaertner
22 
23   Abstract:
24     TIdentCompletionTool enhances the TFindDeclarationTool with the ability
25     to create lists of valid identifiers at a specific code position.
26 }
27 unit IdentCompletionTool;
28 
29 {$mode objfpc}{$H+}
30 
31 interface
32 
33 {$I codetools.inc}
34 
35 // activate for debug:
36 
37 // mem check
38 { $DEFINE MEM_CHECK}
39 
40 // verbosity
41 { $DEFINE CTDEBUG}
42 { $DEFINE ShowFoundIdents}
43 { $DEFINE ShowFilteredIdents}
44 { $DEFINE ShowHistory}
45 { $DEFINE VerboseCodeContext}
46 { $DEFINE VerboseICGatherUnitNames}
47 { $DEFINE VerboseICGatherKeywords}
48 
49 uses
50   {$IFDEF MEM_CHECK}
51   MemCheck,
52   {$ENDIF}
53   Classes, SysUtils, typinfo, crc, Laz_AVL_Tree,
54   // LazUtils
55   LazFileUtils, LazDbgLog, AvgLvlTree,
56   // Codetools
57   FileProcs, CodeTree, CodeAtom, CodeCache, CustomCodeTool, CodeToolsStrConsts,
58   KeywordFuncLists, BasicCodeTools, LinkScanner, SourceChanger,
59   FindDeclarationTool, PascalReaderTool, PascalParserTool, ExprEval;
60 
61 type
62   TIdentCompletionTool = class;
63   TIdentifierHistoryList = class;
64 
65   //----------------------------------------------------------------------------
66   // gathered identifier list
67 
68   TIdentifierCompatibility = (
69     icompExact,
70     icompCompatible,
71     icompUnknown,
72     icompIncompatible
73     );
74   TIdentifierCompatibilities = set of TIdentifierCompatibility;
75 
76   TIdentListItemFlag = (
77     iliHasChilds,
78     iliBaseExprTypeValid,
79     iliIsFunction,
iliIsFunctionValidnull80     iliIsFunctionValid,
81     iliIsAbstractMethod,
82     iliIsAbstractMethodValid,
83     iliParamTypeListValid,
84     iliParamNameListValid,
85     iliNodeValid,
86     iliNodeHashValid,
87     iliNodeGoneWarned,
88     iliIsConstructor,
89     iliIsConstructorValid,
90     iliIsDestructor,
91     iliIsDestructorValid,
92     iliKeyword,
93     iliResultTypeValid,
94     iliHasIndexValid,
95     iliHasIndex,
96     iliHasParamListValid,
97     iliHasParamList,
98     iliIsReadOnlyValid,
99     iliIsReadOnly,
100     iliHintModifiersValid,
101     iliIsDeprecated,
102     iliIsPlatform,
103     iliIsExperimental,
104     iliIsUnimplemented,
105     iliIsLibrary,
106     iliAtCursor, // the item is the identifier at the completion
107     iliNeedsAmpersand, //the item has to be prefixed with '&'
108     iliHasLowerVisibility
109     );
110   TIdentListItemFlags = set of TIdentListItemFlag;
111 
112   { TIdentifierListSearchItem }
113 
114   TIdentifierListSearchItem = class
115   public
116     Identifier: PChar;
117     ParamList: string;
CalcMemSizenull118     function CalcMemSize: PtrUInt;
119   end;
120 
121   TIdentifierList = class;
122 
123   { TIdentifierListItem }
124 
125   TIdentifierListItem = class
126   private
127     FParamTypeList: string;
128     FParamNameList: string;
129     FNode: TCodeTreeNode;
130     FResultType: string;
131     FToolNodesDeletedStep: integer;// only valid if iliNodeValid
132     FNodeStartPos: integer;
133     FNodeDesc: TCodeTreeNodeDesc;
134     FNodeHash: Cardinal;
GetNodenull135     function GetNode: TCodeTreeNode;
GetParamTypeListnull136     function GetParamTypeList: string;
GetParamNameListnull137     function GetParamNameList: string;
138     procedure SetNode(const AValue: TCodeTreeNode);
139     procedure SetParamTypeList(const AValue: string);
140     procedure SetParamNameList(const AValue: string);
141     procedure SetResultType(const AValue: string);
142   public
143     Compatibility: TIdentifierCompatibility;
144     HistoryIndex: integer;
145     Identifier: string;
146     Level: integer;
147     Tool: TFindDeclarationTool;
148     DefaultDesc: TCodeTreeNodeDesc;
149     Flags: TIdentListItemFlags;
150     BaseExprType: TExpressionType;
AsStringnull151     function AsString: string;
152     procedure BeautifyIdentifier({%H-}IdentList: TIdentifierList); virtual;
GetDescnull153     function GetDesc: TCodeTreeNodeDesc;
154     constructor Create(NewCompatibility: TIdentifierCompatibility;
155                        NewHasChilds: boolean; NewHistoryIndex: integer;
156                        NewIdentifier: PChar; NewLevel: integer;
157                        NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool;
158                        NewDefaultDesc: TCodeTreeNodeDesc);
IsProcNodeWithParamsnull159     function IsProcNodeWithParams: boolean;
IsPropertyWithParamsnull160     function IsPropertyWithParams: boolean;
IsPropertyReadOnlynull161     function IsPropertyReadOnly: boolean;
GetHintModifiersnull162     function GetHintModifiers: TPascalHintModifiers;
CheckHasChildsnull163     function CheckHasChilds: boolean;
CanBeAssignednull164     function CanBeAssigned: boolean;
165     procedure UpdateBaseContext;
HasChildsnull166     function HasChilds: boolean;
HasIndexnull167     function HasIndex: boolean;
IsFunctionnull168     function IsFunction: boolean;
IsConstructornull169     function IsConstructor: boolean;
IsDestructornull170     function IsDestructor: boolean;
IsAbstractMethodnull171     function IsAbstractMethod: boolean;
TryIsAbstractMethodnull172     function TryIsAbstractMethod: boolean;
173     procedure Clear;
174     procedure UnbindNode;
175     procedure StoreNodeHash;
RestoreNodenull176     function RestoreNode: boolean;
GetNodeHashnull177     function GetNodeHash(ANode: TCodeTreeNode): Cardinal;
CompareParamListnull178     function CompareParamList(CompareItem: TIdentifierListItem): integer;
CompareParamListnull179     function CompareParamList(CompareItem: TIdentifierListSearchItem): integer;
CalcMemSizenull180     function CalcMemSize: PtrUInt; virtual;
181   public
182     property ParamTypeList: string read GetParamTypeList write SetParamTypeList;
183     property ParamNameList: string read GetParamNameList write SetParamNameList;
184     property ResultType: string read FResultType write SetResultType;
185     property Node: TCodeTreeNode read GetNode write SetNode;
186   end;
187   TIdentifierListItemClass = class of TIdentifierListItem;
188 
189   TUnitNameSpaceIdentifierListItem = class(TIdentifierListItem)
190   public
191     FileUnitName: string;
192     IdentifierStartInUnitName: Integer;
193 
194     constructor Create(NewCompatibility: TIdentifierCompatibility;
195                        NewHasChilds: boolean; NewHistoryIndex: integer;
196                        NewIdentifier: PChar; NewLevel: integer;
197                        NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool;
198                        NewDefaultDesc: TCodeTreeNodeDesc;
199                        NewFileUnitName: PChar;
200                        NewIdentifierStartInUnitName: Integer);
CalcMemSizenull201     function CalcMemSize: PtrUInt; override;
202   end;
203   TUnitNameSpaceIdentifierListItemClass = class of TUnitNameSpaceIdentifierListItem;
204 
205   TIdentifierListFlag = (
206     ilfFilteredListNeedsUpdate,
207     ilfUsedToolsNeedsUpdate
208     );
209   TIdentifierListFlags = set of TIdentifierListFlag;
210 
211   TIdentifierListContextFlag = (
212     ilcfStartInStatement,  // context starts in statements. e.g. between begin..end
213     ilcfStartOfStatement,  // atom is start of statement. e.g. 'A|:=' or 'A|;', does not check if A can be assigned
214     ilcfStartOfOperand,    // atom is start of an operand. e.g. 'A|.B'
215     ilcfStartIsSubIdent,   // atom in front is point
216     ilcfNeedsEndSemicolon, // after context a semicolon is needed. e.g. 'A| end'
217     ilcfNoEndSemicolon,    // no semicolon after. E.g. 'A| else'
218     ilcfNeedsEndComma,     // after context a comma is needed. e.g. 'uses sysutil| classes'
219     ilcfNeedsDo,           // after context a 'do' is needed. e.g. 'with Form1| do'
220     ilcfIsExpression,      // is expression part of statement. e.g. 'if expr'
221     ilcfCanProcDeclaration,// context allows one to declare a procedure/method
222     ilcfEndOfLine,         // atom at end of line
223     ilcfDontAllowProcedures// context doesn't allow procedures (e.g. in function parameter, after other operator, in if codition etc. - Delphi mode supports assignment of procedures!)
224     );
225   TIdentifierListContextFlags = set of TIdentifierListContextFlag;
226 
227   TOnGatherUserIdentifiersToFilteredList = procedure(Sender: TIdentifierList;
228     FilteredList: TFPList; PriorityCount: Integer) of object;
229 
230   TIdentifierList = class
231   private
232     FContext: TFindContext;
233     FNewMemberVisibility: TCodeTreeNodeDesc;
234     FContextFlags: TIdentifierListContextFlags;
235     FOnGatherUserIdentifiersToFilteredList: TOnGatherUserIdentifiersToFilteredList;
236     FSortForHistory: boolean;
237     FSortForScope: boolean;
238     FStartAtom: TAtomPosition;
239     FStartAtomBehind: TAtomPosition;
240     FStartAtomInFront: TAtomPosition;
241     FStartBracketLvl: integer;
242     FStartContextPos: TCodeXYPosition;
243     FCreatedIdentifiers: TFPList; // list of PChar
244     FFilteredList: TFPList; // list of TIdentifierListItem
245     FFlags: TIdentifierListFlags;
246     FHistory: TIdentifierHistoryList;
247     FItems: TAvlTree;     // tree of TIdentifierListItem (completely sorted)
248     FIdentView: TAVLTree; // tree of TIdentifierListItem sorted for identifiers
249     FUsedTools: TAVLTree; // tree of TFindDeclarationTool
250     FIdentSearchItem: TIdentifierListSearchItem;
251     FPrefix: string;
252     FStartContext: TFindContext;
253     FContainsFilter: Boolean;
254     function CompareIdentListItems({%H-}Tree: TAvlTree; Data1, Data2: Pointer): integer;
255     procedure SetHistory(const AValue: TIdentifierHistoryList);
256     procedure SetSortForHistory(AValue: boolean);
257     procedure SetSortForScope(AValue: boolean);
258     procedure UpdateFilteredList;
259     function GetFilteredItems(Index: integer): TIdentifierListItem;
260     procedure SetPrefix(const AValue: string);
261   public
262     constructor Create;
263     destructor Destroy; override;
264     procedure Clear;
265     procedure Add(NewItem: TIdentifierListItem);
266     function Count: integer;
267     function GetFilteredCount: integer;
268     function HasIdentifier(Identifier: PChar; const ParamList: string): boolean;
269     function FindIdentifier(Identifier: PChar; const ParamList: string): TIdentifierListItem;
270     function FindIdentifier(Identifier: PChar; PreferProc: boolean): TIdentifierListItem;
271     function FindIdentifier(Identifier: PChar): TIdentifierListItem;
272     function FindCreatedIdentifier(const Ident: string): integer;
273     function CreateIdentifier(const Ident: string): PChar;
274     function StartUpAtomInFrontIs(const s: string): boolean;
275     function StartUpAtomBehindIs(const s: string): boolean;
276     function CompletePrefix(const OldPrefix: string): string;
277     function CalcMemSize: PtrUInt;
278   public
279     property Context: TFindContext read FContext write FContext;
280     property ContextFlags: TIdentifierListContextFlags
281                                        read FContextFlags write FContextFlags;
282     property NewMemberVisibility: TCodeTreeNodeDesc // identifier is a class member, e.g. a variable or a procedure name
283                      read FNewMemberVisibility write FNewMemberVisibility;
284     property FilteredItems[Index: integer]: TIdentifierListItem read GetFilteredItems;
285     property History: TIdentifierHistoryList read FHistory write SetHistory;
286     property Prefix: string read FPrefix write SetPrefix;
287     property SortForHistory: boolean read FSortForHistory write SetSortForHistory;
288     property SortForScope: boolean read FSortForScope write SetSortForScope;
289     property StartAtom: TAtomPosition read FStartAtom write FStartAtom;
290     property StartAtomInFront: TAtomPosition
291       read FStartAtomInFront write FStartAtomInFront; // in front of variable, not only of identifier
292     property StartAtomBehind: TAtomPosition
293                   read FStartAtomBehind write FStartAtomBehind; // directly behind
294     property StartBracketLvl: integer read FStartBracketLvl write FStartBracketLvl;
295     property StartContext: TFindContext read FStartContext write FStartContext;
296     property StartContextPos: TCodeXYPosition
297                                    read FStartContextPos write FStartContextPos;
298     property ContainsFilter: Boolean read FContainsFilter write FContainsFilter;
299     property OnGatherUserIdentifiersToFilteredList: TOnGatherUserIdentifiersToFilteredList
300       read FOnGatherUserIdentifiersToFilteredList write FOnGatherUserIdentifiersToFilteredList;
301   end;
302 
303   //----------------------------------------------------------------------------
304   // history list
305 
306   { TIdentHistListItem }
307 
308   TIdentHistListItem = class
309   public
310     Identifier: string;
311     NodeDesc: TCodeTreeNodeDesc;
312     ParamList: string;
313     HistoryIndex: integer;
314     function CalcMemSize: PtrUInt;
315   end;
316 
317   { TIdentifierHistoryList }
318 
319   TIdentifierHistoryList = class
320   private
321     FCapacity: integer;
322     FItems: TAVLTree; // tree of TIdentHistListItem
323     procedure SetCapacity(const AValue: integer);
324     function FindItem(NewItem: TIdentifierListItem): TAVLTreeNode;
325   public
326     constructor Create;
327     destructor Destroy; override;
328     procedure Clear;
329     procedure Add(NewItem: TIdentifierListItem);
330     function GetHistoryIndex(AnItem: TIdentifierListItem): integer;
331     function Count: integer;
332     function CalcMemSize: PtrUInt;
333   public
334     property Capacity: integer read FCapacity write SetCapacity;
335   end;
336 
337 
338   //----------------------------------------------------------------------------
339 
340   { TCodeContextInfoItem }
341 
342   TCodeContextInfoItem = class
343   public
344     Expr: TExpressionType;
345     // compiler predefined proc
346     ProcName: string;
347     Params: TStringList;
348     ResultType: string;
349     destructor Destroy; override;
350     function AsDebugString(WithExpr: boolean): string;
351   end;
352 
353   { TCodeContextInfo }
354 
355   TCodeContextInfo = class
356   private
357     FEndPos: integer;
358     FItems: TFPList; // list of TCodeContextInfoItem
359     FParameterIndex: integer;
360     FProcName: string;
361     FProcNameAtom: TAtomPosition;
362     FStartPos: integer;
363     FTool: TFindDeclarationTool;
364     function GetItems(Index: integer): TCodeContextInfoItem;
365   public
366     constructor Create;
367     destructor Destroy; override;
368     function Count: integer;
369     property Items[Index: integer]: TCodeContextInfoItem read GetItems; default;
370     function Add(const Context: TExpressionType): integer;
371     function AddCompilerProc: integer;
372     procedure Clear;
373     property Tool: TFindDeclarationTool read FTool write FTool;
374     property ParameterIndex: integer read FParameterIndex write FParameterIndex;// 1 based
375     property ProcName: string read FProcName write FProcName;
376     property ProcNameAtom: TAtomPosition read FProcNameAtom write FProcNameAtom;
377     property StartPos: integer read FStartPos write FStartPos;// context is valid from StartPos to EndPos
378     property EndPos: integer read FEndPos write FEndPos;
379 
380     function CalcMemSize: PtrUInt;
381   end;
382 
383   //----------------------------------------------------------------------------
384   // TIdentCompletionTool
385 
386   TOnGatherUserIdentifiers = procedure(Sender: TIdentCompletionTool;
387     const ContextFlags: TIdentifierListContextFlags) of object;
388 
389   TIdentCompletionTool = class(TFindDeclarationTool)
390   private
391     FBeautifier: TBeautifyCodeOptions;
392     FLastGatheredIdentParent: TCodeTreeNode;
393     FLastGatheredIdentLevel: integer;
394     FICTClassAndAncestorsAndExtClassOfHelper: TFPList;// list of PCodeXYPosition
395     FIDCTFoundPublicProperties: TAVLTree;// tree of PChar (pointing to the
396                                     // property names in source)
397     FIDTFoundMethods: TAVLTree;// tree of TCodeTreeNodeExtension Txt=clean text
398     FIDTTreeOfUnitFiles: TAVLTree;// tree of TUnitFileInfo
399     FIDTTreeOfUnitFiles_NamespacePath: string;
400     FIDTTreeOfUnitFiles_CaseInsensitive: Boolean;
401     FIDTTreeOfNamespaces: TAVLTree;// tree of TNameSpaceInfo
402     FOnGatherUserIdentifiers: TOnGatherUserIdentifiers;
403     procedure AddToTreeOfUnitFileInfo(const AFilename: string);
404     procedure AddBaseConstant(const BaseName: PChar);
405     procedure AddBaseType(const BaseName: PChar);
406     procedure AddCompilerFunction(const AProcName, AParameterList,
407       AResultType: PChar);
408     procedure AddCompilerProcedure(const AProcName, AParameterList: PChar);
409     procedure AddKeyWord(aKeyWord: string);
410   protected
411     CurrentIdentifierList: TIdentifierList;
412     CurrentIdentifierContexts: TCodeContextInfo;
413     function CollectAllIdentifiers(Params: TFindDeclarationParams;
414       const FoundContext: TFindContext): TIdentifierFoundResult;
415     procedure GatherPredefinedIdentifiers(CleanPos: integer;
416       const Context, GatherContext: TFindContext);
417     procedure GatherUsefulIdentifiers(CleanPos: integer;
418       const Context, GatherContext: TFindContext);
419     procedure GatherUnitnames(const NameSpacePath: string = '');
420     procedure GatherSourceNames(const Context: TFindContext);
421     procedure GatherContextKeywords(const Context: TFindContext;
422       CleanPos: integer; BeautifyCodeOptions: TBeautifyCodeOptions);
423     procedure GatherUserIdentifiers(const ContextFlags: TIdentifierListContextFlags);
424     procedure InitCollectIdentifiers(const CursorPos: TCodeXYPosition;
425       var IdentifierList: TIdentifierList);
426     function ParseSourceTillCollectionStart(const CursorPos: TCodeXYPosition;
427       out CleanCursorPos: integer; out CursorNode: TCodeTreeNode;
428       out IdentStartPos, IdentEndPos: integer): boolean;
429     function FindIdentifierStartPos(const CursorPos: TCodeXYPosition
430                                       ): TCodeXYPosition;
431     procedure FindCollectionContext(Params: TFindDeclarationParams;
432       IdentStartPos: integer; CursorNode: TCodeTreeNode;
433       out ExprType: TExpressionType; out ContextExprStartPos: LongInt;
434       out StartInSubContext, HasInheritedKeyword: Boolean);
435     function CollectAllContexts(Params: TFindDeclarationParams;
436       const FoundContext: TFindContext): TIdentifierFoundResult;
437     function CollectAttributeConstructors({%H-}Params: TFindDeclarationParams;
438       const FoundContext: TFindContext): TIdentifierFoundResult;
439     procedure AddCollectionContext(Tool: TFindDeclarationTool;
440       Node: TCodeTreeNode);
441     function CheckCursorInCompilerDirective(CursorPos: TCodeXYPosition): boolean;
442     procedure AddCompilerDirectiveMacros(Directive: string);
443   public
444     function GatherAvailableUnitNames(const CursorPos: TCodeXYPosition;
445                              var IdentifierList: TIdentifierList): Boolean;
446     function GatherIdentifiers(const CursorPos: TCodeXYPosition;
447                             var IdentifierList: TIdentifierList): boolean;
448     function FindCodeContext(const CursorPos: TCodeXYPosition;
449                              out CodeContexts: TCodeContextInfo): boolean;
450     function FindAbstractMethods(const CursorPos: TCodeXYPosition;
451                                  out ListOfPCodeXYPosition: TFPList;
452                                  SkipAbstractsInStartClass: boolean = false): boolean;
453     function GetValuesOfCaseVariable(const CursorPos: TCodeXYPosition;
454                                      List: TStrings; WithTypeDefIfScoped: boolean = true): boolean;
455     property Beautifier: TBeautifyCodeOptions read FBeautifier write FBeautifier;
456 
457     procedure CalcMemSize(Stats: TCTMemStats); override;
458 
459     property OnGatherUserIdentifiers: TOnGatherUserIdentifiers read FOnGatherUserIdentifiers write FOnGatherUserIdentifiers;
460   end;
461 
462 function dbgs(Flag: TIdentifierListContextFlag): string; overload;
463 function dbgs(Flags: TIdentifierListContextFlags): string; overload;
464 
465 var
466   CIdentifierListItem: TIdentifierListItemClass = TIdentifierListItem;
467   CUnitNameSpaceIdentifierListItem: TUnitNameSpaceIdentifierListItemClass = TUnitNameSpaceIdentifierListItem;
468 
469 implementation
470 
471 const
472   CompilerFuncHistoryIndex = 10;
473   CompilerFuncLevel = 10;
474 
475 function CompareIdentListItemsForIdents(Data1, Data2: Pointer): integer;
476 var
477   Item1: TIdentifierListItem absolute Data1;
478   Item2: TIdentifierListItem absolute Data2;
479 begin
480   // sort alpabetically (lower is better)
481   Result:=CompareIdentifierPtrs(Pointer(Item2.Identifier),Pointer(Item1.Identifier));
482   if Result<>0 then exit;
483 
484   // then sort for ParamList (lower is better)
485   Result:=Item2.CompareParamList(Item1);
486 end;
487 
488 function CompareIdentListSearchWithItems(SearchItem, Item: Pointer): integer;
489 var
490   TheSearchItem: TIdentifierListSearchItem absolute SearchItem;
491   TheItem: TIdentifierListItem absolute Item;
492 begin
493   // sort alpabetically (lower is better)
494   Result:=CompareIdentifierPtrs(Pointer(TheItem.Identifier),TheSearchItem.Identifier);
495   if Result<>0 then exit;
496 
497   // then sort for ParamList (lower is better)
498   Result:=TheItem.CompareParamList(TheSearchItem);
499 end;
500 
501 function CompareIdentListSearchWithItemsWithoutParams(SearchItem, Item: Pointer): integer;
502 var
503   TheSearchItem: TIdentifierListSearchItem absolute SearchItem;
504   TheItem: TIdentifierListItem absolute Item;
505 begin
506   // sort alpabetically (lower is better)
507   Result:=CompareIdentifierPtrs(Pointer(TheItem.Identifier),TheSearchItem.Identifier);
508 end;
509 
510 function CompareIdentHistListItem(Data1, Data2: Pointer): integer;
511 var
512   Item1: TIdentHistListItem absolute Data1;
513   Item2: TIdentHistListItem absolute Data2;
514 begin
515   Result:=CompareIdentifiers(PChar(Pointer(Item2.Identifier)),
516                              PChar(Pointer(Item1.Identifier)));
517   if Result<>0 then exit;
518 
519   //debugln('CompareIdentHistListItem ',Item2.Identifier,'=',Item1.Identifier);
520   Result:=CompareIdentifiers(PChar(Pointer(Item2.ParamList)),
521                              PChar(Pointer(Item1.ParamList)));
522 end;
523 
524 function CompareIdentItemWithHistListItem(Data1, Data2: Pointer): integer;
525 var
526   IdentItem: TIdentifierListItem absolute Data1;
527   HistItem: TIdentHistListItem absolute Data2;
528 begin
529   Result:=CompareIdentifierPtrs(Pointer(HistItem.Identifier),
530                                 Pointer(IdentItem.Identifier));
531   if Result<>0 then exit;
532 
533   //debugln('CompareIdentItemWithHistListItem ',HistItem.Identifier,'=',GetIdentifier(IdentItem.Identifier));
534   Result:=SysUtils.CompareText(HistItem.ParamList,IdentItem.ParamTypeList);
535 end;
536 
537 function dbgs(Flag: TIdentifierListContextFlag): string;
538 begin
539   Result:=GetEnumName(typeinfo(Flag),ord(Flag));
540 end;
541 
542 function dbgs(Flags: TIdentifierListContextFlags): string;
543 var
544   f: TIdentifierListContextFlag;
545 begin
546   Result:='';
547   for f:=Low(TIdentifierListContextFlag) to High(TIdentifierListContextFlag) do
548     if f in Flags then begin
549       if Result<>'' then Result+=',';
550       Result+=dbgs(f);
551     end;
552   Result:='['+Result+']';
553 end;
554 
555 { TUnitNameSpaceIdentifierListItem }
556 
557 constructor TUnitNameSpaceIdentifierListItem.Create(
558   NewCompatibility: TIdentifierCompatibility; NewHasChilds: boolean;
559   NewHistoryIndex: integer; NewIdentifier: PChar; NewLevel: integer;
560   NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool;
561   NewDefaultDesc: TCodeTreeNodeDesc; NewFileUnitName: PChar;
562   NewIdentifierStartInUnitName: Integer);
563 begin
564   inherited Create(NewCompatibility, NewHasChilds, NewHistoryIndex,
565                    NewIdentifier, NewLevel, NewNode, NewTool, NewDefaultDesc);
566   FileUnitName := NewFileUnitName;
567   IdentifierStartInUnitName := NewIdentifierStartInUnitName;
568 end;
569 
CalcMemSizenull570 function TUnitNameSpaceIdentifierListItem.CalcMemSize: PtrUInt;
571 begin
572   Result := inherited CalcMemSize
573     +MemSizeString(FileUnitName);
574 end;
575 
576 { TIdentifierList }
577 
CompareIdentListItemsnull578 function TIdentifierList.CompareIdentListItems(Tree: TAvlTree; Data1, Data2: Pointer): integer;
579 var
580   Item1: TIdentifierListItem absolute Data1;
581   Item2: TIdentifierListItem absolute Data2;
582 begin
583   if SortForScope then begin
584     // first sort for Compatibility  (lower is better)
585     if ord(Item1.Compatibility)<ord(Item2.Compatibility) then begin
586       Result:=-1;
587       exit;
588     end else if ord(Item1.Compatibility)>ord(Item2.Compatibility) then begin
589       Result:=1;
590       exit;
591     end;
592   end;
593 
594   if SortForHistory then begin
595     // then sort for History (lower is better)
596     if Item1.HistoryIndex<Item2.HistoryIndex then begin
597       Result:=-1;
598       exit;
599     end else if Item1.HistoryIndex>Item2.HistoryIndex then begin
600       Result:=1;
601       exit;
602     end;
603   end;
604 
605   if SortForScope then begin
606     // then sort for Level (i.e. scope, lower is better)
607     if Item1.Level<Item2.Level then begin
608       Result:=-1;
609       exit;
610     end else if Item1.Level>Item2.Level then begin
611       Result:=1;
612       exit;
613     end;
614   end;
615 
616   // then sort alpabetically (lower is better)
617   Result:=CompareIdentifierPtrs(Pointer(Item2.Identifier),Pointer(Item1.Identifier));
618   if Result<>0 then exit;
619 
620   // then sort for ParamList (lower is better)
621   Result:=Item2.CompareParamList(Item1);
622 end;
623 
624 procedure TIdentifierList.SetPrefix(const AValue: string);
625 begin
626   if FPrefix=AValue then exit;
627   FPrefix:=AValue;
628   Include(FFlags,ilfFilteredListNeedsUpdate);
629 end;
630 
631 procedure TIdentifierList.UpdateFilteredList;
632 var
633   AnAVLNode: TAvlTreeNode;
634   CurItem: TIdentifierListItem;
635   cPriorityCount: Integer;
636   i: PtrInt;
637 begin
638   if not (ilfFilteredListNeedsUpdate in FFlags) then exit;
639   if FFilteredList=nil then FFilteredList:=TFPList.Create;
640   FFilteredList.Count:=0;
641   FFilteredList.Capacity:=FItems.Count;
642   {$IFDEF CTDEBUG}
643   DebugLn(['TIdentifierList.UpdateFilteredList Prefix="',Prefix,'"']);
644   {$ENDIF}
645   AnAVLNode:=FItems.FindLowest;
646   cPriorityCount := 0;
647   while AnAVLNode<>nil do begin
648     CurItem:=TIdentifierListItem(AnAVLNode.Data);
649     if CurItem.Identifier<>'' then
650     begin
651       if FContainsFilter then
652         i:=IdentifierPos(PChar(Pointer(Prefix)),PChar(Pointer(CurItem.Identifier)))
653       else if ComparePrefixIdent(PChar(Pointer(Prefix)),PChar(Pointer(CurItem.Identifier))) then
654         i:=0
655       else
656         i:=-1;
657       if i=0 then begin
658         {$IFDEF ShowFilteredIdents}
659         DebugLn(['::: FILTERED ITEM ',FFilteredList.Count,' ',CurItem.Identifier]);
660         {$ENDIF}
661         if (length(Prefix)=length(CurItem.Identifier))
662         and (not (iliAtCursor in CurItem.Flags)) then
663           // put exact matches at the beginning
664           FFilteredList.Insert(0,CurItem)
665         else
666           FFilteredList.Insert(cPriorityCount, CurItem);
667         Inc(cPriorityCount);
668       end
669       else if i>0 then begin
670         {$IFDEF ShowFilteredIdents}
671         DebugLn(['::: FILTERED ITEM ',FFilteredList.Count,' ',CurItem.Identifier]);
672         {$ENDIF}
673         FFilteredList.Add(CurItem);
674       end;
675     end;
676     AnAVLNode:=FItems.FindSuccessor(AnAVLNode);
677   end;
678   if Assigned(FOnGatherUserIdentifiersToFilteredList) then
679     FOnGatherUserIdentifiersToFilteredList(Self, FFilteredList, cPriorityCount);
680   {$IFDEF CTDEBUG}
681   DebugLn(['TIdentifierList.UpdateFilteredList ',dbgs(FFilteredList.Count),' of ',dbgs(FItems.Count)]);
682   {$ENDIF}
683   Exclude(FFlags,ilfFilteredListNeedsUpdate);
684 end;
685 
686 procedure TIdentifierList.SetHistory(const AValue: TIdentifierHistoryList);
687 begin
688   if FHistory=AValue then exit;
689   FHistory:=AValue;
690 end;
691 
692 procedure TIdentifierList.SetSortForHistory(AValue: boolean);
693 begin
694   if FSortForHistory=AValue then Exit;
695   FSortForHistory:=AValue;
696   Clear;
697 end;
698 
699 procedure TIdentifierList.SetSortForScope(AValue: boolean);
700 begin
701   if FSortForScope=AValue then Exit;
702   FSortForScope:=AValue;
703   Clear;
704 end;
705 
GetFilteredItemsnull706 function TIdentifierList.GetFilteredItems(Index: integer): TIdentifierListItem;
707 begin
708   UpdateFilteredList;
709   if (Index<0) or (Index>=FFilteredList.Count) then
710     Result:=nil
711   else
712     Result:=TIdentifierListItem(FFilteredList[Index]);
713 end;
714 
715 constructor TIdentifierList.Create;
716 begin
717   FFlags:=[ilfFilteredListNeedsUpdate];
718   FItems:=TAvlTree.CreateObjectCompare(@CompareIdentListItems);
719   FIdentView:=TAVLTree.Create(@CompareIdentListItemsForIdents);
720   FIdentSearchItem:=TIdentifierListSearchItem.Create;
721   FCreatedIdentifiers:=TFPList.Create;
722   FSortForHistory:=true;
723   FSortForScope:=true;
724 end;
725 
726 destructor TIdentifierList.Destroy;
727 begin
728   Clear;
729   FreeAndNil(FUsedTools);
730   FreeAndNil(FItems);
731   FreeAndNil(FIdentView);
732   FreeAndNil(FFilteredList);
733   FreeAndNil(FIdentSearchItem);
734   FreeAndNil(FCreatedIdentifiers);
735   inherited Destroy;
736 end;
737 
738 procedure TIdentifierList.Clear;
739 var
740   i: Integer;
741   p: Pointer;
742 begin
743   fContextFlags:=[];
744   fContext:=CleanFindContext;
745   FNewMemberVisibility:=ctnNone;
746   FStartBracketLvl:=0;
747   fStartContext:=CleanFindContext;
748   fStartContextPos.Code:=nil;
749   fStartContextPos.X:=1;
750   fStartContextPos.Y:=1;
751   for i:=0 to FCreatedIdentifiers.Count-1 do begin
752     p:=FCreatedIdentifiers[i];
753     FreeMem(p);
754   end;
755   FCreatedIdentifiers.Clear;
756   FItems.FreeAndClear;
757   FIdentView.Clear;
758   if FUsedTools<>nil then
759     FUsedTools.Clear;
760   FFlags:=FFlags+[ilfFilteredListNeedsUpdate,ilfUsedToolsNeedsUpdate];
761 end;
762 
763 procedure TIdentifierList.Add(NewItem: TIdentifierListItem);
764 var
765   AnAVLNode: TAVLTreeNode;
766 begin
767   if (ilcfDontAllowProcedures in ContextFlags) and (NewItem.GetDesc = ctnProcedure) and
768      not (NewItem.IsFunction or NewItem.IsConstructor)
769   then
770   begin
771     NewItem.Free;
772     Exit;
773   end;
774 
775   AnAVLNode:=FIdentView.FindKey(NewItem,@CompareIdentListItemsForIdents);
776   if AnAVLNode=nil then begin
777     if History<>nil then
778       NewItem.HistoryIndex:=History.GetHistoryIndex(NewItem);
779     FItems.Add(NewItem);
780     FIdentView.Add(NewItem);
781     FFlags:=FFlags+[ilfFilteredListNeedsUpdate,ilfUsedToolsNeedsUpdate];
782   end else begin
783     // redefined identifier -> ignore
784     //DebugLn('TIdentifierList.Add redefined: ',NewItem.AsString);
785     NewItem.Free;
786   end;
787 end;
788 
Countnull789 function TIdentifierList.Count: integer;
790 begin
791   Result:=FItems.Count;
792 end;
793 
GetFilteredCountnull794 function TIdentifierList.GetFilteredCount: integer;
795 begin
796   UpdateFilteredList;
797   Result:=FFilteredList.Count;
798 end;
799 
HasIdentifiernull800 function TIdentifierList.HasIdentifier(Identifier: PChar;
801   const ParamList: string): boolean;
802 begin
803   FIdentSearchItem.Identifier:=Identifier;
804   FIdentSearchItem.ParamList:=ParamList;
805   Result:=FIdentView.FindKey(FIdentSearchItem,
806                              @CompareIdentListSearchWithItems)<>nil;
807 end;
808 
FindIdentifiernull809 function TIdentifierList.FindIdentifier(Identifier: PChar;
810   const ParamList: string): TIdentifierListItem;
811 var
812   AVLNode: TAVLTreeNode;
813 begin
814   FIdentSearchItem.Identifier:=Identifier;
815   FIdentSearchItem.ParamList:=ParamList;
816   AVLNode:=FIdentView.FindKey(FIdentSearchItem,@CompareIdentListSearchWithItems);
817   if AVLNode<>nil then
818     Result:=TIdentifierListItem(AVLNode.Data)
819   else
820     Result:=nil;
821 end;
822 
FindIdentifiernull823 function TIdentifierList.FindIdentifier(Identifier: PChar; PreferProc: boolean
824   ): TIdentifierListItem;
825 var
826   AVLNode: TAVLTreeNode;
827   StartNode: TAVLTreeNode;
828 begin
829   Result:=nil;
830   FIdentSearchItem.Identifier:=Identifier;
831   // ignore ParamList (for checking function overloading)
832   StartNode:=FIdentView.FindKey(FIdentSearchItem,@CompareIdentListSearchWithItemsWithoutParams);
833   if StartNode=nil then exit;
834   // identifier found, check preference
835   if (TIdentifierListItem(StartNode.Data).GetDesc in [ctnProcedure,ctnProcedureHead])=PreferProc
836   then
837     exit(TIdentifierListItem(StartNode.Data));
838 
839   // identifier is a (not) proc, find the same identifier that fits PreferProc
840 
841   // search in next nodes
842   AVLNode:=StartNode;
843   repeat
844     AVLNode:=FIdentView.FindSuccessor(AVLNode);
845     if (AVLNode=nil)
846     or (CompareIdentifiers(Identifier,PChar(TIdentifierListItem(AVLNode.Data).Identifier))<>0)
847     then break;
848     if (TIdentifierListItem(AVLNode.Data).GetDesc in [ctnProcedure,ctnProcedureHead])=PreferProc
849     then
850       exit(TIdentifierListItem(AVLNode.Data));
851   until false;
852   // search in previous nodes
853   AVLNode:=StartNode;
854   repeat
855     AVLNode:=FIdentView.FindPrecessor(AVLNode);
856     if (AVLNode=nil)
857     or (CompareIdentifiers(Identifier,PChar(TIdentifierListItem(AVLNode.Data).Identifier))<>0)
858     then break;
859     if (TIdentifierListItem(AVLNode.Data).GetDesc in [ctnProcedure,ctnProcedureHead])=PreferProc
860     then
861       exit(TIdentifierListItem(AVLNode.Data));
862   until false;
863 end;
864 
FindCreatedIdentifiernull865 function TIdentifierList.FindCreatedIdentifier(const Ident: string): integer;
866 begin
867   if Ident<>'' then begin
868     Result:=FCreatedIdentifiers.Count-1;
869     while (Result>=0)
870     and (CompareIdentifiers(PChar(Pointer(Ident)),
871                             PChar(Pointer(FCreatedIdentifiers[Result])))<>0)
872     do
873       dec(Result);
874   end else begin
875     Result:=-1;
876   end;
877 end;
878 
FindIdentifiernull879 function TIdentifierList.FindIdentifier(Identifier: PChar): TIdentifierListItem;
880 var
881   Node: TAVLTreeNode;
882 begin
883   FIdentSearchItem.Identifier:=Identifier;
884   // ignore ParamList
885   Node:=FIdentView.FindKey(FIdentSearchItem,@CompareIdentListSearchWithItemsWithoutParams);
886   if Assigned(Node) then
887     Result := TIdentifierListItem(Node.Data)
888   else
889     Result := nil;
890 end;
891 
CreateIdentifiernull892 function TIdentifierList.CreateIdentifier(const Ident: string): PChar;
893 var
894   i: Integer;
895 begin
896   if Ident<>'' then begin
897     i:=FindCreatedIdentifier(Ident);
898     if i>=0 then
899       Result:=PChar(Pointer(FCreatedIdentifiers[i]))
900     else begin
901       GetMem(Result,length(Ident)+1);
902       Move(Ident[1],Result^,length(Ident)+1);
903       FCreatedIdentifiers.Add(Result);
904     end;
905   end else
906     Result:=nil;
907 end;
908 
StartUpAtomInFrontIsnull909 function TIdentifierList.StartUpAtomInFrontIs(const s: string): boolean;
910 begin
911   Result:=StartContext.Tool.FreeUpAtomIs(StartAtomInFront,s);
912 end;
913 
StartUpAtomBehindIsnull914 function TIdentifierList.StartUpAtomBehindIs(const s: string): boolean;
915 begin
916   Result:=StartContext.Tool.FreeUpAtomIs(StartAtomBehind,s);
917 end;
918 
CompletePrefixnull919 function TIdentifierList.CompletePrefix(const OldPrefix: string): string;
920 // search all identifiers beginning with Prefix
921 // and return the biggest shared prefix of all of them
922 var
923   AnAVLNode: TAvlTreeNode;
924   CurItem: TIdentifierListItem;
925   FoundFirst: Boolean;
926   SamePos: Integer;
927   l: Integer;
928 begin
929   Result:=OldPrefix;
930   FoundFirst:=false;
931   AnAVLNode:=FItems.FindLowest;
932   while AnAVLNode<>nil do begin
933     CurItem:=TIdentifierListItem(AnAVLNode.Data);
934     if (CurItem.Identifier<>'')
935     and ComparePrefixIdent(PChar(Pointer(Prefix)),PChar(Pointer(CurItem.Identifier)))
936     and (not (iliAtCursor in CurItem.Flags))
937     then begin
938       if not FoundFirst then begin
939         Result:=CurItem.Identifier;
940         FoundFirst:=true;
941       end else begin
942         SamePos:=length(Prefix)+1;
943         l:=length(Result);
944         if l>length(CurItem.Identifier) then
945           l:=length(CurItem.Identifier);
946         while (SamePos<=l)
947         and (UpChars[CurItem.Identifier[SamePos]]=UpChars[Result[SamePos]])
948         do
949           inc(SamePos);
950         if SamePos<=length(Result) then begin
951           Result:=copy(Result,1,SamePos-1);
952           if length(Result)=length(Prefix) then exit;
953         end;
954       end;
955     end;
956     AnAVLNode:=FItems.FindSuccessor(AnAVLNode);
957   end;
958 end;
959 
CalcMemSizenull960 function TIdentifierList.CalcMemSize: PtrUInt;
961 var
962   i: Integer;
963   Node: TAVLTreeNode;
964   AvgNode: TAvlTreeNode;
965   li: TIdentifierListItem;
966   hli: TIdentHistListItem;
967 begin
968   Result:=PtrUInt(InstanceSize)
969     +MemSizeString(FPrefix);
970   if FCreatedIdentifiers<>nil then begin
971     inc(Result,MemSizeFPList(FCreatedIdentifiers));
972     for i:=0 to FCreatedIdentifiers.Count-1 do
973       {%H-}inc(Result,GetIdentLen(PChar(FCreatedIdentifiers[i])));
974   end;
975   if FFilteredList<>nil then begin
976     inc(Result,MemSizeFPList(FFilteredList));
977     for i:=0 to FFilteredList.Count-1 do
978       inc(Result,TIdentifierListItem(FFilteredList[i]).CalcMemSize);
979   end;
980   if FHistory<>nil then begin
981     inc(Result,FHistory.CalcMemSize);
982   end;
983   if FItems<>nil then begin
984     {%H-}inc(Result,FItems.Count*SizeOf(TAvlTreeNode));
985     AvgNode:=FItems.FindLowest;
986     while AvgNode<>nil do begin
987       li:=TIdentifierListItem(AvgNode.Data);
988       inc(Result,li.CalcMemSize);
989       AvgNode:=AvgNode.Successor;
990     end;
991   end;
992   if FIdentView<>nil then begin
993     {%H-}inc(Result,FIdentView.Count*SizeOf(TAVLTreeNode));
994     Node:=FIdentView.FindLowest;
995     while Node<>nil do begin
996       hli:=TIdentHistListItem(Node.Data);
997       inc(Result,hli.CalcMemSize);
998       Node:=FIdentView.FindSuccessor(Node);
999     end;
1000   end;
1001   if FIdentSearchItem<>nil then
1002     inc(Result,FIdentSearchItem.CalcMemSize);
1003 end;
1004 
1005 { TIdentCompletionTool }
1006 
1007 procedure TIdentCompletionTool.AddToTreeOfUnitFileInfo(const AFilename: string);
1008 begin
1009   AddToTreeOfUnitFilesOrNamespaces(FIDTTreeOfUnitFiles,FIDTTreeOfNamespaces,
1010     FIDTTreeOfUnitFiles_NamespacePath,AFilename,FIDTTreeOfUnitFiles_CaseInsensitive,false);
1011 end;
1012 
1013 procedure TIdentCompletionTool.AddCompilerProcedure(const AProcName, AParameterList: PChar);
1014 var
1015   NewItem: TIdentifierListItem;
1016 begin
1017   //DebugLn(['AddCompilerProcedure ',AProcName,' ',ilcfStartOfStatement in CurrentIdentifierList.ContextFlags]);
1018   if (ilcfDontAllowProcedures in CurrentIdentifierList.ContextFlags) then exit;
1019 
1020   NewItem:=CIdentifierListItem.Create(
1021       icompUnknown,
1022       false,
1023       CompilerFuncHistoryIndex,
1024       AProcName,
1025       CompilerFuncLevel,
1026       nil,
1027       nil,
1028       ctnProcedure);
1029   NewItem.ParamTypeList:=AParameterList;
1030   NewItem.ParamNameList:=AParameterList;
1031   NewItem.Flags:=NewItem.Flags+[iliParamTypeListValid,iliParamNameListValid];
1032   CurrentIdentifierList.Add(NewItem);
1033 end;
1034 
1035 procedure TIdentCompletionTool.AddKeyWord(aKeyWord: string);
1036 var
1037   NewItem: TIdentifierListItem;
1038 begin
1039   NewItem:=CIdentifierListItem.Create(
1040       icompExact,false,0,
1041       CurrentIdentifierList.CreateIdentifier(aKeyWord),
1042       1000,nil,nil,ctnNone);
1043   include(NewItem.Flags,iliKeyword);
1044   CurrentIdentifierList.Add(NewItem);
1045 end;
1046 
1047 procedure TIdentCompletionTool.AddCompilerFunction(const AProcName, AParameterList,
1048   AResultType: PChar);
1049 var
1050   NewItem: TIdentifierListItem;
1051 begin
1052   NewItem:=CIdentifierListItem.Create(
1053       icompUnknown,
1054       false,
1055       CompilerFuncHistoryIndex,
1056       AProcName,
1057       CompilerFuncLevel,
1058       nil,
1059       nil,
1060       ctnProcedure);
1061   NewItem.ParamTypeList:=AParameterList;
1062   NewItem.ParamNameList:=AParameterList;
1063   NewItem.ResultType:=AResultType;
1064   NewItem.Flags:=NewItem.Flags+[iliParamTypeListValid,iliParamNameListValid,
1065                          iliIsFunction,iliIsFunctionValid,iliResultTypeValid];
1066   CurrentIdentifierList.Add(NewItem);
1067 end;
1068 
1069 procedure TIdentCompletionTool.AddBaseType(const BaseName: PChar);
1070 var
1071   NewItem: TIdentifierListItem;
1072 begin
1073   NewItem:=CIdentifierListItem.Create(
1074       icompUnknown,
1075       false,
1076       CompilerFuncHistoryIndex,
1077       BaseName,
1078       CompilerFuncLevel,
1079       nil,
1080       nil,
1081       ctnTypeDefinition);
1082   CurrentIdentifierList.Add(NewItem);
1083 end;
1084 
1085 procedure TIdentCompletionTool.AddBaseConstant(const BaseName: PChar);
1086 var
1087   NewItem: TIdentifierListItem;
1088 begin
1089   NewItem:=CIdentifierListItem.Create(
1090       icompUnknown,
1091       false,
1092       CompilerFuncHistoryIndex,
1093       BaseName,
1094       CompilerFuncLevel,
1095       nil,
1096       nil,
1097       ctnConstant);
1098   CurrentIdentifierList.Add(NewItem);
1099 end;
1100 
CollectAllIdentifiersnull1101 function TIdentCompletionTool.CollectAllIdentifiers(
1102   Params: TFindDeclarationParams; const FoundContext: TFindContext
1103   ): TIdentifierFoundResult;
1104 var
1105   Ident: PChar;
1106   CurContextParent: TCodeTreeNode;
1107 
1108   function ProtectedNodeIsInAllowedClass: boolean;
1109   var
1110     CurClassNode: TCodeTreeNode;
1111     FoundClassContext: TFindContext;
1112   begin
1113     Result:=false;
1114     if (FICTClassAndAncestorsAndExtClassOfHelper<>nil) then begin
1115       // start of the identifier completion is in a method or class
1116       // => all protected ancestor classes are allowed as well.
1117       CurClassNode:=FoundContext.Node;
1118       while (CurClassNode<>nil)
1119       and (not (CurClassNode.Desc in AllClasses)) do
1120         CurClassNode:=CurClassNode.Parent;
1121       if CurClassNode=nil then exit;
1122       FoundClassContext:=CreateFindContext(Params.NewCodeTool,CurClassNode);
1123       if IndexOfFindContext(FICTClassAndAncestorsAndExtClassOfHelper,@FoundClassContext)>=0 then begin
1124         // this class node is the class or one of the ancestors of the class or extended class of the helper+ancestors
1125         // of the start context of the identifier completion
1126         exit(true);
1127       end;
1128     end;
1129     //DebugLn(['ProtectedNodeIsInAllowedClass hidden: ',FindContextToString(FoundContext)]);
1130   end;
1131 
1132   function PropertyIsOverridenPublicPublish: boolean;
1133   begin
1134     // protected properties can be made public in child classes.
1135     //debugln('PropertyIsOverridenPublicPublish Identifier=',GetIdentifier(Ident),' Find=',dbgs((FIDCTFoundPublicProperties<>nil) and (FIDCTFoundPublicProperties.Find(Ident)<>nil)));
1136     if FIDCTFoundPublicProperties<>nil then begin
1137       if FIDCTFoundPublicProperties.Find(Ident)<>nil then begin
1138         // there is a public/published property with the same name
1139         exit(true);
1140       end;
1141     end;
1142     Result:=false;
1143   end;
1144 
1145   procedure SavePublicPublishedProperty;
1146   begin
1147     if FIDCTFoundPublicProperties=nil then begin
1148       // create tree
1149       FIDCTFoundPublicProperties:=
1150                          TAVLTree.Create(TListSortCompare(@CompareIdentifiers))
1151     end else if FIDCTFoundPublicProperties.Find(Ident)<>nil then begin
1152       // identifier is already public
1153       exit;
1154     end;
1155     FIDCTFoundPublicProperties.Add(Ident);
1156     //debugln('SavePublicPublishedProperty Identifier=',GetIdentifier(Ident),' Find=',dbgs(FIDCTFoundPublicProperties.Find(Ident)<>nil));
1157   end;
1158 
1159 var
1160   NewItem: TIdentifierListItem;
1161   Node: TCodeTreeNode;
1162   ProtectedForeignClass: Boolean;
1163   Lvl: LongInt;
1164   NamePos: TAtomPosition;
1165   HasLowerVisibility: Boolean;
1166 begin
1167   // proceed searching ...
1168   Result:=ifrProceedSearch;
1169 
1170   {$IFDEF ShowFoundIdents}
1171   if FoundContext.Tool=Self then
1172   DebugLn('::: COLLECT IDENT ',FoundContext.Node.DescAsString,
1173     ' "',StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50)),'"'
1174     ,' '+dbgs(fdfIgnoreUsedUnits in Params.Flags));
1175   {$ENDIF}
1176 
1177   CurContextParent:=FoundContext.Node.GetFindContextParent;
1178   if FLastGatheredIdentParent<>CurContextParent then begin
1179     // new context level
1180     FLastGatheredIdentParent:=CurContextParent;
1181     inc(FLastGatheredIdentLevel);
1182   end;
1183 
1184   Lvl:=FLastGatheredIdentLevel;
1185   HasLowerVisibility:=False;
1186 
1187   ProtectedForeignClass:=false;
1188   if FoundContext.Tool=Self then begin
1189     // identifier is in the same unit
1190     //DebugLn('::: COLLECT IDENT in SELF ',FoundContext.Node.DescAsString,
1191     //  ' "',dbgstr(FoundContext.Tool.Src,FoundContext.Node.StartPos,50),'"'
1192     //  ,' fdfIgnoreUsedUnits='+dbgs(fdfIgnoreUsedUnits in Params.Flags));
1193     if (FoundContext.Node=CurrentIdentifierList.StartContext.Node)
1194     or (FoundContext.Node=CurrentIdentifierList.Context.Node)
1195     or (FoundContext.Node.StartPos=CurrentIdentifierList.StartAtom.StartPos)
1196     then begin
1197       // found identifier is in cursor node
1198       // => do not show it
1199       exit;
1200     end;
1201   end else begin
1202     // identifier is in another unit
1203     Node:=FoundContext.Node.Parent;
1204     if (Node<>nil) and (Node.Desc in AllClassSubSections) then
1205       Node:=Node.Parent;
1206     if (Node<>nil) and (Node.Desc in AllClassBaseSections) then begin
1207       //debugln(['TIdentCompletionTool.CollectAllIdentifiers Node=',Node.DescAsString,' Context=',CurrentIdentifierList.Context.Node.DescAsString,' CtxVis=',NodeDescToStr(CurrentIdentifierList.NewMemberVisibility)]);
1208       if (CurrentIdentifierList.NewMemberVisibility<>ctnNone)
1209       and (CurrentIdentifierList.NewMemberVisibility<Node.Desc)
1210       and (FoundContext.Node.Desc
1211         in ([ctnProcedure,ctnProcedureHead,ctnProperty]+AllClassSections))
1212       then begin
1213         // the user wants to override a method or property
1214         // => ignore all with a higher visibility, because fpc does not allow
1215         //    to downgrade the visibility and will give a hint when trying
1216         //---- No, allow visibility downgrading to reduce confusion tha CodeTools do not list those functions.
1217         //---- FPC actually allows it although it shows a warning
1218         //debugln(['TIdentCompletionTool.CollectAllIdentifiers skipping member, because it would downgrade: ',dbgstr(FoundContext.Tool.ExtractNode(FoundContext.Node,[]),1,30)]);
1219         HasLowerVisibility:=True;
1220       end;
1221       case Node.Desc of
1222       ctnClassPrivate:
1223         begin
1224           // skip private definitions in other units
1225           exit;
1226         end;
1227       ctnClassProtected:
1228         begin
1229           // protected definitions are only accessible from descendants
1230           // or if visibility was raised (e.g. property)
1231           if ProtectedNodeIsInAllowedClass then begin
1232             // protected node in an ancestor => allowed
1233             //debugln('TIdentCompletionTool.CollectAllIdentifiers ALLOWED Protected in ANCESTOR '+StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50)));
1234           end else if (FoundContext.Node.Desc=ctnProperty) then begin
1235             // protected property: maybe the visibility was raised => continue
1236             ProtectedForeignClass:=true;
1237             //debugln('TIdentCompletionTool.CollectAllIdentifiers MAYBE Protected made Public '+StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50)));
1238           end else begin
1239             // otherwise: treat as private
1240             //debugln('TIdentCompletionTool.CollectAllIdentifiers FORBIDDEN Protected '+StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50)));
1241             exit;
1242           end;
1243         end;
1244       end;
1245     end;
1246   end;
1247 
1248   Ident:=nil;
1249   case FoundContext.Node.Desc of
1250 
1251   ctnTypeDefinition,ctnGenericType:
1252     begin
1253       Node:=FoundContext.Node.FirstChild;
1254       if FoundContext.Node.Desc=ctnTypeDefinition then
1255         Ident:=@FoundContext.Tool.Src[FoundContext.Node.StartPos]
1256       else begin
1257         // generic
1258         if Node=nil then exit;
1259         Ident:=@FoundContext.Tool.Src[Node.StartPos];
1260       end;
1261       if Node=nil then begin
1262         // type without definition
1263       end;
1264       if (Node<>nil)
1265       and (Node.Desc in AllClasses)
1266       and ((ctnsForwardDeclaration and Node.SubDesc)>0)
1267       then begin
1268         // forward definition of a class
1269         if CurrentIdentifierList.FindIdentifier(Ident,'')<>nil then begin
1270           // the real class is already in the list => skip forward
1271           exit;
1272         end;
1273       end;
1274     end;
1275 
1276   ctnGenericParameter:
1277     Ident:=@FoundContext.Tool.Src[FoundContext.Node.StartPos];
1278 
1279   ctnVarDefinition,ctnConstDefinition,ctnEnumIdentifier,ctnLabel,ctnGlobalProperty:
1280     Ident:=@FoundContext.Tool.Src[FoundContext.Node.StartPos];
1281 
1282   ctnProcedure,ctnProcedureHead:
1283     //do not list class constructors and destructors
1284     if not FoundContext.Tool.NodeIsClassConstructorOrDestructor(FoundContext.Node) then
1285     begin
1286       Ident:=FoundContext.Tool.GetProcNameIdentifier(FoundContext.Node);
1287       NewItem := CurrentIdentifierList.FindIdentifier(Ident,true);
1288       if (NewItem<>nil) and (NewItem.Tool<>nil) then begin
1289         if (NewItem.GetNode<>nil) then begin
1290           if (FoundContext.Node.Parent.Desc in AllClassBaseSections)
1291             <> (NewItem.Node.Parent.Desc in AllClassBaseSections)
1292           then
1293             exit; // class members hide normal procs and nested procs hide class members
1294           if (Lvl <> NewItem.Level) then begin
1295             // there is a previous declaration on a different level
1296             if (NewItem.Node.Desc<>ctnProcedure)
1297             or (not NewItem.Tool.ProcNodeHasSpecifier(NewItem.Node, psOVERLOAD))
1298             then
1299               exit; // there is a previous declaration without 'overload'
1300           end;
1301         end;
1302       end;
1303     end;
1304 
1305   ctnProperty:
1306     begin
1307       Ident:=FoundContext.Tool.GetPropertyNameIdentifier(FoundContext.Node);
1308       if FoundContext.Tool.PropNodeIsTypeLess(FoundContext.Node) then begin
1309         if FoundContext.Node.Parent.Desc in [ctnClassPublic,ctnClassPublished]
1310         then
1311           SavePublicPublishedProperty;
1312         // do not show properties without types (e.g. property Color;)
1313         // only show the real definition, which will follow in the ancestor
1314         exit;
1315       end;
1316       if (FoundContext.Node.Parent.Desc=ctnClassPrivate)
1317       and (FoundContext.Tool<>Self)
1318       and (not PropertyIsOverridenPublicPublish) then begin
1319         // a private property in another unit, that was not
1320         // made public/publish later
1321         // => skip
1322         exit;
1323       end;
1324       if (FoundContext.Node.Parent.Desc=ctnClassProtected)
1325       and ProtectedForeignClass
1326       and (not PropertyIsOverridenPublicPublish) then begin
1327         // a protected property in another unit, that was not
1328         // made public/publish later
1329         // => skip
1330         exit;
1331       end;
1332     end;
1333 
1334   ctnRecordCase:
1335     Ident:=@FoundContext.Tool.Src[Params.NewCleanPos];
1336 
1337   ctnUseUnitNamespace,ctnUseUnitClearName:
1338     if (FoundContext.Tool=Self) then begin
1339       Ident:=@Src[FoundContext.Node.StartPos];
1340     end;
1341 
1342   ctnUnit,ctnProgram,ctnLibrary,ctnPackage:
1343     if (FoundContext.Tool=Self)
1344     and GetSourceNamePos(NamePos) then
1345       Ident:=@Src[NamePos.StartPos];
1346 
1347   end;
1348   if Ident=nil then exit;
1349 
1350   NewItem:=CIdentifierListItem.Create(
1351                             icompUnknown,
1352                             false,
1353                             0,
1354                             Ident,
1355                             Lvl,
1356                             FoundContext.Node,
1357                             FoundContext.Tool,
1358                             ctnNone);
1359 
1360   //Add the '&' character to prefixed identifiers
1361   if (Ident^='&') and (IsIdentStartChar[Ident[1]]) then
1362     Include(NewItem.Flags,iliNeedsAmpersand);
1363 
1364   // found identifier is in cursor node
1365   if (FoundContext.Node=CurrentIdentifierList.StartContext.Node) then
1366     Include(NewItem.Flags,iliAtCursor);
1367 
1368   // method has lower visibility
1369   if HasLowerVisibility then
1370     Include(NewItem.Flags,iliHasLowerVisibility);
1371 
1372   {$IFDEF ShowFoundIdents}
1373   if FoundContext.Tool=Self then
1374   DebugLn('  IDENT COLLECTED: ',NewItem.AsString);
1375   {$ENDIF}
1376 
1377   CurrentIdentifierList.Add(NewItem);
1378 end;
1379 
1380 procedure TIdentCompletionTool.GatherPredefinedIdentifiers(CleanPos: integer;
1381   const Context, GatherContext: TFindContext);
1382 // Add predefined identifiers
1383 
1384   function StatementLevel: integer;
1385   var
1386     ANode: TCodeTreeNode;
1387   begin
1388     Result:=0;
1389     ANode:=Context.Node;
1390     while (ANode<>nil) and (not (ANode.Desc in [ctnBeginBlock,ctnAsmBlock])) do
1391     begin
1392       ANode:=ANode.Parent;
1393       inc(Result);
1394     end;
1395     if ANode=nil then Result:=0;
1396   end;
1397 
1398   procedure AddSystemUnit(const AnUnitName: PChar);
1399   var
1400     NewItem: TIdentifierListItem;
1401   begin
1402     NewItem:=CUnitNameSpaceIdentifierListItem.Create(
1403         icompUnknown,
1404         false,
1405         CompilerFuncHistoryIndex,
1406         AnUnitName,
1407         CompilerFuncLevel,
1408         nil,
1409         nil,
1410         ctnUseUnitClearName,
1411         AnUnitName,
1412         1);
1413     CurrentIdentifierList.Add(NewItem);
1414   end;
1415 
1416 var
1417   NewItem: TIdentifierListItem;
1418   ProcNode: TCodeTreeNode;
1419   HiddenUnits: String;
1420   p: PChar;
1421   SystemTool: TFindDeclarationTool;
1422   I: TExpressionTypeDesc;
1423   InSystemContext: Boolean;
1424   FPCFulVersion: LongInt;
1425 begin
1426   if CleanPos=0 then ;
1427 
1428   SystemTool := FindCodeToolForUsedUnit('System','',False);
1429   InSystemContext :=
1430      (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags) or
1431        ((ilcfStartIsSubIdent in CurrentIdentifierList.ContextFlags) and
1432         (GatherContext.Tool<>nil) and (GatherContext.Node<>nil) and (SystemTool<>nil) and
1433         (GatherContext.Tool = SystemTool) and (GatherContext.Node = SystemTool.FindInterfaceNode));
1434 
1435   if InSystemContext and (Context.Node.Desc in AllPascalStatements) then
1436   begin
1437     // see fpc/compiler/psystem.pp
1438     FPCFulVersion:=StrToIntDef(Scanner.Values['FPC_FULLVERSION'],0);
1439     AddCompilerProcedure('Assert','Condition:Boolean;const Message:String');
1440     AddCompilerFunction('Assigned','P:Pointer','Boolean');
1441     AddCompilerFunction('Addr','var X','Pointer');
1442     AddCompilerFunction('BitSizeOf','Identifier','Integer');
1443     AddCompilerProcedure('Break','');
1444     AddCompilerFunction('Concat','S1:String;S2:String[...;Sn:String]', 'String');
1445     if FPCFulVersion>=30100 then
1446       AddCompilerFunction('Concat','A1:Array;[...;An:Array]', 'Array');
1447     AddCompilerProcedure('Continue','');
1448     if FPCFulVersion>=30100 then
1449     begin
1450       // FromPosition and Count parameters are optional
1451       AddCompilerFunction('Copy','const S:string[;FromPosition,Count:Integer]', 'string');
1452       AddCompilerFunction('Copy','const A:array[;FromPosition,Count:Integer]', 'string');
1453     end else
1454     begin
1455       AddCompilerFunction('Copy','const S:string;FromPosition,Count:Integer', 'string');
1456       AddCompilerFunction('Copy','const A:array;FromPosition,Count:Integer', 'string');
1457     end;
1458     AddCompilerProcedure('Dec','var X:Ordinal;N:Integer=1');
1459     AddCompilerFunction('Default','T:Type','const');
1460     if FPCFulVersion>=30100 then //Delete and Insert are available as intrinsic since FPC 3.1
1461     begin
1462       AddCompilerProcedure('Delete','var S:string;Index,Count:Integer');
1463       AddCompilerProcedure('Delete','var A:array;Index,Count:Integer');
1464       AddCompilerProcedure('Insert','const Source:string;var Dest:string;Index:Integer');
1465       AddCompilerProcedure('Insert','Item; var A:array;Index:Integer');
1466     end;
1467     AddCompilerProcedure('Dispose','var X:Pointer');
1468     AddCompilerProcedure('Exclude','var S:Set;X:Ordinal');
1469     AddCompilerProcedure('Exit','');
1470     AddCompilerProcedure('Finalize','var X');
1471     AddCompilerFunction('get_frame','','Pointer');
1472     AddCompilerFunction('High','Arg:TypeOrVariable','Ordinal');
1473     AddCompilerProcedure('Inc','var X:Ordinal;N:Integer=1');
1474     AddCompilerProcedure('Include','var S:Set;X:Ordinal');
1475     AddCompilerProcedure('Initialize','var X');
1476     AddCompilerFunction('Length','S:String','Ordinal');
1477     AddCompilerFunction('Length','A:Array','Ordinal');
1478     AddCompilerFunction('Low','Arg:TypeOrVariable','Ordinal');
1479     AddCompilerProcedure('New','var X:Pointer');
1480     AddCompilerFunction('ObjCSelector','String','SEL');
1481     AddCompilerFunction('Ofs','var X','LongInt');
1482     AddCompilerFunction('Ord','X:Ordinal', 'Integer');
1483     AddCompilerProcedure('Pack','A:Array;N:Integer;var A:Array');
1484     AddCompilerFunction('Pred','X:Ordinal', 'Ordinal');
1485     AddCompilerProcedure('Read','');
1486     AddCompilerProcedure('ReadLn','');
1487     AddCompilerProcedure('ReadStr','S:String;var Args:Arguments');
1488     AddCompilerFunction('Seg','var X','LongInt');
1489     AddCompilerProcedure('SetLength','var S:String;NewLength:Integer');
1490     AddCompilerProcedure('SetLength','var A:Array;NewLength:Integer');
1491     if Scanner.Values.IsDefined('FPC_HAS_CPSTRING') then begin
1492       AddCompilerProcedure('SetString','out S:RawByteString;Buf:PAnsiChar;Len:SizeInt');
1493       AddCompilerProcedure('SetString','out S:AnsiString;Buf:PAnsiChar;Len:SizeInt');
1494       AddCompilerProcedure('SetString','out S:AnsiString;Buf:PWideChar;Len:SizeInt');
1495       AddCompilerProcedure('SetString','out S:ShortString;Buf:PChar;Len:SizeInt');
1496       AddCompilerProcedure('SetString','out S:UnicodeString;Buf:PUnicodeChar;Len:SizeInt');
1497       AddCompilerProcedure('SetString','out S:UnicodeString;Buf:PChar;Len:SizeInt');
1498       AddCompilerProcedure('SetString','out S:WideString;Buf:PWideChar;Len:SizeInt');
1499       AddCompilerProcedure('SetString','out S:WideString;Buf:PChar;Len:SizeInt');
1500     end;
1501     AddCompilerFunction('SizeOf','Identifier','Integer');
1502     AddCompilerFunction('Slice','var A:Array;Count:Integer','Array');
1503     AddCompilerProcedure('Str','const X[:Width[:Decimals]];var S:String');
1504     AddCompilerFunction('Succ','X:Ordinal', 'Ordinal');
1505     AddCompilerFunction('TypeInfo','Identifier', 'Pointer');
1506     AddCompilerFunction('TypeOf','Identifier', 'Pointer');
1507     AddCompilerProcedure('Val','S:String;var V;var Code:Integer');
1508     AddCompilerFunction('Unaligned','var X','var'); // Florian declaration :)
1509     AddCompilerProcedure('Unpack','A:Array;var A:Array;N:Integer');
1510     AddCompilerProcedure('Write','Args:Arguments');
1511     AddCompilerProcedure('WriteLn','Args:Arguments');
1512     AddCompilerProcedure('WriteStr','var S:String;Args:Arguments');
1513     if Scanner.PascalCompiler=pcPas2js then begin
1514       AddCompilerFunction('Str','const X[:Width[:Decimals]]','string');
1515       AddCompilerFunction('AWait','const Expr: T','T');
1516       AddCompilerFunction('AWait','aType; p: TJSPromise','aType');
1517     end;
1518   end;
1519 
1520   if (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags) and
1521      (Context.Node.Desc in AllPascalStatements)
1522   then
1523   begin
1524     if (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags)
1525     and Context.Tool.NodeIsInAMethod(Context.Node)
1526     and (not CurrentIdentifierList.HasIdentifier('Self','')) then begin
1527       // method body -> add 'Self'
1528       NewItem:=CIdentifierListItem.Create(
1529           icompUnknown,
1530           true,
1531           1,
1532           'Self',
1533           StatementLevel,
1534           nil,
1535           nil,
1536           ctnVarDefinition);
1537       CurrentIdentifierList.Add(NewItem);
1538     end;
1539     ProcNode:=Context.Node.GetNodeOfType(ctnProcedure);
1540     if (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags)
1541     and Context.Tool.NodeIsFunction(ProcNode)
1542     and (not CurrentIdentifierList.HasIdentifier('Result','')) then begin
1543       // function body -> add 'Result'
1544       NewItem:=CIdentifierListItem.Create(
1545           icompUnknown,
1546           true,
1547           1,
1548           'Result',
1549           StatementLevel,
1550           nil,
1551           nil,
1552           ctnVarDefinition);
1553       CurrentIdentifierList.Add(NewItem);
1554     end;
1555   end;
1556 
1557   // system types
1558   if InSystemContext then
1559   begin
1560     for I in [xtChar..xtPointer, xtLongint..xtByte, xtVariant] do
1561       AddBaseType(PChar(ExpressionTypeDescNames[I]));
1562     if not (ilcfStartInStatement in CurrentIdentifierList.ContextFlags) then
1563       for I in [xtFile, xtText] do
1564         AddBaseType(PChar(ExpressionTypeDescNames[I]));
1565     if Scanner.PascalCompiler=pcPas2js then begin
1566       for I in xtAllPas2JSExtraTypes do
1567         AddBaseType(PChar(ExpressionTypeDescNames[I]));
1568     end;
1569     AddBaseConstant('True');
1570     AddBaseConstant('False');
1571     //the nil constant doesn't belong to system context, therefore it is added in next step
1572   end;
1573   if (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags) then
1574   begin
1575     AddBaseConstant(PChar(ExpressionTypeDescNames[xtNil]));
1576     // system units
1577     HiddenUnits:=Scanner.GetHiddenUsedUnits;
1578     if HiddenUnits<>'' then begin
1579       p:=PChar(HiddenUnits);
1580       while p^<>#0 do begin
1581         while p^=',' do inc(p);
1582         if GetIdentLen(p)>0 then
1583           AddSystemUnit(p);
1584         while not (p^ in [',',#0]) do inc(p);
1585       end;
1586     end;
1587   end;
1588 end;
1589 
1590 procedure TIdentCompletionTool.GatherUsefulIdentifiers(CleanPos: integer;
1591   const Context, GatherContext: TFindContext);
1592 
1593   procedure AddPropertyProc(ProcName: string);
1594   var
1595     NewItem: TIdentifierListItem;
1596   begin
1597     NewItem:=CIdentifierListItem.Create(
1598         icompExact,true,0,
1599         CurrentIdentifierList.CreateIdentifier(ProcName),
1600         0,nil,nil,ctnProcedure);
1601     CurrentIdentifierList.Add(NewItem);
1602   end;
1603 
1604 var
1605   PropertyName: String;
1606 begin
1607   //debugln(['TIdentCompletionTool.GatherUsefulIdentifiers ',CleanPosToStr(CleanPos),' ',dbgsFC(Context)]);
1608   GatherPredefinedIdentifiers(CleanPos,Context,GatherContext);
1609   if Context.Node.Desc=ctnProperty then begin
1610     PropertyName:=ExtractPropName(Context.Node,false);
1611     //debugln('TIdentCompletionTool.GatherUsefulIdentifiers Property ',PropertyName);
1612     MoveCursorToCleanPos(CleanPos);
1613     ReadPriorAtom;
1614     //debugln(['TIdentCompletionTool.GatherUsefulIdentifiers Atom=',GetAtom]);
1615     if UpAtomIs('READ') then begin
1616       // add the default class completion 'read' specifier function
AddPropertyProcnull1617       AddPropertyProc(Beautifier.PropertyReadIdentPrefix+PropertyName);
1618     end;
1619     if UpAtomIs('WRITE') then begin
1620       // add the default class completion 'write' specifier function
AddPropertyProcnull1621       AddPropertyProc(Beautifier.PropertyWriteIdentPrefix+PropertyName);
1622     end;
1623     if (UpAtomIs('READ') or UpAtomIs('WRITE'))
1624     and (Context.Tool.FindClassOrInterfaceNode(Context.Node)<>nil)
1625     then begin
1626       // add the default class completion 'read'/'write' specifier variable
1627       AddPropertyProc(Beautifier.PrivateVariablePrefix+PropertyName);
1628     end;
1629     if UpAtomIs('STORED') then begin
1630       // add the default class completion 'stored' specifier function
AddPropertyProcnull1631       AddPropertyProc(PropertyName+Beautifier.PropertyStoredIdentPostfix);
1632     end;
1633   end;
1634 end;
1635 
1636 procedure TIdentCompletionTool.GatherUserIdentifiers(
1637   const ContextFlags: TIdentifierListContextFlags);
1638 begin
1639   if Assigned(FOnGatherUserIdentifiers) then
1640     FOnGatherUserIdentifiers(Self, ContextFlags);
1641 end;
1642 
1643 procedure TIdentCompletionTool.GatherUnitnames(const NameSpacePath: string);
1644 
1645   procedure GatherUnitsFromSet;
1646   begin
1647     // collect all unit files in fpc unit paths
1648     DirectoryCache.IterateFPCUnitsInSet(@AddToTreeOfUnitFileInfo);
1649   end;
1650 
1651 var
1652   UnitPath, SrcPath: string;
1653   BaseDir: String;
1654   ANode: TAVLTreeNode;
1655   UnitFileInfo: TUnitFileInfo;
1656   NewItem: TUnitNameSpaceIdentifierListItem;
1657   UnitExt: String;
1658   SrcExt: String;
1659   CurSourceName: String;
1660   NameSpaceInfo: TNameSpaceInfo;
1661 begin
1662   UnitPath:='';
1663   SrcPath:='';
1664   GatherUnitAndSrcPath(UnitPath,SrcPath);
1665   CurSourceName:=GetSourceName;
1666   //DebugLn('TIdentCompletionTool.GatherUnitnames CurSourceName="',CurSourceName,'" UnitPath="',UnitPath,'" SrcPath="',SrcPath,'"');
1667   BaseDir:=ExtractFilePath(MainFilename);
1668   FIDTTreeOfUnitFiles:=nil;
1669   FIDTTreeOfNamespaces:=nil;
1670   try
1671     // search in unitpath
1672     FIDTTreeOfUnitFiles_CaseInsensitive := true;
1673     FIDTTreeOfUnitFiles_NamespacePath := NameSpacePath;
1674     {$IFDEF VerboseICGatherUnitNames}
1675     FIDTTreeOfUnitFiles:=TAVLTree.Create(@CompareUnitFileInfos);
1676     {$ENDIF}
1677 
1678     UnitExt:=PascalCompilerUnitExt[Scanner.PascalCompiler];
1679     if Scanner.CompilerMode=cmMacPas then
1680       UnitExt:=UnitExt+';p';
1681     GatherUnitFiles(BaseDir,UnitPath,UnitExt,NameSpacePath,false,true,FIDTTreeOfUnitFiles, FIDTTreeOfNamespaces);
1682     {$IFDEF VerboseICGatherUnitNames}
1683     debugln(['TIdentCompletionTool.GatherUnitnames UnitPath ',FIDTTreeOfUnitFiles.Count]);
1684     {$ENDIF}
1685     // search in srcpath
1686     SrcExt:=PascalCompilerSrcExt[Scanner.PascalCompiler];
1687     if Scanner.CompilerMode=cmMacPas then
1688       SrcExt:=SrcExt+';p';
1689     GatherUnitFiles(BaseDir,SrcPath,SrcExt,NameSpacePath,false,true,FIDTTreeOfUnitFiles, FIDTTreeOfNamespaces);
1690     {$IFDEF VerboseICGatherUnitNames}
1691     debugln(['TIdentCompletionTool.GatherUnitnames Plus SrcPath ',FIDTTreeOfUnitFiles.Count]);
1692     {$ENDIF}
1693     // add default units
1694     GatherUnitsFromSet;
1695     {$IFDEF VerboseICGatherUnitNames}
1696     debugln(['TIdentCompletionTool.GatherUnitnames Plus FPC units ',FIDTTreeOfUnitFiles.Count]);
1697     {$ENDIF}
1698     // create list
1699     if FIDTTreeOfUnitFiles<>nil then
1700     begin
1701       ANode:=FIDTTreeOfUnitFiles.FindLowest;
1702       while ANode<>nil do begin
1703         UnitFileInfo:=TUnitFileInfo(ANode.Data);
1704         ANode:=FIDTTreeOfUnitFiles.FindSuccessor(ANode);
1705         if CompareText(PChar(Pointer(UnitFileInfo.FileUnitName)), Length(UnitFileInfo.FileUnitName),
1706                        PChar(Pointer(CurSourceName)), Length(CurSourceName), False)=0
1707         then
1708           continue;
1709         NewItem:=CUnitNameSpaceIdentifierListItem.Create(
1710             icompCompatible,true,0,
1711             CurrentIdentifierList.CreateIdentifier(UnitFileInfo.FileUnitNameWithoutNamespace),
1712             0,nil,nil,ctnUnit, PChar(UnitFileInfo.FileUnitName), UnitFileInfo.IdentifierStartInUnitName);
1713         if NewItem.IdentifierStartInUnitName < 1 then
1714           NewItem.IdentifierStartInUnitName := 1;
1715         {$IFDEF VerboseICGatherUnitNames}
1716         //debugln(['TIdentCompletionTool.GatherUnitnames Add ',UnitFileInfo.FileUnitName,' NewCount=',CurrentIdentifierList]);
1717         {$ENDIF}
1718         CurrentIdentifierList.Add(NewItem);
1719       end;
1720     end;
1721     if FIDTTreeOfNamespaces<>nil then
1722     begin
1723       ANode:=FIDTTreeOfNamespaces.FindLowest;
1724       while ANode<>nil do begin
1725         NameSpaceInfo:=TNameSpaceInfo(ANode.Data);
1726         NewItem:=CUnitNameSpaceIdentifierListItem.Create(
1727             icompCompatible,true,0,
1728             CurrentIdentifierList.CreateIdentifier(NameSpaceInfo.NameSpace),
1729             0,nil,nil,ctnUseUnitNamespace, PChar(NameSpaceInfo.UnitName),
1730             NameSpaceInfo.IdentifierStartInUnitName);
1731         CurrentIdentifierList.Add(NewItem);
1732         ANode:=FIDTTreeOfNamespaces.FindSuccessor(ANode);
1733       end;
1734     end;
1735   finally
1736     FreeTreeOfUnitFiles(FIDTTreeOfUnitFiles);
1737     FreeTreeOfUnitFiles(FIDTTreeOfNamespaces);
1738   end;
1739 end;
1740 
1741 procedure TIdentCompletionTool.GatherSourceNames(const Context: TFindContext);
1742 
1743   procedure Add(const SrcName: string);
1744   var
1745     NewItem: TIdentifierListItem;
1746   begin
1747     NewItem:=CIdentifierListItem.Create(
1748         icompExact,true,0,
1749         CurrentIdentifierList.CreateIdentifier(SrcName),
1750         0,nil,nil,Context.Node.Desc);
1751     CurrentIdentifierList.Add(NewItem);
1752   end;
1753 
1754 var
1755   NewSourceName: String;
1756   FileSourceName: String;
1757 begin
1758   // add the unitname as in the filename and as in the source
1759   FileSourceName:=ExtractFilenameOnly(MainFilename);
1760   NewSourceName:=GetSourceName(false);
1761   //DebugLn('TIdentCompletionTool.GatherSourceNames FileSourceName=',FileSourceName,' NewSourceName=',NewSourceName);
1762   if (FileSourceName<>lowercase(FileSourceName)) then begin
1763     // the file is not written lowercase => case is important, ignore source name
1764     Add(FileSourceName);
1765   end else if (SysUtils.CompareText(NewSourceName,FileSourceName)<>0) then begin
1766     // source name is not correct => only use file name
1767     Add(FileSourceName);
1768   end else if NewSourceName=FileSourceName then begin
1769     // both are the same => add only one
1770     Add(FileSourceName);
1771   end else begin
1772     // both are valid, just different in case
1773     // the filename is written lowercase
1774     // => prefer the source name
1775     Add(NewSourceName);
1776   end;
1777 end;
1778 
1779 procedure TIdentCompletionTool.GatherContextKeywords(
1780   const Context: TFindContext; CleanPos: integer;
1781   BeautifyCodeOptions: TBeautifyCodeOptions);
1782 type
1783   TPropertySpecifier = (
1784     psIndex,psRead,psWrite,psStored,psImplements,psDefault,psNoDefault
1785   );
1786   TPropertySpecifiers = set of TPropertySpecifier;
1787 
1788   procedure Add(Keyword: string);
1789   var
1790     NewItem: TIdentifierListItem;
1791   begin
1792     KeyWord:=BeautifyCodeOptions.BeautifyKeyWord(Keyword);
1793     NewItem:=CIdentifierListItem.Create(
1794         icompExact,false,0,
1795         CurrentIdentifierList.CreateIdentifier(Keyword),
1796         1000,nil,nil,ctnNone);
1797     include(NewItem.Flags,iliKeyword);
1798     CurrentIdentifierList.Add(NewItem);
1799   end;
1800 
1801   procedure AddSpecifiers(Forbidden: TPropertySpecifiers);
1802   begin
1803     if not (psIndex in Forbidden) then Add('index');
1804     if not (psRead in Forbidden) then Add('read');
1805     if not (psWrite in Forbidden) then Add('write');
1806     if not (psStored in Forbidden) then Add('stored');
1807     if not (psImplements in Forbidden) then Add('implements');
1808     if not (psDefault in Forbidden) then Add('default');
1809     if not (psNoDefault in Forbidden) then Add('nodefault');
1810   end;
1811 
1812   procedure CheckProperty(PropNode: TCodeTreeNode);
1813   var
1814     Forbidden: TPropertySpecifiers;
1815   begin
1816     if not MoveCursorToPropType(PropNode) then exit;
1817     if CleanPos<CurPos.EndPos then exit;
1818     ReadNextAtom;
1819     if CurPos.Flag=cafPoint then begin
1820       ReadNextAtom;
1821       if CurPos.Flag<>cafWord then exit;
1822       ReadNextAtom;
1823     end;
1824     Forbidden:=[];
1825     repeat
1826       if CleanPos<=CurPos.EndPos then begin
1827         AddSpecifiers(Forbidden);
1828         exit;
1829       end;
1830       if (not (psIndex in Forbidden)) and UpAtomIs('INDEX') then begin
1831         ReadNextAtom;
1832         Include(Forbidden,psIndex);
1833       end else if (not (psRead in Forbidden)) and UpAtomIs('READ') then begin
1834         ReadNextAtom;
1835         Forbidden:=Forbidden+[psIndex..psRead];
1836       end else if (not (psWrite in Forbidden)) and UpAtomIs('WRITE') then begin
1837         ReadNextAtom;
1838         Forbidden:=Forbidden+[psIndex..psWrite];
1839       end else if (not (psImplements in Forbidden)) and UpAtomIs('IMPLEMENTS')
1840       then begin
1841         ReadNextAtom;
1842         exit;
1843       end else if (not (psStored in Forbidden)) and UpAtomIs('STORED') then
1844       begin
1845         ReadNextAtom;
1846         Forbidden:=Forbidden+[psIndex..psImplements];
1847       end else if (not (psDefault in Forbidden)) and UpAtomIs('DEFAULT') then
1848       begin
1849         ReadNextAtom;
1850         exit;
1851       end else if (not (psNoDefault in Forbidden)) and UpAtomIs('NODEFAULT') then
1852       begin
1853         ReadNextAtom;
1854         exit;
1855       end else if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen] then begin
1856         if not ReadTilBracketClose(false) then exit;
1857       end else
1858         ReadNextAtom;
1859     until (CleanPos<CurPos.StartPos) or (CurPos.EndPos>SrcLen);
1860   end;
1861 
1862   procedure AddMethodSpecifiers;
1863   var
1864     i: Integer;
1865   begin
1866     for i:=0 to IsKeyWordMethodSpecifier.Count-1 do
1867       Add(IsKeyWordMethodSpecifier.GetItem(i).KeyWord+';');
1868   end;
1869 
1870   procedure AddProcSpecifiers;
1871   var
1872     i: Integer;
1873   begin
1874     for i:=0 to IsKeyWordProcedureSpecifier.Count-1 do
1875       Add(IsKeyWordProcedureSpecifier.GetItem(i).KeyWord+';');
1876   end;
1877 
1878   procedure AddProcTypeSpecifiers;
1879   var
1880     i: Integer;
1881   begin
1882     for i:=0 to IsKeyWordProcedureTypeSpecifier.Count-1 do
1883       Add(IsKeyWordProcedureTypeSpecifier.GetItem(i).KeyWord+';');
1884   end;
1885 
1886 var
1887   Node, SubNode, NodeInFront: TCodeTreeNode;
1888   p, AtomStartPos, AtomEndPos: Integer;
1889   NodeBehind, LastChild: TCodeTreeNode;
1890 begin
1891   try
1892     AtomStartPos:=CleanPos;
1893     AtomEndPos:=CleanPos;
1894     NodeInFront:=nil;
1895 
1896     Node:=Context.Node;
1897     if Node<>nil then begin
1898       MoveCursorToNearestAtom(CleanPos);
1899       {$IFDEF VerboseICGatherKeywords}
1900       debugln(['TIdentCompletionTool.GatherContextKeywords MoveCursorToNearestAtom Node=',Node.DescAsString,' Atom="',GetAtom,'"']);
1901       {$ENDIF}
1902       ReadNextAtom;
1903       {$IFDEF VerboseICGatherKeywords}
1904       debugln(['TIdentCompletionTool.GatherContextKeywords MoveCursorToNearestAtom+ReadNextAtom Node=',Node.DescAsString,' Atom="',GetAtom,'"']);
1905       {$ENDIF}
1906       AtomStartPos:=CurPos.StartPos;
1907       AtomEndPos:=CurPos.EndPos;
1908       if CleanPos<=AtomEndPos then begin
1909         // CleanPos is within an atom
1910         while (Node.Parent<>nil)
1911         and (AtomStartPos=Node.StartPos) do
1912           // at the start of the node -> the node is created by the atom at cursor
1913           // use parent as context
1914           Node:=Node.Parent;
1915 
1916         // get node in front
1917         ReadPriorAtomSafe(AtomStartPos);
1918         {$IFDEF VerboseICGatherKeywords}
1919         debugln(['TIdentCompletionTool.GatherContextKeywords prioratom=',CleanPosToStr(CurPos.StartPos),'="',GetAtom(CurPos),'"']);
1920         {$ENDIF}
1921         if CurPos.StartPos>0 then
1922           NodeInFront:=FindDeepestNodeAtPos(CurPos.StartPos,false);
1923       end else begin
1924         // CleanPos is between an atom
1925         NodeInFront:=FindDeepestNodeAtPos(AtomEndPos,false);
1926       end;
1927     end;
1928     {$IFDEF VerboseICGatherKeywords}
1929     debugln(['TIdentCompletionTool.GatherContextKeywords Node=',Node.DescAsString,' Atom="',GetAtom,'"']);
1930     {$ENDIF}
1931 
1932     NodeBehind:=nil;
1933     MoveCursorToCleanPos(AtomStartPos);
1934     ReadNextAtom;
1935     {$IFDEF VerboseICGatherKeywords}
1936     debugln(['TIdentCompletionTool.GatherContextKeywords nextatom=',CleanPosToStr(CurPos.StartPos),'=',GetAtom(CurPos)]);
1937     {$ENDIF}
1938     if CurPos.StartPos>CleanPos then
1939       NodeBehind:=FindDeepestNodeAtPos(CurPos.StartPos,false);
1940 
1941     {$IFDEF VerboseICGatherKeywords}
1942     debugln(['TIdentCompletionTool.GatherContextKeywords CASE Node=',Node.DescAsString,' NodeInFront=',NodeInFront.DescAsString,' NodeBehind=',NodeBehind.DescAsString]);
1943     {$ENDIF}
1944 
1945     case Node.Desc of
1946     ctnClass,ctnObject,ctnRecordType,ctnObjCCategory,ctnObjCClass,
1947     ctnClassHelper, ctnRecordHelper, ctnTypeHelper,
1948     ctnClassPrivate,ctnClassProtected,ctnClassPublic,ctnClassPublished:
1949       begin
1950         Add('public');
1951         Add('private');
1952         Add('protected');
1953         Add('published');
1954         Add('procedure');
1955         Add('function');
1956         Add('property');
1957         if (Node.Desc=ctnClass) or (Node.Parent.Desc=ctnClass) then begin
1958           Add('constructor');
1959           Add('destructor');
1960         end;
1961         if (Node.Desc=ctnRecordType) or (Node.Parent.Desc=ctnRecordType) then begin
1962           Add('case');
1963         end;
1964         LastChild:=Node.LastChild;
1965         if (LastChild<>nil) and (CleanPos>LastChild.StartPos)
1966         and (LastChild.EndPos>LastChild.StartPos)
1967         and (LastChild.EndPos<Srclen) then begin
1968           {$IFDEF VerboseICGatherKeywords}
1969           debugln(['TIdentCompletionTool.GatherContextKeywords end of class section ',dbgstr(copy(Src,Node.LastChild.EndPos-10,10))]);
1970           {$ENDIF}
1971           SubNode:=LastChild;
1972           if SubNode.Desc=ctnProperty then begin
1973             CheckProperty(SubNode);
1974           end;
1975         end;
1976       end;
1977 
1978     ctnClassInterface,ctnDispinterface,ctnObjCProtocol,ctnCPPClass:
1979       begin
1980         Add('procedure');
1981         Add('function');
1982       end;
1983 
1984     ctnInterface,ctnImplementation:
1985       begin
1986         if (Node.FirstChild=nil)
1987         or ((Node.FirstChild.Desc<>ctnUsesSection)
1988           and (Node.FirstChild.StartPos>=CleanPos))
1989         then
1990           Add('uses');
1991         Add('type');
1992         Add('var');
1993         Add('const');
1994         Add('procedure');
1995         Add('function');
1996         Add('resourcestring');
1997         if Node.Desc=ctnInterface then begin
1998           Add('property');
1999         end;
2000         if (NodeBehind=nil)
2001         or (NodeBehind.Desc in [ctnInitialization,ctnFinalization,ctnEndPoint,ctnBeginBlock])
2002         then begin
2003           if Node.Desc=ctnInterface then
2004             Add('implementation');
2005           Add('initialization');
2006           Add('finalization');
2007         end;
2008       end;
2009 
2010     ctnInitialization:
2011       if (NodeBehind=nil)
2012       or (NodeBehind.Desc in [ctnInitialization,ctnFinalization,ctnEndPoint,ctnBeginBlock])
2013       then begin
2014         Add('finalization');
2015         Add('begin');
2016       end;
2017 
2018     ctnProcedure:
2019       begin
2020         Add('begin');
2021         Add('type');
2022         Add('var');
2023         Add('const');
2024         Add('procedure');
2025         Add('function');
2026       end;
2027 
2028     ctnProcedureHead:
2029       begin
2030         MoveCursorBehindProcName(Node);
2031         p:=CurPos.StartPos;
2032         while (p>=1) and (Src[p] in [' ',#9]) do dec(p);
2033         if CleanPos>=p then
2034           AddMethodSpecifiers;
2035       end;
2036 
2037     ctnVarDefinition:
2038       if Node.Parent.Desc in [ctnClass,ctnObject,ctnRecordType,ctnObjCCategory,ctnObjCClass]
2039         +AllClassBaseSections
2040       then begin
2041         Add('public');
2042         Add('private');
2043         Add('protected');
2044         Add('published');
2045         Add('procedure');
2046         Add('function');
2047         Add('property');
2048         if [cmsObjectiveC1,cmsObjectiveC2]*Scanner.CompilerModeSwitches<>[] then
2049         begin
2050           Add('required');
2051           Add('optional');
2052         end;
2053         if (Node.Desc=ctnClass) or (Node.Parent.Desc=ctnClass) then begin
2054           Add('constructor');
2055           Add('destructor');
2056         end;
2057         if (Node.Desc=ctnRecordType) or (Node.Parent.Desc=ctnRecordType) then begin
2058           Add('case');
2059         end;
2060       end;
2061 
2062     ctnTypeSection,ctnVarSection,ctnConstSection,ctnLabelSection,ctnResStrSection,
2063     ctnLibrary,ctnProgram:
2064       begin
2065         Add('type');
2066         Add('const');
2067         Add('var');
2068         Add('resourcestring');
2069         Add('procedure');
2070         Add('function');
2071         Add('property');
2072         if Node.Desc=ctnLibrary then begin
2073           Add('initialization');
2074           Add('finalization');
2075           Add('begin');
2076         end;
2077       end;
2078 
2079     ctnProperty:
2080       CheckProperty(Node);
2081 
2082     end;
2083 
2084     if NodeInFront<>nil then begin
2085       {$IFDEF VerboseICGatherKeywords}
2086       debugln(['TIdentCompletionTool.GatherContextKeywords Check NodeInFront=',NodeInFront.DescAsString]);
2087       {$ENDIF}
2088       SubNode:=NodeInFront;
2089       while (SubNode<>nil) and (SubNode.EndPos<=CleanPos) do begin
2090         {$IFDEF VerboseICGatherKeywords}
2091         debugln(['TIdentCompletionTool.GatherContextKeywords Check NodeInFront SubNode=',SubNode.DescAsString]);
2092         {$ENDIF}
2093         if (SubNode.Desc=ctnProcedureHead) then begin
2094           // e.g. in interface: procedure DoIt; v|
2095           // procedure head postfix modifiers
2096           {$IFDEF VerboseICGatherKeywords}
2097           debugln(['TIdentCompletionTool.GatherContextKeywords SubNode.Parent=',SubNode.Parent.DescAsString]);
2098           {$ENDIF}
2099           if SubNode.Parent.Desc=ctnProcedure then begin
2100             {$IFDEF VerboseICGatherKeywords}
2101             debugln(['TIdentCompletionTool.GatherContextKeywords SubNode.Parent.Parent=',SubNode.Parent.Parent.DescAsString]);
2102             {$ENDIF}
2103             if SubNode.Parent.Parent.Desc in (AllClasses+AllClassBaseSections) then
2104               AddMethodSpecifiers
2105             else
2106               AddProcSpecifiers;
2107           end else if SubNode.Parent.Desc=ctnProcedureType then begin
2108             AddProcTypeSpecifiers;
2109           end;
2110           break;
2111         end;
2112         SubNode:=SubNode.Parent;
2113         {$IFDEF VerboseICGatherKeywords}
2114         if (SubNode<>nil) and (SubNode.EndPos>CleanPos) then
2115           debugln(['TIdentCompletionTool.GatherContextKeywords EndOfCheck NodeInFront SubNode=',SubNode.DescAsString]);
2116         {$ENDIF}
2117       end;
2118     end;
2119   except
2120     // ignore parser errors
2121     on E: ECodeToolError do ;
2122     on E: ELinkScannerError do ;
2123   end;
2124 end;
2125 
2126 procedure TIdentCompletionTool.InitCollectIdentifiers(
2127   const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList);
2128 var
2129   StartContext: TFindContext;
2130 begin
2131   if IdentifierList=nil then IdentifierList:=TIdentifierList.Create;
2132   CurrentIdentifierList:=IdentifierList;
2133   CurrentIdentifierList.Clear;
2134   FLastGatheredIdentParent:=nil;
2135   FLastGatheredIdentLevel:=0;
2136   CurrentIdentifierList.StartContextPos:=CursorPos;
2137   StartContext := CurrentIdentifierList.StartContext;
2138   StartContext.Tool := Self;
2139   CurrentIdentifierList.StartContext:=StartContext;
2140 end;
2141 
ParseSourceTillCollectionStartnull2142 function TIdentCompletionTool.ParseSourceTillCollectionStart(
2143   const CursorPos: TCodeXYPosition; out CleanCursorPos: integer;
2144   out CursorNode: TCodeTreeNode; out IdentStartPos, IdentEndPos: integer): boolean;
2145 var
2146   StartContext: TFindContext;
2147   ContextPos: Integer;
2148 begin
2149   Result:=false;
2150   CleanCursorPos:=0;
2151   CursorNode:=nil;
2152   IdentStartPos:=0;
2153   IdentEndPos:=0;
2154 
2155   // build code tree
2156   {$IFDEF CTDEBUG}
2157   DebugLn(['TIdentCompletionTool.ParseSourceTillCollectionStart A CursorPos=',dbgs(CursorPos)]);
2158   {$ENDIF}
2159   BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanCursorPos,
2160                           [btSetIgnoreErrorPos]);
2161   // Return if CleanCursorPos is before Tree.Root.StartNode.
2162   // For example a comment at the beginning of a unit.
2163   if Tree.Root.StartPos>CleanCursorPos then
2164     Exit;
2165   if FindDeepestNodeAtPos(CleanCursorPos,false)=nil then
2166   begin
2167     debugln(['TIdentCompletionTool.ParseSourceTillCollectionStart',
2168       ' BuildTreeAndGetCleanPos worked, but no node found.',
2169       ' CursorPos=',dbgs(CursorPos),' CleanCursorPos=',CleanCursorPos,
2170       ' ScannedRange=',dbgs(ScannedRange),
2171       ' Scanner.ScannedRange=',dbgs(Scanner.ScannedRange),
2172       ' IgnoreErrorAfterValid=',IgnoreErrorAfterValid
2173       ]);
2174     if IgnoreErrorAfterValid then
2175       debugln(['  IgnoreErrorAfter=',dbgs(IgnoreErrorAfter),' IgnoreErrorAfterCleanedPos=',IgnoreErrorAfterCleanedPos,' CleanPosIsAfterIgnorePos=',CleanPosIsAfterIgnorePos(CleanCursorPos)]);
2176     if CursorPos.Y<=CursorPos.Code.LineCount then
2177       debugln(['  Line=',dbgstr(CursorPos.Code.GetLine(CursorPos.Y-1,true),1,CursorPos.X-1),'|',dbgstr(CursorPos.Code.GetLine(CursorPos.Y-1,true),CursorPos.X,100)]);
2178     CursorNode:=Tree.Root;
2179     if CursorNode=nil then begin
2180       debugln(['  no nodes']);
2181     end else begin
2182       while CursorNode.NextBrother<>nil do
2183         CursorNode:=CursorNode.NextBrother;
2184       while CursorNode<>nil do begin
2185         debugln(['  Node=',CursorNode.DescAsString,',Start=',CursorNode.StartPos,',End=',CursorNode.EndPos,',Src="...',dbgstr(RightStr(ExtractNode(CursorNode,[]),100)),'"']);
2186         CursorNode:=CursorNode.LastChild;
2187       end;
2188     end;
2189   end;
2190 
2191   // find node at position
2192   ContextPos:=CleanCursorPos;
2193   // The context node might be in front of the CleanCursorPos
2194   // For example: A.|end; In this case the statement ends at the point.
2195   // Check the atom in front
2196   ReadPriorAtomSafe(CleanCursorPos);
2197   if (CurPos.Flag<>cafNone) then begin
2198     if (CurPos.Flag in [cafPoint,cafRoundBracketOpen,cafEdgedBracketOpen])
2199     or UpAtomIs('INHERITED') then
2200       ContextPos:=CurPos.StartPos;
2201   end;
2202 
2203   CursorNode:=BuildSubTreeAndFindDeepestNodeAtPos(ContextPos,true);
2204   if CurrentIdentifierList<>nil then begin
2205     StartContext:=CurrentIdentifierList.StartContext;
2206     StartContext.Node:=CursorNode;
2207     CurrentIdentifierList.StartContext:=StartContext;
2208   end;
2209 
2210   // get identifier position
2211   if CursorPos.Code.LineColIsOutside(CursorPos.Y,CursorPos.X) then begin
2212     IdentStartPos:=CleanCursorPos;
2213     IdentEndPos:=CleanCursorPos;
2214   end else begin
2215     GetIdentStartEndAtPosition(Src,CleanCursorPos,IdentStartPos,IdentEndPos);
2216   end;
2217   //DebugLn(['TIdentCompletionTool.ParseSourceTillCollectionStart ',dbgstr(copy(Src,IdentStartPos,10)),' CursorPos.X=',CursorPos.X,' LineLen=',CursorPos.Code.GetLineLength(CursorPos.Y-1),' ',CursorPos.Code.GetLine(CursorPos.Y-1)]);
2218   if CursorPos.X>CursorPos.Code.GetLineLength(CursorPos.Y-1)+1 then
2219     IdentStartPos:=IdentEndPos;
2220   Result:=true;
2221 end;
2222 
TIdentCompletionTool.FindIdentifierStartPosnull2223 function TIdentCompletionTool.FindIdentifierStartPos(
2224   const CursorPos: TCodeXYPosition): TCodeXYPosition;
2225 var
2226   p: integer;
2227   IdentStartPos, IdentEndPos: integer;
2228 begin
2229   CursorPos.Code.LineColToPosition(CursorPos.Y,CursorPos.X,p);
2230   if p<1 then
2231     RaiseException(20170421201041,ctsCursorPosOutsideOfCode);
2232   if CursorPos.X<=CursorPos.Code.GetLineLength(CursorPos.Y-1)+1 then begin
2233     GetIdentStartEndAtPosition(CursorPos.Code.Source,p,IdentStartPos,IdentEndPos);
2234   end else begin
2235     IdentStartPos:=p;
2236     IdentEndPos:=p;
2237   end;
2238   Result:=CursorPos;
2239   if IdentStartPos>0 then
2240     dec(Result.X,p-IdentStartPos);
2241   //DebugLn(['TIdentCompletionTool.FindIdentifierStartPos ',dbgstr(copy(CursorPos.Code.Source,IdentStartPos,20))]);
2242 end;
2243 
2244 procedure TIdentCompletionTool.FindCollectionContext(
2245   Params: TFindDeclarationParams; IdentStartPos: integer;
2246   CursorNode: TCodeTreeNode; out ExprType: TExpressionType; out
2247   ContextExprStartPos: LongInt; out StartInSubContext,
2248   HasInheritedKeyword: Boolean);
2249 
GetContextExprStartPosnull2250   function GetContextExprStartPos(IdentStartPos: integer;
2251     ContextNode: TCodeTreeNode): integer;
2252   begin
2253     MoveCursorToCleanPos(IdentStartPos);
2254     ReadPriorAtom;
2255     HasInheritedKeyword := UpAtomIs('INHERITED');
2256     if (CurPos.Flag=cafPoint)
2257     or HasInheritedKeyword then begin
2258       Result:=FindStartOfTerm(IdentStartPos,NodeTermInType(ContextNode));
2259       if Result<ContextNode.StartPos then
2260         Result:=ContextNode.StartPos;
2261     end else
2262       Result:=IdentStartPos;
2263     MoveCursorToCleanPos(Result);
2264     ReadNextAtom;
2265     case ContextNode.Desc of
2266     ctnProperty:
2267       // check for special property keywords
2268       if WordIsPropertySpecifier.DoItCaseInsensitive(Src,
2269           CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
2270       then
2271         // do not resolve property specifiers
2272         Result:=IdentStartPos;
2273     end;
2274   end;
2275 
2276 var
2277   IgnoreCurContext: Boolean;
2278   GatherContext: TFindContext;
2279 begin
2280   GatherContext:=CreateFindContext(Self,CursorNode);
2281   ExprType := CleanExpressionType;
2282 
2283   IgnoreCurContext:=false;
2284   //DebugLn(['TIdentCompletionTool.FindCollectionContext IdentStartPos=',dbgstr(copy(Src,IdentStartPos,20)),' ',CursorNode.DescAsString]);
2285   ContextExprStartPos:=GetContextExprStartPos(IdentStartPos,CursorNode);
2286   if GatherContext.Node.Desc=ctnWithVariable then begin
2287     if GatherContext.Node.PriorBrother<>nil then
2288       GatherContext.Node:=GatherContext.Node.PriorBrother
2289     else
2290       GatherContext.Node:=GatherContext.Node.Parent;
2291   end
2292   else if (GatherContext.Node.GetNodeOfType(ctnClassInheritance)<>nil) then
2293   begin
2294     while not (GatherContext.Node.Desc in AllClasses) do
2295       GatherContext.Node:=GatherContext.Node.Parent;
2296     GatherContext.Node:=GatherContext.Node.Parent;
2297     IgnoreCurContext:=true;
2298   end else if GatherContext.Node.Desc=ctnIdentifier then begin
2299     IgnoreCurContext:=true;
2300   end;
2301 
2302   StartInSubContext:=false;
2303   //DebugLn(['TIdentCompletionTool.FindCollectionContext ContextExprStartPos=',ContextExprStartPos,' "',dbgstr(copy(Src,ContextExprStartPos,20)),'" IdentStartPos="',dbgstr(copy(Src,IdentStartPos,20)),'" Gather=',FindContextToString(GatherContext)]);
2304   if ContextExprStartPos<IdentStartPos then begin
2305     MoveCursorToCleanPos(IdentStartPos);
2306     Params.ContextNode:=CursorNode;
2307     Params.SetIdentifier(Self,nil,nil);
2308     Params.Flags:=[fdfExceptionOnNotFound,
2309                    fdfSearchInParentNodes,fdfSearchInAncestors,fdfSearchInHelpers,fdfTypeType];
2310     if IgnoreCurContext then
2311       Params.Flags:=Params.Flags+[fdfIgnoreCurContextNode];
2312     ExprType:=FindExpressionTypeOfTerm(ContextExprStartPos,IdentStartPos,
2313                                        Params,false);
2314     if ExprType.Desc=xtContext then begin
2315       GatherContext:=ExprType.Context;
2316       //debugln(['TIdentCompletionTool.FindCollectionContext ',ExprTypeToString(ExprType)]);
2317       StartInSubContext:=true;
2318     end else begin
2319       // for example "string.|"
2320       GatherContext:=CleanFindContext;
2321     end;
2322   end;
2323   ExprType.Context := GatherContext;
2324 end;
2325 
CollectAllContextsnull2326 function TIdentCompletionTool.CollectAllContexts(
2327   Params: TFindDeclarationParams; const FoundContext: TFindContext
2328   ): TIdentifierFoundResult;
2329 begin
2330   Result:=ifrProceedSearch;
2331   if FoundContext.Node=nil then exit;
2332   //DebugLn(['TIdentCompletionTool.CollectAllContexts ',FoundContext.Node.DescAsString]);
2333   case FoundContext.Node.Desc of
2334   ctnProcedure:
2335     begin
2336       //DebugLn('TIdentCompletionTool.CollectAllContexts Found Proc CurrentContexts.ProcNameAtom.StartPos=',dbgs(CurrentIdentifierContexts.ProcNameAtom.StartPos));
2337       if (CurrentIdentifierContexts.ProcName='') then exit;
2338       FoundContext.Tool.MoveCursorToProcName(FoundContext.Node,true);
2339       //DebugLn(['TIdentCompletionTool.CollectAllContexts ProcName=',GetIdentifier(@FoundContext.Tool.Src[FoundContext.Tool.CurPos.StartPos])]);
2340       if FoundContext.Tool.CompareSrcIdentifiers(
2341         FoundContext.Tool.CurPos.StartPos,
2342         PChar(CurrentIdentifierContexts.ProcName))
2343       then begin
2344         // method without 'overload' hides inherited one
2345         if not FoundContext.Tool.ProcNodeHasSpecifier(FoundContext.Node, psOVERLOAD) then
2346           Exclude(Params.Flags, fdfSearchInAncestors);
2347       end else exit;
2348     end;
2349   ctnProperty,ctnGlobalProperty:
2350     begin
2351       if (CurrentIdentifierContexts.ProcName='') then exit;
2352       FoundContext.Tool.MoveCursorToPropName(FoundContext.Node);
2353       if not FoundContext.Tool.CompareSrcIdentifiers(
2354         FoundContext.Tool.CurPos.StartPos,
2355         PChar(CurrentIdentifierContexts.ProcName))
2356       then exit;
2357     end;
2358   ctnVarDefinition:
2359     begin
2360       //debugln(['TIdentCompletionTool.CollectAllContexts ',FoundContext.Tool.ExtractNode(FoundContext.Node,[])]);
2361       if (CurrentIdentifierContexts.ProcName='') then exit;
2362       if not FoundContext.Tool.CompareSrcIdentifiers(
2363         FoundContext.Node.StartPos,
2364         PChar(CurrentIdentifierContexts.ProcName))
2365       then exit;
2366     end;
2367   else
2368     exit;
2369   end;
2370   {$IFDEF VerboseCodeContext}
2371   DebugLn(['TIdentCompletionTool.CollectAllContexts add ',FoundContext.Node.DescAsString]);
2372   {$ENDIF}
2373   AddCollectionContext(FoundContext.Tool,FoundContext.Node);
2374 end;
2375 
TIdentCompletionTool.CollectAttributeConstructorsnull2376 function TIdentCompletionTool.CollectAttributeConstructors(
2377   Params: TFindDeclarationParams; const FoundContext: TFindContext
2378   ): TIdentifierFoundResult;
2379 begin
2380   Result:=ifrProceedSearch;
2381   if FoundContext.Node=nil then exit;
2382   {$IFDEF VerboseCodeContext}
2383   //DebugLn(['TIdentCompletionTool.CollectAttributeConstructors ',FoundContext.Node.DescAsString]);
2384   {$ENDIF}
2385   case FoundContext.Node.Desc of
2386   ctnProcedure:
2387     begin
2388       {$IFDEF VerboseCodeContext}
2389       //DebugLn('TIdentCompletionTool.CollectAttributeConstructors Found Proc ',FoundContext.Tool.ExtractProcName(FoundContext.Node,[]),' ',FoundContext.Tool.CleanPosToStr(FoundContext.Node.StartPos,true));
2390       {$ENDIF}
2391       if (CurrentIdentifierContexts.ProcName='') then exit;
2392       if FoundContext.Tool.NodeIsConstructor(FoundContext.Node) then begin
2393         {$IFDEF VerboseCodeContext}
2394         DebugLn('TIdentCompletionTool.CollectAttributeConstructors Found Constructor ',FoundContext.Tool.ExtractProcName(FoundContext.Node,[]),' ',FoundContext.Tool.CleanPosToStr(FoundContext.Node.StartPos,true));
2395         {$ENDIF}
2396         AddCollectionContext(FoundContext.Tool,FoundContext.Node);
2397       end;
2398       // ToDo: method without 'overload' hides inherited one
2399       //if not FoundContext.Tool.ProcNodeHasSpecifier(FoundContext.Node, psOVERLOAD) then
2400       //  Exclude(Params.Flags, fdfSearchInAncestors);
2401     end;
2402   else
2403     exit;
2404   end;
2405 end;
2406 
2407 procedure TIdentCompletionTool.AddCollectionContext(Tool: TFindDeclarationTool;
2408   Node: TCodeTreeNode);
2409 begin
2410   if CurrentIdentifierContexts=nil then
2411     CurrentIdentifierContexts:=TCodeContextInfo.Create;
2412   CurrentIdentifierContexts.Add(CreateExpressionType(xtContext,xtNone,
2413                                            CreateFindContext(Tool,Node)));
2414   //DebugLn('TIdentCompletionTool.AddCollectionContext ',Node.DescAsString,' ',ExtractNode(Node,[]));
2415 end;
2416 
TIdentCompletionTool.CheckCursorInCompilerDirectivenull2417 function TIdentCompletionTool.CheckCursorInCompilerDirective(CursorPos: TCodeXYPosition
2418   ): boolean;
2419 var
2420   Line: String;
2421   p: Integer;
2422   EndPos: Integer;
2423   InnerStart: Integer;
2424   Directive: String;
2425   ms: TCompilerModeSwitch;
2426   cm: TCompilerMode;
2427   OptimizerSwitch: TOptimizerSwitch;
2428   SrcType: TCodeTreeNodeDesc;
2429   Compiler: TPascalCompiler;
2430 begin
2431   Result:=false;
2432   Line:=CursorPos.Code.GetLine(CursorPos.Y-1,false);
2433   p:=1;
2434   while p<=length(Line) do begin
2435     p:=FindNextCompilerDirective(Line,p,Scanner.NestedComments);
2436     if p>length(Line) then exit;
2437     EndPos:=FindCommentEnd(Line,p,Scanner.NestedComments);
2438     if (CursorPos.X>p) and (CursorPos.X<EndPos) then begin
2439       // in a directive
2440       Result:=true;
2441       InnerStart:=p;
2442       if Line[InnerStart]='{' then
2443         inc(InnerStart,2)
2444       else
2445         inc(InnerStart,3);
2446       //debugln(['TIdentCompletionTool.IsInCompilerDirective InnerStart=',InnerStart,' X=',CursorPos.X]);
2447       SrcType:=GetSourceType;
2448       Compiler:=Scanner.PascalCompiler;
2449       if (InnerStart=CursorPos.X)
2450       or ((CursorPos.X>=InnerStart) and (InnerStart<=length(Line))
2451           and (CursorPos.X<=InnerStart+GetIdentLen(@Line[InnerStart])))
2452       then begin
2453         // at start of directive
2454         // see fpc/compiler/scandir.pas (incomplete list, e.g. Define is missing there)
2455         AddKeyWord('A1');
2456         AddKeyWord('A2');
2457         AddKeyWord('A4');
2458         AddKeyWord('A8');
2459         AddKeyWord('Align');
2460         AddKeyWord('AlignAssertions');
2461         AddKeyWord('AppID');
2462         AddKeyWord('AppName');
2463         AddKeyWord('AppType');
2464         AddKeyWord('AsmMode');
2465         AddKeyWord('Assertions');
2466         AddKeyWord('BitPacking');
2467         AddKeyWord('BoolEval');
2468         AddKeyWord('Calling');
2469         AddKeyWord('CheckLowAddrLoads');
2470         AddKeyWord('CheckPointer');
2471         AddKeyWord('CodeAlign');
2472         AddKeyWord('Codepage');
2473         AddKeyWord('COperators');
2474         AddKeyWord('Copyright');
2475         AddKeyWord('D');
2476         AddKeyWord('DebugInfo');
2477         AddKeyWord('Define');
2478         if Compiler=pcDelphi then
2479           AddKeyWord('DefinitionInfo');
2480         if Compiler=pcDelphi then
2481           AddKeyWord('DenyPackageUnit');
2482         if (Compiler=pcDelphi) and (SrcType=ctnPackage) then
2483           AddKeyWord('DesignOnly');
2484         AddKeyWord('Description');
2485         AddKeyWord('ElIfC');
2486         AddKeyWord('Else');
2487         AddKeyWord('ElseC');
2488         AddKeyWord('ElseIf');
2489         AddKeyWord('EndC');
2490         AddKeyWord('EndIf');
2491         AddKeyWord('EndRegion');
2492         AddKeyWord('Error');
2493         AddKeyWord('ErrorC');
2494         AddKeyWord('ExtendedSyntax');
2495         if (Compiler=pcDelphi) and (SrcType in [ctnProgram,ctnLibrary,ctnPackage]) then
2496           AddKeyWord('Extension');
2497         AddKeyWord('ExternalSym');
2498         AddKeyWord('F');
2499         AddKeyWord('Fatal');
2500         AddKeyWord('FPUType');
2501         AddKeyWord('FrameworkPath');
2502         AddKeyWord('Goto');
2503         if Compiler=pcDelphi then
2504           AddKeyWord('HighCharUnicode');
2505         AddKeyWord('Hint');
2506         AddKeyWord('Hints');
2507         AddKeyWord('HPPEmit');
2508         AddKeyWord('HugeCode');
2509         AddKeyWord('HugePointerArithmetikNormalization');
2510         AddKeyWord('HugePointerComparisonNormalization');
2511         AddKeyWord('HugePointerNormalization');
2512         AddKeyWord('IEEEErrors');
2513         AddKeyWord('IfC');
2514         AddKeyWord('IfDef');
2515         AddKeyWord('IfEnd');
2516         AddKeyWord('IfNDef');
2517         AddKeyWord('IfOpt');
2518         AddKeyWord('ImageBase');
2519         if Compiler=pcDelphi then
2520           AddKeyWord('ImplicitBuild');
2521         AddKeyWord('ImplicitExceptions');
2522         if Compiler=pcDelphi then
2523           AddKeyWord('ImportedData');
2524         AddKeyWord('Include');
2525         AddKeyWord('IncludePath');
2526         AddKeyWord('Info');
2527         AddKeyWord('Inline');
2528         AddKeyWord('Interfaces');
2529         AddKeyWord('IOChecks');
2530         AddKeyWord('L');
2531         if Compiler=pcDelphi then
2532           AddKeyWord('LegacyIfEnd');
2533         AddKeyWord('LibExport');
2534         if Compiler=pcDelphi then
2535           AddKeyWord('LibPrefix');
2536         if Compiler=pcDelphi then
2537           AddKeyWord('LibPostfix');
2538         AddKeyWord('LibraryPath');
2539         if Compiler=pcDelphi then
2540           AddKeyWord('LibVersion');
2541         AddKeyWord('Link');
2542         AddKeyWord('LinkFramework');
2543         AddKeyWord('LinkLib');
2544         AddKeyWord('LocalSymbols');
2545         AddKeyWord('LongStrings');
2546         AddKeyWord('M');
2547         AddKeyWord('Macro');
2548         AddKeyWord('MaxFPURegisters');
2549         AddKeyWord('MaxStackSize');
2550         AddKeyWord('Memory');
2551         AddKeyWord('Message');
2552         if Compiler=pcDelphi then
2553           AddKeyWord('MethodInfo');
2554         AddKeyWord('MinEnumSize');
2555         AddKeyWord('MinFPConstPrec');
2556         AddKeyWord('MMX');
2557         AddKeyWord('Mode');
2558         AddKeyWord('ModeSwitch');
2559         AddKeyWord('NameSpace');
2560         if Compiler=pcDelphi then
2561           AddKeyWord('NoInclude');
2562         AddKeyWord('Note');
2563         AddKeyWord('Notes');
2564         AddKeyWord('ObjectChecks');
2565         if Compiler=pcDelphi then
2566           AddKeyWord('ObjExportAll');
2567         AddKeyWord('ObjectPath');
2568         if Compiler=pcDelphi then
2569           AddKeyWord('ObjTypeName');
2570         if Compiler=pcDelphi then
2571           AddKeyWord('OldTypeLayout');
2572         AddKeyWord('OpenStrings');
2573         AddKeyWord('Optimization');
2574         AddKeyWord('Output_Format');
2575         AddKeyWord('OV');
2576         AddKeyWord('OverflowChecks');
2577         AddKeyWord('PackEnum');
2578         AddKeyWord('PackRecords');
2579         AddKeyWord('PackSet');
2580         AddKeyWord('PIC');
2581         AddKeyWord('PointerMath');
2582         AddKeyWord('Pop');
2583         AddKeyWord('Profile');
2584         AddKeyWord('Push');
2585         AddKeyWord('R');
2586         AddKeyWord('RangeChecks');
2587         if Compiler=pcDelphi then
2588           AddKeyWord('RealCompatibility');
2589         AddKeyWord('ReferenceInfo');
2590         AddKeyWord('Region');
2591         AddKeyWord('Resource');
2592         AddKeyWord('RTTI');
2593         if (Compiler=pcDelphi) and (SrcType=ctnPackage) then
2594           AddKeyWord('RunOnly');
2595         if Compiler=pcDelphi then
2596           AddKeyWord('SafeDivide');
2597         AddKeyWord('SafeFPUExceptions');
2598         AddKeyWord('Saturation');
2599         AddKeyWord('ScopedEnums');
2600         AddKeyWord('ScreenName');
2601         AddKeyWord('SetC');
2602         AddKeyWord('SetPEFlags');
2603         AddKeyWord('SetPEOptFlags');
2604         AddKeyWord('SetPEOSVersion');
2605         AddKeyWord('SetPESubSysVersion');
2606         AddKeyWord('SetPEUserVersion');
2607         AddKeyWord('SmartLink');
2608         AddKeyWord('StackFrames');
2609         AddKeyWord('Stop');
2610         AddKeyWord('StringChecks');
2611         if Compiler=pcDelphi then
2612           AddKeyWord('StrongLinkTypes');
2613         AddKeyWord('Syscall');
2614         AddKeyWord('TargetSwitch');
2615         AddKeyWord('ThreadName');
2616         AddKeyWord('TypedAddress');
2617         AddKeyWord('TypeInfo');
2618         AddKeyWord('UnDef');
2619         AddKeyWord('UnitPath');
2620         AddKeyWord('VarParaCopyOutCheck');
2621         AddKeyWord('VarPropSetter');
2622         AddKeyWord('VarStringChecks');
2623         AddKeyWord('Wait');
2624         AddKeyWord('Warn');
2625         AddKeyWord('Warning');
2626         AddKeyWord('Warnings');
2627         AddKeyWord('WeakPackageUnit');
2628         AddKeyWord('WriteableConst'); // unusual spelling in fpc
2629         if Compiler=pcDelphi then
2630           AddKeyWord('ExtendedCompatibility');
2631         if Compiler=pcDelphi then
2632           AddKeyWord('ExtendedSyntax');
2633         if Compiler=pcDelphi then
2634           AddKeyWord('ExternalSym');
2635         if Compiler=pcDelphi then
2636           AddKeyWord('ExcessPrecision');
2637         AddKeyWord('Z1');
2638         AddKeyWord('Z2');
2639         AddKeyWord('Z4');
2640         AddKeyWord('ZeroBasedStrings');
2641       end else if InnerStart<=length(Line) then begin
2642         // in parameter of directive
2643         Directive:=lowercase(GetIdentifier(@Line[InnerStart]));
2644         if (Directive='ifdef')
2645         or (Directive='ifndef')
2646         or (Directive='if')
2647         or (Directive='elseif')
2648         or (Directive='ifc')
2649         then begin
2650           AddCompilerDirectiveMacros(Directive);
2651         end else if Directive='modeswitch' then begin
2652           for ms:=low(TCompilerModeSwitch) to high(TCompilerModeSwitch) do
2653             AddKeyWord(lowercase(CompilerModeSwitchNames[ms]));
2654         end else if Directive='mode' then begin
2655           for cm:=low(TCompilerMode) to high(TCompilerMode) do
2656             AddKeyWord(lowercase(CompilerModeNames[cm]));
2657         end else if Directive='warn' then begin
2658           AddKeyWord('constructing_abstract');
2659           AddKeyWord('implicit_variants');
2660           AddKeyWord('no_retval');
2661           AddKeyWord('symbol_deprecated');
2662           AddKeyWord('symbol_experimental');
2663           AddKeyWord('symbol_library');
2664           AddKeyWord('symbol_platform');
2665           AddKeyWord('symbol_unimplemented');
2666           AddKeyWord('unit_deprecated');
2667           AddKeyWord('unit_experimental');
2668           AddKeyWord('unit_library');
2669           AddKeyWord('unit_platform');
2670           AddKeyWord('unit_unimplemented');
2671           AddKeyWord('zero_nil_compat');
2672           AddKeyWord('implicit_string_cast');
2673           AddKeyWord('implicit_variants');
2674           AddKeyWord('no_retval');
2675           AddKeyWord('symbol_deprecated');
2676           AddKeyWord('symbol_experimental');
2677           AddKeyWord('symbol_library');
2678           AddKeyWord('symbol_platform');
2679           AddKeyWord('symbol_unimplemented');
2680           AddKeyWord('unit_deprecated');
2681           AddKeyWord('unit_experimental');
2682           AddKeyWord('unit_library');
2683           AddKeyWord('unit_platform');
2684           AddKeyWord('unit_unimplemented');
2685           AddKeyWord('zero_nil_compat');
2686           AddKeyWord('implicit_string_cast');
2687           AddKeyWord('implicit_string_cast_loss');
2688           AddKeyWord('explicit_string_cast');
2689           AddKeyWord('explicit_string_cast_loss');
2690           AddKeyWord('cvt_narrowing_string_lost');
2691         end else if (Directive='i') or (Directive='include') then begin
2692           AddKeyWord('Date');
2693           AddKeyWord('FPCTarget');
2694           AddKeyWord('FPCTargetOS');
2695           AddKeyWord('FPCTargetCPU');
2696           AddKeyWord('FPCVersion');
2697           AddKeyWord('Time');
2698           AddKeyWord('CurrentRoutine'); // since FPC 3.1+
2699           AddKeyWord('Line'); // since FPC 3.1+
2700         end else if (Directive='codepage') then begin
2701           // see fpcsrc/compiler/widestr.pas
2702           AddKeyWord('UTF8');
2703           AddKeyWord('cp1250');
2704           AddKeyWord('cp1251');
2705           AddKeyWord('cp1252');
2706           AddKeyWord('cp1253');
2707           AddKeyWord('cp1254');
2708           AddKeyWord('cp1255');
2709           AddKeyWord('cp1256');
2710           AddKeyWord('cp1257');
2711           AddKeyWord('cp1258');
2712           AddKeyWord('cp437');
2713           AddKeyWord('cp646');
2714           AddKeyWord('cp850');
2715           AddKeyWord('cp852');
2716           AddKeyWord('cp856');
2717           AddKeyWord('cp866');
2718           AddKeyWord('cp874');
2719           AddKeyWord('cp8859_1');
2720           AddKeyWord('cp8859_2');
2721           AddKeyWord('cp8859_5');
2722         end else if Directive='interfaces' then begin
2723           AddKeyWord('COM');
2724           AddKeyWord('CORBA');
2725         end else if Directive='optimization' then begin
2726           for OptimizerSwitch in TOptimizerSwitch do
2727             AddKeyWord(OptimizerSwitchStr[OptimizerSwitch]);
2728         end;
2729       end;
2730       exit;
2731     end;
2732     p:=EndPos;
2733   end;
2734 end;
2735 
2736 procedure TIdentCompletionTool.AddCompilerDirectiveMacros(Directive: string);
2737 var
2738   Macros: TStringToStringTree;
2739   StrItem: PStringToStringItem;
2740   CodeBufs: TAVLTree;
2741   AVLNode: TAVLTreeNode;
2742 
2743   procedure Add(e: TExpressionEvaluator);
2744   var
2745     i: Integer;
2746   begin
2747     for i:=0 to e.Count-1 do
2748       Macros[e.Names(i)]:=e.Values(i);
2749   end;
2750 
2751   procedure AddExprWords(CodeBuf: TCodeBuffer);
2752   var
2753     CurSrc: String;
2754     p: Integer;
2755     sp: PChar;
2756     NamePos: PChar;
2757     EndP: PChar;
2758     CurName: String;
2759   begin
2760     p:=1;
2761     CurSrc:=CodeBuf.Source;
2762     while p<=length(CurSrc) do begin
2763       p:=FindNextCompilerDirective(CurSrc,p,Scanner.NestedComments);
2764       if p>length(CurSrc) then break;
2765       sp:=@CurSrc[p];
2766       p:=FindCommentEnd(CurSrc,p,Scanner.NestedComments);
2767       // skip comment start
2768       if sp^='{' then inc(sp,2)
2769       else if sp^='(' then inc(sp,3);
2770       if not IsIdentStartChar[sp^] then break;
2771       NamePos:=sp;
2772       inc(sp,GetIdentLen(NamePos));
2773       if sp^=#0 then break;
2774       if (CompareIdentifiers(NamePos,'ifdef')=0)
2775       or (CompareIdentifiers(NamePos,'ifndef')=0)
2776       or (CompareIdentifiers(NamePos,'if')=0)
2777       or (CompareIdentifiers(NamePos,'ifc')=0)
2778       or (CompareIdentifiers(NamePos,'elseif')=0)
2779       or (CompareIdentifiers(NamePos,'elifc')=0)
2780       or (CompareIdentifiers(NamePos,'define')=0)
2781       or (CompareIdentifiers(NamePos,'unde')=0)
2782       or (CompareIdentifiers(NamePos,'setc')=0)
2783       then begin
2784         // add all identifiers in directive
2785         if p>length(CurSrc) then
2786           EndP:=PChar(CurSrc)+length(CurSrc)
2787         else
2788           EndP:=@CurSrc[p];
2789         while (sp<EndP) do begin
2790           if IsIdentStartChar[sp^] then begin
2791             CurName:=GetIdentifier(sp);
2792             if (CompareIdentifiers(sp,'defined')<>0)
2793             and (CompareIdentifiers(sp,'undefined')<>0) then begin
2794               if not Macros.Contains(CurName) then begin
2795                 Macros[CurName]:='';
2796               end;
2797             end;
2798             inc(sp,length(CurName));
2799           end else begin
2800             inc(sp);
2801           end;
2802         end;
2803       end;
2804     end;
2805   end;
2806 
2807 begin
2808   CodeBufs:=nil;
2809   Macros:=TStringToStringTree.Create(false);
2810   try
2811     Add(Scanner.InitialValues);
2812     Add(Scanner.Values);
2813     if (Directive='if') or (Directive='elseif')
2814     or (Directive='ifc') or (Directive='elifc') then begin
2815       AddCompilerFunction('defined','','boolean');
2816       AddCompilerFunction('undefined','','boolean');
2817     end;
2818 
2819     // add all words of all directives in unit
2820     CodeBufs:=Scanner.CreateTreeOfSourceCodes;
2821     AVLNode:=CodeBufs.FindLowest;
2822     while AVLNode<>nil do begin
2823       AddExprWords(TCodeBuffer(AVLNode.Data));
2824       AVLNode:=CodeBufs.FindSuccessor(AVLNode);
2825     end;
2826 
2827     for StrItem in Macros do
2828       AddKeyWord(StrItem^.Name);
2829   finally
2830     CodeBufs.Free;
2831     Macros.Free;
2832   end;
2833 end;
2834 
TIdentCompletionTool.GatherAvailableUnitNamesnull2835 function TIdentCompletionTool.GatherAvailableUnitNames(const CursorPos: TCodeXYPosition;
2836   var IdentifierList: TIdentifierList): Boolean;
2837 begin
2838   Result:=false;
2839 
2840   try
2841     InitCollectIdentifiers(CursorPos, IdentifierList);
2842 
2843     GatherUnitNames;
2844     Result:=true;
2845 
2846   finally
2847     CurrentIdentifierList:=nil;
2848   end;
2849 end;
2850 
TIdentCompletionTool.GatherIdentifiersnull2851 function TIdentCompletionTool.GatherIdentifiers(
2852   const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList
2853   ): boolean;
2854 var
2855   CleanCursorPos, IdentStartPos, IdentEndPos: integer;
2856   CursorNode: TCodeTreeNode;
2857   Params: TFindDeclarationParams;
2858   GatherContext: TFindContext;
2859   ContextExprStartPos: Integer;
2860   StartInSubContext: Boolean;
2861   StartPosOfVariable: LongInt;
2862   CursorContext: TFindContext;
2863   IdentStartXY: TCodeXYPosition;
2864   InFrontOfDirective, HasInheritedKeyword: Boolean;
2865   ExprType: TExpressionType;
2866   IdentifierPath: string;
2867 
2868   procedure CheckProcedureDeclarationContext;
2869   var
2870     Node: TCodeTreeNode;
2871     Can: Boolean;
2872   begin
2873     //DebugLn(['CheckProcedureDeclarationContext ',CursorNode.DescAsString]);
2874     Node:=CursorNode;
2875     Can:=false;
2876     if (Node.Parent<>nil)
2877     and (Node.Parent.Desc in AllClassSections)
2878     and (Node.Desc=ctnVarDefinition)
2879     and (CurrentIdentifierList.StartAtomBehind.Flag<>cafColon) then begin
2880       { cursor is at a class variable definition without type
2881         for example:
2882 
2883         public
2884           MouseM|
2885         end;
2886       }
2887       Can:=true;
2888     end
2889     else if (((Node.Desc=ctnProcedure) and (not NodeIsMethodBody(Node)))
2890     or ((Node.Desc=ctnProcedureHead) and (not NodeIsMethodBody(Node.Parent))))
2891     and (not (CurrentIdentifierList.StartAtomBehind.Flag
2892               in [cafEdgedBracketOpen,cafRoundBracketOpen]))
2893     then begin
2894       // for example: procedure DoSomething|
2895       Can:=true;
2896     end
2897     else if Node.Desc in (AllClassBaseSections+AllSourceTypes
2898                      +[ctnInterface,ctnImplementation])
2899     then begin
2900       //DebugLn(['TIdentCompletionTool.CheckProcedureDeclarationContext ilcfCanProcDeclaration']);
2901       Can:=true;
2902     end;
2903     if Can then
2904       CurrentIdentifierList.ContextFlags:=
2905         CurrentIdentifierList.ContextFlags+[ilcfCanProcDeclaration];
2906   end;
2907 
2908 begin
2909   Result:=false;
2910 
2911   ActivateGlobalWriteLock;
2912   try
2913     InitCollectIdentifiers(CursorPos,IdentifierList);
2914     IdentStartXY:=FindIdentifierStartPos(CursorPos);
2915     if CheckCursorInCompilerDirective(IdentStartXY) then exit(true);
2916 
2917     if not ParseSourceTillCollectionStart(IdentStartXY,CleanCursorPos,CursorNode,
2918                                           IdentStartPos,IdentEndPos) then
2919       Exit;
2920     Params:=TFindDeclarationParams.Create(Self,CursorNode);
2921     try
2922       if CleanCursorPos=0 then ;
2923       if IdentStartPos>0 then begin
2924         MoveCursorToCleanPos(IdentStartPos);
2925         ReadNextAtom;
2926         CurrentIdentifierList.StartAtom:=CurPos;
2927       end;
2928 
2929       MoveCursorToCleanPos(IdentStartPos);
2930       ReadPriorAtom;
2931       IdentifierPath := '';
2932       while CurPos.Flag = cafPoint do
2933       begin
2934         ReadPriorAtom;
2935         if CurPos.Flag <> cafWord then
2936           Break;
2937         IdentifierPath := GetUpAtom + '.' + IdentifierPath;
2938         ReadPriorAtom;
2939       end;
2940 
2941       // find context
2942       GatherContext:=CreateFindContext(Self,CursorNode);
2943       {$IFDEF CTDEBUG}
2944       DebugLn('TIdentCompletionTool.GatherIdentifiers B',
2945         ' CleanCursorPos=',CleanPosToStr(CleanCursorPos),
2946         ' IdentStartPos=',CleanPosToStr(IdentStartPos),' IdentEndPos=',CleanPosToStr(IdentEndPos),
2947         ' Ident=',copy(Src,IdentStartPos,IdentEndPos-IdentStartPos),
2948         ' GatherContext=',FindContextToString(GatherContext));
2949       {$ENDIF}
2950       CurrentIdentifierList.NewMemberVisibility:=GetClassVisibility(CursorNode);
2951       if CursorNode.Desc in [ctnUsesSection,ctnUseUnit,ctnUseUnitNamespace,ctnUseUnitClearName] then begin
2952         GatherUnitNames(IdentifierPath);
2953         MoveCursorToCleanPos(IdentEndPos);
2954         ReadNextAtom;
2955         if (CurPos.Flag=cafWord) and (not UpAtomIs('IN')) then begin
2956           // add comma
2957           CurrentIdentifierList.ContextFlags:=
2958             CurrentIdentifierList.ContextFlags+[ilcfNeedsEndComma];
2959         end;
2960       end else if (CursorNode.Desc in AllSourceTypes)
2961       and (PositionsInSameLine(Src,CursorNode.StartPos,IdentStartPos)) then begin
2962         GatherSourceNames(GatherContext);
2963       end else begin
2964         FindCollectionContext(Params,IdentStartPos,CursorNode,
2965                              ExprType,ContextExprStartPos,StartInSubContext,
2966                              HasInheritedKeyword);
2967         //debugln(['TIdentCompletionTool.GatherIdentifiers FindCollectionContext ',ExprTypeToString(ExprType)]);
2968 
2969         GatherContext := ExprType.Context;
2970         // find class and ancestors if existing (needed for protected identifiers)
2971         if (GatherContext.Tool = Self) or HasInheritedKeyword then
2972         begin
2973           FindContextClassAndAncestorsAndExtendedClassOfHelper(IdentStartXY, FICTClassAndAncestorsAndExtClassOfHelper);
2974         end;
2975 
2976         CursorContext:=CreateFindContext(Self,CursorNode);
2977         GatherContextKeywords(CursorContext,IdentStartPos,Beautifier);
2978 
2979         // check for incomplete context
2980 
2981         // context bracket level
2982         CurrentIdentifierList.StartBracketLvl:=
2983           GetBracketLvl(Src,CursorNode.StartPos,IdentStartPos,
2984                         Scanner.NestedComments);
2985         if CursorNode.Desc in AllPascalStatements then begin
2986           CurrentIdentifierList.ContextFlags:=
2987             CurrentIdentifierList.ContextFlags+[ilcfStartInStatement];
2988         end;
2989 
2990         // context in front of
2991         StartPosOfVariable:=FindStartOfTerm(IdentStartPos,NodeTermInType(CursorNode));
2992         if StartPosOfVariable>0 then begin
2993           if StartPosOfVariable=IdentStartPos then begin
2994             // cursor is at start of an operand
2995             CurrentIdentifierList.ContextFlags:=
2996               CurrentIdentifierList.ContextFlags+[ilcfStartOfOperand];
2997           end else begin
2998             MoveCursorToCleanPos(IdentStartPos);
2999             ReadPriorAtom;
3000             if CurPos.Flag=cafPoint then
3001               // cursor is behind a point
3002               CurrentIdentifierList.ContextFlags:=
3003                 CurrentIdentifierList.ContextFlags+[ilcfStartIsSubIdent];
3004           end;
3005           MoveCursorToCleanPos(StartPosOfVariable);
3006           ReadPriorAtom;
3007           CurrentIdentifierList.StartAtomInFront:=CurPos;
3008           if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags)
3009           then begin
3010             // check if LValue
3011             if (CurPos.Flag in [cafSemicolon,cafEnd,cafColon])
3012             or UpAtomIs('BEGIN')
3013             or UpAtomIs('TRY') or UpAtomIs('FINALLY') or UpAtomIs('EXCEPT')
3014             or UpAtomIs('FOR') or UpAtomIs('DO') or UpAtomIs('THEN')
3015             or UpAtomIs('REPEAT') or UpAtomIs('ASM') or UpAtomIs('ELSE')
3016             then begin
3017               CurrentIdentifierList.ContextFlags:=
3018                 CurrentIdentifierList.ContextFlags+[ilcfStartOfStatement];
3019             end;
3020             // check if expression
3021             if UpAtomIs('IF') or UpAtomIs('CASE') or UpAtomIs('WHILE')
3022             or UpAtomIs('UNTIL')
3023             then begin
3024               // todo: check at start of expression, not only in front of variable
3025               CurrentIdentifierList.ContextFlags:=
3026                 CurrentIdentifierList.ContextFlags+[ilcfIsExpression, ilcfDontAllowProcedures];
3027             end;
3028             // check if procedure is allowed
3029             if (CurPos.Flag in [cafEdgedBracketOpen, cafEqual, cafOtherOperator])
3030             or ((Scanner.CompilerMode<>cmDelphi) and (CurPos.Flag in [cafAssignment, cafComma, cafRoundBracketOpen])) // "MyEvent := MyProc;" and "o.Test(MyProc)" is supported only in Delphi mode
3031             then
3032               CurrentIdentifierList.ContextFlags:=
3033                 CurrentIdentifierList.ContextFlags+[ilcfDontAllowProcedures];
3034           end;
3035         end;
3036         // context behind
3037         if (IdentEndPos<SrcLen) then begin
3038           MoveCursorToCleanPos(IdentEndPos);
3039           //debugln(['TIdentCompletionTool.GatherIdentifiers "',dbgstr(Src,IdentStartPos,IdentEndPos-IdentStartPos),'"']);
3040           InFrontOfDirective:=(CurPos.StartPos<SrcLen) and (Src[CurPos.StartPos]='{')
3041                               and (Src[CurPos.StartPos+1]='$');
3042           ReadNextAtom;
3043 
3044           // check end of line
3045           if (not InFrontOfDirective)
3046           and (CursorPos.Code.LineColIsOutside(CursorPos.Y,CursorPos.X)
3047             or (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
3048           then
3049             CurrentIdentifierList.ContextFlags:=
3050               CurrentIdentifierList.ContextFlags+[ilcfEndOfLine];
3051 
3052           CurrentIdentifierList.StartAtomBehind:=CurPos;
3053           // check if a semicolon is needed or forbidden at the end
3054           if InFrontOfDirective
3055           or (CurrentIdentifierList.StartBracketLvl>0)
3056           or (CurPos.Flag in [cafSemicolon, cafEqual, cafColon, cafComma,
3057                      cafPoint, cafRoundBracketOpen, cafRoundBracketClose,
3058                      cafEdgedBracketOpen, cafEdgedBracketClose])
3059           or ((CurPos.Flag in [cafWord,cafNone])
3060               and (UpAtomIs('ELSE')
3061                    or UpAtomIs('THEN')
3062                    or UpAtomIs('DO')
3063                    or UpAtomIs('TO')
3064                    or UpAtomIs('OF')
3065                    or WordIsBinaryOperator.DoItCaseInsensitive(Src,
3066                             CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
3067           then begin
3068             // do not add semicolon
3069             CurrentIdentifierList.ContextFlags:=
3070               CurrentIdentifierList.ContextFlags+[ilcfNoEndSemicolon];
3071           end;
3072           // check if in statement
3073           if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags) then
3074           begin
3075             // check if a semicolon is needed at the end
3076             if (not (ilcfNoEndSemicolon in CurrentIdentifierList.ContextFlags))
3077             then begin
3078               // check if a semicolon is needed at the end
3079               if (CurPos.Flag in [cafEnd])
3080               or WordIsBlockKeyWord.DoItCaseInsensitive(Src,
3081                                     CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
3082               or ((CurPos.Flag=cafWord)
3083                   and (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
3084               then begin
3085                 // add semicolon
3086                 CurrentIdentifierList.ContextFlags:=
3087                   CurrentIdentifierList.ContextFlags+[ilcfNeedsEndSemicolon];
3088               end;
3089             end;
3090           end;
3091           // check missing 'do' after 'with'
3092           if CurrentIdentifierList.StartUpAtomInFrontIs('WITH')
3093           and (not CurrentIdentifierList.StartUpAtomBehindIs('DO'))
3094           and (not CurrentIdentifierList.StartUpAtomBehindIs('AS'))
3095           and (CurrentIdentifierList.StartBracketLvl=0)
3096           and (not (CurrentIdentifierList.StartAtomBehind.Flag in
3097                  [cafComma,cafPoint,cafRoundBracketOpen,cafEdgedBracketOpen]))
3098           and (not CurrentIdentifierList.StartUpAtomBehindIs('^'))
3099           then
3100             CurrentIdentifierList.ContextFlags:=
3101               CurrentIdentifierList.ContextFlags+[ilcfNeedsDo];
3102         end else begin
3103           // end of source
3104           CurrentIdentifierList.ContextFlags:=
3105             CurrentIdentifierList.ContextFlags+[ilcfEndOfLine];
3106         end;
3107 
3108         // search and gather identifiers in context
3109         if (GatherContext.Tool<>nil) and (GatherContext.Node<>nil) then begin
3110           {$IFDEF CTDEBUG}
3111           DebugLn('TIdentCompletionTool.GatherIdentifiers D CONTEXT: ',
3112             GatherContext.Tool.MainFilename,
3113             ' ',GatherContext.Node.DescAsString,
3114             ' "',StringToPascalConst(copy(GatherContext.Tool.Src,GatherContext.Node.StartPos,50)),'"');
3115           {$ENDIF}
3116 
3117           // gather all identifiers in context
3118           Params.ContextNode:=GatherContext.Node;
3119           Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
3120           Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
3121           if (Params.ContextNode.Desc=ctnInterface) and StartInSubContext then
3122             Include(Params.Flags,fdfIgnoreUsedUnits);
3123           if not StartInSubContext then
3124             Include(Params.Flags,fdfSearchInParentNodes);
3125           if Params.ContextNode.Desc in AllClasses then
3126             Exclude(Params.Flags,fdfSearchInParentNodes);
3127           {$IFDEF CTDEBUG}
3128           DebugLn('TIdentCompletionTool.GatherIdentifiers F');
3129           {$ENDIF}
3130           CurrentIdentifierList.Context:=GatherContext;
3131           if GatherContext.Node.Desc=ctnIdentifier then
3132             Params.Flags:=Params.Flags+[fdfIgnoreCurContextNode];
3133           GatherContext.Tool.FindIdentifierInContext(Params);
3134         end else
3135         if ExprType.Desc in xtAllTypeHelperTypes then
3136         begin
3137           // gather all identifiers in cursor context for basic types (strings etc.)
3138           Params.ContextNode:=CursorNode;
3139           Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
3140           Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
3141           CurrentIdentifierList.Context:=CursorContext;
3142           FindIdentifierInBasicTypeHelpers(ExprType.Desc, Params);
3143         end;
3144 
3145         // check for procedure/method declaration context
3146         CheckProcedureDeclarationContext;
3147 
3148         // add useful identifiers
3149         {$IFDEF CTDEBUG}
3150         DebugLn('TIdentCompletionTool.GatherIdentifiers G');
3151         {$ENDIF}
3152         GatherUsefulIdentifiers(IdentStartPos,CursorContext,GatherContext);
3153         GatherUserIdentifiers(CurrentIdentifierList.ContextFlags);
3154       end;
3155 
3156       Result:=true;
3157     finally
3158       FreeListOfPFindContext(FICTClassAndAncestorsAndExtClassOfHelper);
3159       FreeAndNil(FIDCTFoundPublicProperties);
3160       Params.Free;
3161       ClearIgnoreErrorAfter;
3162     end;
3163   finally
3164     DeactivateGlobalWriteLock;
3165     CurrentIdentifierList:=nil;
3166   end;
3167   {$IFDEF CTDEBUG}
3168   DebugLn(['TIdentCompletionTool.GatherIdentifiers END ']);
3169   {$ENDIF}
3170 end;
3171 
FindCodeContextnull3172 function TIdentCompletionTool.FindCodeContext(const CursorPos: TCodeXYPosition;
3173   out CodeContexts: TCodeContextInfo): boolean;
3174 var
3175   CleanCursorPos: integer;
3176   CursorNode: TCodeTreeNode;
3177   Params: TFindDeclarationParams;
3178 
3179   procedure AddPredefinedProcs(CurrentContexts: TCodeContextInfo;
3180     ProcNameAtom: TAtomPosition);
3181 
3182     procedure AddCompilerProc(const AProcName: string;
3183       const Params: string; const ResultType: string = '');
3184     var
3185       i: LongInt;
3186       Item: TCodeContextInfoItem;
3187     begin
3188       if CompareIdentifiers(PChar(AProcName),@Src[ProcNameAtom.StartPos])<>0
3189       then exit;
3190       i:=CurrentContexts.AddCompilerProc;
3191       Item:=CurrentContexts[i];
3192       Item.ProcName:=AProcName;
3193       Item.ResultType:=ResultType;
3194       Item.Params:=TStringList.Create;
3195       Item.Params.Delimiter:=';';
3196       Item.Params.StrictDelimiter:=true;
3197       Item.Params.DelimitedText:=Params;
3198     end;
3199 
3200   var
3201     IsPointedSystem: Boolean = False;
3202     FPCFullVersion: LongInt;
3203   begin
3204     MoveCursorToAtomPos(ProcNameAtom);
3205     ReadPriorAtom;
3206     if (CurPos.Flag = cafPoint) then
3207     begin
3208       ReadPriorAtom;
3209       IsPointedSystem := UpAtomIs('SYSTEM');
3210     end;
3211     if (CurPos.Flag in [cafEnd,cafSemicolon,cafEqual,cafComma,cafColon,
3212       cafRoundBracketOpen,cafEdgedBracketOpen,cafAssignment,cafOtherOperator])
3213     or IsPointedSystem
3214     or UpAtomIs('BEGIN')
3215     or UpAtomIs('TRY') or UpAtomIs('FINALLY') or UpAtomIs('EXCEPT')
3216     or UpAtomIs('ASM')
3217     or UpAtomIs('REPEAT') or UpAtomIs('UNTIL') or UpAtomIs('WHILE') or UpAtomIs('DO')
3218     or UpAtomIs('IF') or UpAtomIs('THEN') or UpAtomIs('ELSE')
3219     then begin
3220       // see fpc/compiler/psystem.pp
3221       FPCFullVersion:=StrToIntDef(Scanner.Values['FPC_FULLVERSION'],0);
3222       AddCompilerProc('Assert','Condition:Boolean;const Message:String');
3223       AddCompilerProc('Assigned','P:Pointer','Boolean');
3224       AddCompilerProc('Addr','var X','Pointer');
3225       AddCompilerProc('BitSizeOf','Identifier','Integer');
3226       AddCompilerProc('Concat','S1:String;S2:String[...;Sn:String]', 'String');
3227       if FPCFullVersion>=30100 then // FromPosition and Count parameters are optional
3228       begin
3229         AddCompilerProc('Concat','A1:Array[;...An:Array]', 'Array');
3230         AddCompilerProc('Copy','const S:string[;FromPosition,Count:Integer]', 'string');
3231         AddCompilerProc('Copy','const A:array[;FromPosition,Count:Integer]', 'string');
3232       end else
3233       begin
3234         AddCompilerProc('Copy','const S:string;FromPosition,Count:Integer', 'string');
3235         AddCompilerProc('Copy','const A:array;FromPosition,Count:Integer', 'string');
3236       end;
3237       AddCompilerProc('Dec','var X:Ordinal;N:Integer=1');
3238       AddCompilerProc('Default','T:Type','const');
3239       AddCompilerProc('Dispose','var X:Pointer');
3240       AddCompilerProc('Exclude','var S:Set;X:Ordinal');
3241       AddCompilerProc('Exit','ResultValue:Ordinal=Result');
3242       AddCompilerProc('Finalize','var X');
3243       AddCompilerProc('get_frame','','Pointer');
3244       AddCompilerProc('High','Arg:TypeOrVariable','Ordinal');
3245       if FPCFullVersion>=30100 then //Delete and Insert are available as intrinsic since FPC 3.1
3246       begin
3247         AddCompilerProc('Delete','var S:string;Index,Count:Integer');
3248         AddCompilerProc('Delete','var A:array;Index,Count:Integer');
3249         AddCompilerProc('Insert','const Source:string;var Dest:string;Index:Integer');
3250         AddCompilerProc('Insert','Item; var A:array;Index:Integer');
3251       end;
3252       AddCompilerProc('Inc','var X:Ordinal;N:Integer=1');
3253       AddCompilerProc('Include','var S:Set;X:Ordinal');
3254       AddCompilerProc('Initialize','var X');
3255       AddCompilerProc('Length','S:String','Integer');
3256       AddCompilerProc('Length','A:Array','Integer');
3257       AddCompilerProc('Low','Arg:TypeOrVariable','Ordinal');
3258       AddCompilerProc('New','var X:Pointer');
3259       AddCompilerProc('Ofs','var X','LongInt');
3260       AddCompilerProc('Ord','X:Ordinal', 'Integer');
3261       AddCompilerProc('Pack','A:Array;N:Integer;var A:Array');
3262       AddCompilerProc('Pred','X:Ordinal', 'Ordinal');
3263       AddCompilerProc('Read','');
3264       AddCompilerProc('ReadLn','');
3265       AddCompilerProc('ReadStr','S:String;var Args:Arguments');
3266       AddCompilerProc('Seg','var X','LongInt');
3267       AddCompilerProc('SetLength','var S:String;NewLength:Integer');
3268       AddCompilerProc('SetLength','var A:Array;NewLength:Integer');
3269       if Scanner.Values.IsDefined('FPC_HAS_CPSTRING') then begin
3270         AddCompilerProc('SetString','out S:RawByteString;Buf:PAnsiChar;Len:SizeInt');
3271         AddCompilerProc('SetString','out S:AnsiString;Buf:PAnsiChar;Len:SizeInt');
3272         AddCompilerProc('SetString','out S:AnsiString;Buf:PWideChar;Len:SizeInt');
3273         AddCompilerProc('SetString','out S:ShortString;Buf:PChar;Len:SizeInt');
3274         AddCompilerProc('SetString','out S:UnicodeString;Buf:PUnicodeChar;Len:SizeInt');
3275         AddCompilerProc('SetString','out S:UnicodeString;Buf:PChar;Len:SizeInt');
3276         AddCompilerProc('SetString','out S:WideString;Buf:PWideChar;Len:SizeInt');
3277         AddCompilerProc('SetString','out S:WideString;Buf:PChar;Len:SizeInt');
3278       end;
3279       AddCompilerProc('SizeOf','Identifier','Integer');
3280       AddCompilerProc('Slice','var A:Array;Count:Integer','Array');
3281       AddCompilerProc('Str','const X[:Width[:Decimals]];var S:String');
3282       AddCompilerProc('Succ','X:Ordinal', 'Ordinal');
3283       AddCompilerProc('TypeInfo','Identifier', 'Pointer');
3284       AddCompilerProc('TypeOf','Identifier', 'Pointer');
3285       AddCompilerProc('Val','S:String;var V;var Code:Integer');
3286       AddCompilerProc('Unaligned','var X','var');
3287       AddCompilerProc('Unpack','A:Array;var A:Array;N:Integer');
3288       AddCompilerProc('Write','Args:Arguments');
3289       AddCompilerProc('WriteLn','Args:Arguments');
3290       AddCompilerProc('WriteStr','var S:String;Args:Arguments');
3291     end;
3292   end;
3293 
CheckContextIsParameternull3294   function CheckContextIsParameter(var Ok: boolean): boolean;
3295   // returns true, on error or context is parameter
3296   var
3297     VarNameAtom, ProcNameAtom: TAtomPosition;
3298     ParameterIndex, StartPos: integer;
3299     ContextExprStartPos: LongInt;
3300     StartInSubContext, HasInheritedKeyword, IsAttributeParams: Boolean;
3301     ExprType: TExpressionType;
3302     AttribParamNode: TCodeTreeNode;
3303   begin
3304     Result:=false;
3305     IsAttributeParams:=false;
3306     if (CursorNode.Desc=ctnParamsRound)
3307     and (CursorNode.Parent.Desc=ctnAttribParam) then begin
3308       IsAttributeParams:=true;
3309       AttribParamNode:=CursorNode.Parent;
3310       StartPos:=AttribParamNode.StartPos;
3311     end else if CursorNode.GetNodeOfTypes([ctnBeginBlock,ctnInitialization,ctnFinalization])<>nil
3312     then begin
3313       StartPos:=CursorNode.StartPos;
3314     end else begin
3315       // not in a begin..end block
3316       DebugLn(['TIdentCompletionTool.FindCodeContext.CheckContextIsParameter not in a begin block "',CursorNode.DescAsString,'"']);
3317       exit;
3318     end;
3319     // check if cursor is in a parameter list
3320     if not CheckParameterSyntax(StartPos, CleanCursorPos,
3321                                 VarNameAtom, ProcNameAtom, ParameterIndex)
3322     then begin
3323       if VarNameAtom.StartPos=0 then ;
3324       DebugLn(['TIdentCompletionTool.FindCodeContext.CheckContextIsParameter not in a parameter list']);
3325       exit;
3326     end;
3327     //DebugLn('CheckContextIsParameter Variable=',GetAtom(VarNameAtom),' Proc=',GetAtom(ProcNameAtom),' ParameterIndex=',dbgs(ParameterIndex));
3328 
3329     // it is a parameter -> create context
3330     Result:=true;
3331     if CurrentIdentifierContexts=nil then
3332       CurrentIdentifierContexts:=TCodeContextInfo.Create;
3333     CurrentIdentifierContexts.Tool:=Self;
3334     CurrentIdentifierContexts.ParameterIndex:=ParameterIndex+1;
3335     CurrentIdentifierContexts.ProcNameAtom:=ProcNameAtom;
3336     CurrentIdentifierContexts.ProcName:=GetAtom(ProcNameAtom);
3337 
3338     MoveCursorToAtomPos(ProcNameAtom);
3339     ReadNextAtom; // read opening bracket
3340     CurrentIdentifierContexts.StartPos:=CurPos.EndPos;
3341     // read closing bracket
3342     if ReadTilBracketClose(false) then
3343       CurrentIdentifierContexts.EndPos:=CurPos.StartPos
3344     else
3345       CurrentIdentifierContexts.EndPos:=SrcLen+1;
3346 
3347     if IsAttributeParams then begin
3348       debugln(['CheckContextIsParameter AttribParamNode={',ExtractNode(AttribParamNode,[]),'}']);
3349       Params.Flags:=fdfDefaultForExpressions+[fdfSkipClassForward];
3350       Params.Identifier:=@Src[ProcNameAtom.StartPos];
3351       Params.ContextNode:=AttribParamNode.FirstChild;
3352       ExprType:=FindExpressionTypeOfTerm(AttribParamNode.StartPos,ProcNameAtom.EndPos,Params,false);
3353       {$IFDEF VerboseCodeContext}
3354       debugln(['CheckContextIsParameter Attribute: ',ExprTypeToString(ExprType)]);
3355       {$ENDIF}
3356       if (ExprType.Context.Node = nil) or (ExprType.Context.Tool = nil) then
3357         exit;
3358       CurrentIdentifierList.Context:=ExprType.Context;
3359       Params.ContextNode:=ExprType.Context.Node;
3360       Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
3361       Params.SetIdentifier(Self,'*',@CollectAttributeConstructors);
3362       ExprType.Context.Tool.FindIdentifierInContext(Params);
3363     end else begin
3364       AddPredefinedProcs(CurrentIdentifierContexts,ProcNameAtom);
3365       FindCollectionContext(Params,ProcNameAtom.StartPos,CursorNode,
3366                             ExprType,ContextExprStartPos,StartInSubContext,
3367                             HasInheritedKeyword);
3368 
3369       if ContextExprStartPos=0 then ;
3370       {$IFDEF VerboseCodeContext}
3371       DebugLn(['CheckContextIsParameter StartInSubContext=',StartInSubContext,' ',ExprTypeToString(ExprType),' "',copy(ExprType.Context.Tool.Src,ExprType.Context.Node.StartPos-20,25),'"']);
3372       {$ENDIF}
3373       if (ExprType.Context.Node = nil) or (ExprType.Context.Tool = nil) then
3374       begin
3375         if ExprType.Desc in xtAllIdentPredefinedTypes then
3376         begin
3377           ExprType.Context.Node := CursorNode;
3378           ExprType.Context.Tool := Self;
3379         end else
3380           Exit;
3381       end;
3382 
3383       Params.ContextNode:=ExprType.Context.Node;
3384       if IsAttributeParams then begin
3385         Params.SetIdentifier(Self,'*',@CollectAttributeConstructors);
3386       end else begin
3387         Params.SetIdentifier(Self,@Src[ProcNameAtom.StartPos],@CollectAllContexts);
3388       end;
3389       Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
3390       if not StartInSubContext then
3391         Include(Params.Flags,fdfSearchInParentNodes);
3392       CurrentIdentifierList.Context:=ExprType.Context;
3393       {$IFDEF VerboseCodeContext}
3394       DebugLn('CheckContextIsParameter searching procedures, properties and variables ...');
3395       {$ENDIF}
3396       if ExprType.Desc in xtAllTypeHelperTypes then
3397         ExprType.Context.Tool.FindIdentifierInBasicTypeHelpers(ExprType.Desc, Params)
3398       else
3399         ExprType.Context.Tool.FindIdentifierInContext(Params);
3400     end;
3401 
3402     // gather declarations of all parameter lists
3403     {$IFDEF VerboseCodeContext}
3404     DebugLn('CheckContextIsParameter END');
3405     {$ENDIF}
3406     Ok:=true;
3407   end;
3408 
3409 var
3410   IdentifierList: TIdentifierList;
3411   IdentStartPos, IdentEndPos: integer;
3412 begin
3413   Result:=false;
3414   CodeContexts:=nil;
3415   IdentifierList:=nil;
3416   CurrentIdentifierContexts:=CodeContexts;
3417 
3418   ActivateGlobalWriteLock;
3419   try
3420     InitCollectIdentifiers(CursorPos,IdentifierList);
3421     if not ParseSourceTillCollectionStart(CursorPos,CleanCursorPos,CursorNode,
3422                                           IdentStartPos,IdentEndPos) then
3423       Exit;
3424     Params:=TFindDeclarationParams.Create(Self, CursorNode);
3425     try
3426       if IdentStartPos=0 then ;
3427       if IdentEndPos=0 then ;
3428 
3429       // find class and ancestors if existing (needed for protected identifiers)
3430       FindContextClassAndAncestorsAndExtendedClassOfHelper(CursorPos,
3431         FICTClassAndAncestorsAndExtClassOfHelper);
3432 
3433       if CursorNode<>nil then begin
3434         if not CheckContextIsParameter(Result) then begin
3435           DebugLn(['TIdentCompletionTool.FindCodeContext cursor not at parameter']);
3436           exit;
3437         end;
3438       end;
3439 
3440       if CurrentIdentifierContexts=nil then begin
3441         // create default
3442         AddCollectionContext(Self,CursorNode);
3443       end;
3444 
3445       Result:=true;
3446     finally
3447       if Result then begin
3448         CodeContexts:=CurrentIdentifierContexts;
3449         CurrentIdentifierContexts:=nil;
3450       end else begin
3451         FreeAndNil(CurrentIdentifierContexts);
3452       end;
3453       FreeListOfPFindContext(FICTClassAndAncestorsAndExtClassOfHelper);
3454       FreeAndNil(FIDCTFoundPublicProperties);
3455       Params.Free;
3456       ClearIgnoreErrorAfter;
3457     end;
3458   finally
3459     DeactivateGlobalWriteLock;
3460     FreeAndNil(CurrentIdentifierList);
3461   end;
3462 end;
3463 
FindAbstractMethodsnull3464 function TIdentCompletionTool.FindAbstractMethods(
3465   const CursorPos: TCodeXYPosition; out ListOfPCodeXYPosition: TFPList;
3466   SkipAbstractsInStartClass: boolean): boolean;
3467 const
3468   ProcAttr = [phpWithoutClassKeyword,phpWithHasDefaultValues];
3469   FlagIsAbstract = 0;
3470   FlagIsImplemented = 1;
3471 var
3472   ImplementedInterfaces: TStringToPointerTree;
3473   SearchedAncestors: TAVLTree;
3474   Procs: TAVLTree; // tree of TCodeTreeNodeExtension
3475 
3476   procedure AddProc(ATool: TFindDeclarationTool; ProcNode: TCodeTreeNode;
3477     IsAbstract: boolean);
3478   var
3479     ProcText: String;
3480     AVLNode: TAVLTreeNode;
3481     NodeExt: TCodeTreeNodeExtension;
3482   begin
3483     ProcText:=ATool.ExtractProcHead(ProcNode,ProcAttr);
3484     AVLNode:=FindCodeTreeNodeExtAVLNode(Procs,ProcText);
3485     if AVLNode<>nil then begin
3486       // known proc
3487       NodeExt:=TCodeTreeNodeExtension(AVLNode.Data);
3488       //debugln(['AddProc "',ProcText,'" WasImplemented=',NodeExt.Flags=1,' IsAbstract=',IsAbstract]);
3489       if NodeExt.Flags=FlagIsImplemented then
3490         exit; // already implemented
3491       if IsAbstract then
3492         exit; // already abstract
3493       NodeExt.Flags:=FlagIsImplemented;
3494       NodeExt.Node:=ProcNode;
3495       NodeExt.Data:=ATool;
3496     end else begin
3497       // new method
3498       //debugln(['AddProc "',ProcText,'" New IsAbstract=',IsAbstract]);
3499       NodeExt:=TCodeTreeNodeExtension.Create;
3500       NodeExt.Node:=ProcNode;
3501       NodeExt.Data:=ATool;
3502       NodeExt.Txt:=ProcText;
3503       if IsAbstract then
3504         NodeExt.Flags:=FlagIsAbstract
3505       else
3506         NodeExt.Flags:=FlagIsImplemented;
3507       Procs.Add(NodeExt);
3508     end;
3509   end;
3510 
3511   procedure CollectImplements(ClassNode: TCodeTreeNode);
3512   var
3513     Node: TCodeTreeNode;
3514     StopNode: TCodeTreeNode;
3515     InterfaceName: String;
3516   begin
3517     Node:=ClassNode.FirstChild;
3518     StopNode:=ClassNode.NextSkipChilds;
3519     while Node<>StopNode do begin
3520       if Node.Desc in AllClassBaseSections then begin
3521         Node:=Node.Next;
3522         continue;
3523       end else if Node.Desc=ctnProperty then begin
3524         if PropertyHasSpecifier(Node,'IMPLEMENTS',false) then begin
3525           ReadNextAtom;
3526           while AtomIsIdentifier do begin
3527             InterfaceName:=GetAtom;
3528             ReadNextAtom;
3529             if CurPos.Flag=cafPoint then begin
3530               ReadNextAtom;
3531               AtomIsIdentifierE(true);
3532               InterfaceName+='.'+GetAtom;
3533               ReadNextAtom;
3534             end;
3535             //debugln(['CollectImplements ',InterfaceName]);
3536             ImplementedInterfaces[InterfaceName]:=Node;
3537             if CurPos.Flag<>cafComma then break;
3538             ReadNextAtom;
3539           end;
3540         end;
3541       end else if Node.Desc=ctnProcedure then begin
3542         if ProcNodeHasSpecifier(Node,psABSTRACT) then begin
3543           if not SkipAbstractsInStartClass then
3544             AddProc(Self,Node,true);
3545         end else begin
3546           AddProc(Self,Node,false);
3547         end;
3548       end;
3549       Node:=Node.NextSkipChilds;
3550     end;
3551   end;
3552 
3553   procedure CollectAncestors(aTool: TFindDeclarationTool;
3554     ClassNode: TCodeTreeNode; IsStartClass: boolean); forward;
3555 
3556   procedure CollectAncestor(ATool: TFindDeclarationTool;
3557     InheritanceNode: TCodeTreeNode; SearchedAncestors: TAVLTree;
3558     IsStartClass: boolean);
3559   var
3560     Params: TFindDeclarationParams;
3561     ClassNode: TCodeTreeNode;
3562     StopNode: TCodeTreeNode;
3563     Node: TCodeTreeNode;
3564     IsInterface: Boolean;
3565   begin
3566     //debugln(['CollectAncestor Ancestor=',ATool.ExtractIdentifierWithPoints(InheritanceNode.StartPos,false)]);
3567     Params:=TFindDeclarationParams.Create;
3568     try
3569       if not ATool.FindAncestorOfClassInheritance(InheritanceNode,Params,true)
3570       then exit;
3571       ATool:=Params.NewCodeTool;
3572       ClassNode:=Params.NewNode;
3573       if SearchedAncestors.Find(ClassNode)<>nil then
3574         exit; // already searched
3575       SearchedAncestors.Add(ClassNode);
3576       // check all procs of this ancestor
3577       StopNode:=ClassNode.NextSkipChilds;
3578       Node:=ClassNode.FirstChild;
3579       IsInterface:=ClassNode.Desc in AllClassInterfaces;
3580       if IsInterface and (not IsStartClass) then
3581         exit;
3582       while Node<>StopNode do begin
3583         if Node.Desc in AllClassBaseSections then begin
3584           Node:=Node.Next;
3585           continue;
3586         end else if Node.Desc=ctnProcedure then begin
3587           if IsInterface
3588           or ATool.ProcNodeHasSpecifier(Node,psABSTRACT) then
3589             AddProc(ATool,Node,true)
3590           else
3591             AddProc(ATool,Node,false);
3592         end;
3593         Node:=Node.NextSkipChilds;
3594       end;
3595       CollectAncestors(ATool,ClassNode,false);
3596     finally
3597       Params.Free;
3598     end;
3599   end;
3600 
3601   procedure CollectAncestors(aTool: TFindDeclarationTool;
3602     ClassNode: TCodeTreeNode; IsStartClass: boolean);
3603   var
3604     InheritanceNode: TCodeTreeNode;
3605     AncestorName: String;
3606     Node: TCodeTreeNode;
3607   begin
3608     //debugln(['CollectAncestors of Class=',aTool.ExtractClassName(ClassNode,false)]);
3609     InheritanceNode:=ATool.FindInheritanceNode(ClassNode);
3610     if (InheritanceNode=nil)
3611     or (InheritanceNode.FirstChild=nil) then begin
3612       // no ancestors
3613       exit;
3614     end;
3615     Node:=InheritanceNode.FirstChild;
3616     while Node<>nil do begin
3617       InheritanceNode:=Node;
3618       Node:=Node.NextBrother;
3619       if InheritanceNode.Desc=ctnIdentifier then begin
3620         if IsStartClass then begin
3621           AncestorName:=ATool.ExtractIdentifierWithPoints(InheritanceNode.StartPos,true);
3622           if ImplementedInterfaces.FindNode(AncestorName)<>nil then
3623             continue;
3624         end;
3625         CollectAncestor(ATool,InheritanceNode,SearchedAncestors,IsStartClass);
3626       end;
3627     end;
3628   end;
3629 
3630 var
3631   CleanCursorPos: integer;
3632   CursorNode: TCodeTreeNode;
3633   ClassNode: TCodeTreeNode;
3634   AVLNode: TAVLTreeNode;
3635   NodeExt: TCodeTreeNodeExtension;
3636   ProcXYPos: TCodeXYPosition;
3637   ATool: TFindDeclarationTool;
3638 begin
3639   Result:=false;
3640   ListOfPCodeXYPosition:=nil;
3641   ImplementedInterfaces:=nil;
3642   Procs:=nil;
3643   SearchedAncestors:=nil;
3644   try
3645     BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanCursorPos,
3646                             [btSetIgnoreErrorPos]);
3647 
3648     // find node at position
3649     CursorNode:=BuildSubTreeAndFindDeepestNodeAtPos(CleanCursorPos,true);
3650 
3651     // if cursor is on type node, find class node
3652     if CursorNode.Desc=ctnTypeDefinition then
3653       CursorNode:=CursorNode.FirstChild
3654     else if CursorNode.Desc=ctnGenericType then
3655       CursorNode:=CursorNode.LastChild
3656     else
3657       CursorNode:=FindClassOrInterfaceNode(CursorNode);
3658 
3659     if (CursorNode=nil)
3660     or (not (CursorNode.Desc in AllClassObjects))
3661     or ((CursorNode.SubDesc and ctnsForwardDeclaration)>0) then begin
3662       MoveCursorToCleanPos(CleanCursorPos);
3663       RaiseException(20170421201053,'TIdentCompletionTool.FindAbstractMethods cursor is not in a class');
3664     end;
3665     ClassNode:=CursorNode;
3666 
3667     // search class for implemented interfaces and method
3668     ImplementedInterfaces:=TStringToPointerTree.Create(false);
3669     Procs:=TAVLTree.Create(@CompareCodeTreeNodeExt);
3670     CollectImplements(ClassNode);
3671 
3672     // search all ancestors
3673     SearchedAncestors:=TAVLTree.Create;
3674     SearchedAncestors.Add(ClassNode);
3675     CollectAncestors(Self,ClassNode,true);
3676 
3677     // AddCodePosition for each abstract method
3678     AVLNode:=Procs.FindLowest;
3679     while AVLNode<>nil do begin
3680       NodeExt:=TCodeTreeNodeExtension(AVLNode.Data);
3681       if NodeExt.Flags=FlagIsAbstract then begin
3682         ATool:=TFindDeclarationTool(NodeExt.Data);
3683         if not ATool.CleanPosToCaret(NodeExt.Node.StartPos,ProcXYPos) then
3684           raise Exception.Create('TIdentCompletionTool.FindAbstractMethods inconsistency');
3685         AddCodePosition(ListOfPCodeXYPosition,ProcXYPos);
3686       end;
3687       AVLNode:=Procs.FindSuccessor(AVLNode);
3688     end;
3689 
3690     Result:=true;
3691   finally
3692     DisposeAVLTree(Procs);
3693     ImplementedInterfaces.Free;
3694     SearchedAncestors.Free;
3695   end;
3696 end;
3697 
TIdentCompletionTool.GetValuesOfCaseVariablenull3698 function TIdentCompletionTool.GetValuesOfCaseVariable(
3699   const CursorPos: TCodeXYPosition; List: TStrings; WithTypeDefIfScoped: boolean
3700   ): boolean;
3701 var
3702   CleanCursorPos: integer;
3703   CursorNode: TCodeTreeNode;
3704   CaseAtom: TAtomPosition;
3705   Params: TFindDeclarationParams;
3706   EndPos: LongInt;
3707   ExprType: TExpressionType;
3708   Node: TCodeTreeNode;
3709   Tool: TFindDeclarationTool;
3710   EnumPrefix: string;
3711 begin
3712   Result:=false;
3713   ActivateGlobalWriteLock;
3714   Params:=nil;
3715   try
3716     BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanCursorPos,
3717                            [btSetIgnoreErrorPos]);
3718 
3719     // find node at position
3720     CursorNode:=BuildSubTreeAndFindDeepestNodeAtPos(CleanCursorPos,true);
3721 
3722     // find keyword case
3723     MoveCursorToNodeStart(CursorNode);
3724     CaseAtom:=CleanAtomPosition;
3725     repeat
3726       ReadNextAtom;
3727       if UpAtomIs('CASE') then
3728         CaseAtom:=CurPos
3729     until (CurPos.EndPos>SrcLen) or (CurPos.EndPos>CleanCursorPos);
3730     if CaseAtom.StartPos<1 then begin
3731       debugln(['TIdentCompletionTool.GetValuesOfCaseVariable "case" not found']);
3732       exit;
3733     end;
3734 
3735     // find case variable
3736     EndPos:=FindEndOfExpression(CaseAtom.EndPos);
3737     if EndPos>CleanCursorPos then
3738       EndPos:=CleanCursorPos;
3739     //DebugLn(['TIdentCompletionTool.GetValuesOfCaseVariable Expr=',dbgstr(copy(Src,CaseAtom.EndPos,EndPos-CaseAtom.EndPos))]);
3740 
3741     Params:=TFindDeclarationParams.Create(Self, CursorNode);
3742     Params.Flags:=fdfDefaultForExpressions+[fdfFunctionResult];
3743     ExprType:=FindExpressionTypeOfTerm(CaseAtom.EndPos,EndPos,Params,true);
3744     //DebugLn(['TIdentCompletionTool.GetValuesOfCaseVariable Type=',ExprTypeToString(ExprType)]);
3745 
3746     if ExprType.Desc=xtContext then begin
3747       // resolve aliases and properties
3748       Params.Clear;
3749       Params.Flags:=fdfDefaultForExpressions;
3750       ExprType.Context:=ExprType.Context.Tool.FindBaseTypeOfNode(Params,
3751                                  ExprType.Context.Node);
3752     end;
3753 
3754     case ExprType.Desc of
3755 
3756     xtBoolean,xtByteBool,xtWordBool,xtLongBool,xtQWordBool:
3757       begin
3758         List.Add('True');
3759         List.Add('False');
3760       end;
3761 
3762     xtContext:
3763       begin
3764         Node:=ExprType.Context.Node;
3765         Tool:=ExprType.Context.Tool;
3766         if Node=nil then exit;
3767         case Node.Desc of
3768 
3769         ctnEnumerationType:
3770           begin
3771             if WithTypeDefIfScoped
3772             and (Tool.Scanner.GetDirectiveValueAt(sdScopedEnums, Node.StartPos) = '1') then
3773             begin
3774               Tool.MoveCursorToCleanPos(Node.Parent.StartPos);
3775               Tool.ReadNextAtom;
3776               EnumPrefix := Tool.GetAtom+'.';
3777             end else
3778               EnumPrefix := '';
3779 
3780             Node:=Node.FirstChild;
3781             while Node<>nil do begin
3782               List.Add(EnumPrefix+GetIdentifier(@Tool.Src[Node.StartPos]));
3783               Node:=Node.NextBrother;
3784             end;
3785           end;
3786 
3787         else
3788           debugln(['TIdentCompletionTool.GetValuesOfCaseVariable not an enum: ',Node.DescAsString]);
3789           exit;
3790         end;
3791       end;
3792     else
3793       exit;
3794     end;
3795 
3796     Result:=true;
3797   finally
3798     Params.Free;
3799     DeactivateGlobalWriteLock;
3800   end;
3801 end;
3802 
3803 procedure TIdentCompletionTool.CalcMemSize(Stats: TCTMemStats);
3804 var
3805   Node: TAVLTreeNode;
3806   Ext: TCodeTreeNodeExtension;
3807   m: PtrUint;
3808 begin
3809   inherited CalcMemSize(Stats);
3810   if FICTClassAndAncestorsAndExtClassOfHelper<>nil then
3811     Stats.Add('TIdentCompletionTool.ClassAndAncestorsAndExtClassOfHelper',
3812         FICTClassAndAncestorsAndExtClassOfHelper.Count*(SizeOf(TAVLTreeNode)+SizeOf(TCodeXYPosition)));
3813   if FIDCTFoundPublicProperties<>nil then
3814     Stats.Add('TIdentCompletionTool.FoundPublicProperties',
3815               FIDCTFoundPublicProperties.Count*SizeOf(TAVLTreeNode));
3816   if FIDTFoundMethods<>nil then begin
3817     m:=PtrUint(FIDTFoundMethods.Count)*SizeOf(TAVLTreeNode);
3818     Node:=FIDTFoundMethods.FindLowest;
3819     while Node<>nil do begin
3820       Ext:=TCodeTreeNodeExtension(Node.Data);
3821       inc(m,Ext.CalcMemSize);
3822       Node:=FIDTFoundMethods.FindSuccessor(Node);
3823     end;
3824     STats.Add('TIdentCompletionTool.FoundMethods',m);
3825   end;
3826   if CurrentIdentifierList<>nil then
3827     Stats.Add('TIdentCompletionTool.CurrentIdentifierList',
3828       CurrentIdentifierList.CalcMemSize);
3829   if CurrentIdentifierContexts<>nil then
3830     Stats.Add('TIdentCompletionTool.CurrentContexts',
3831               CurrentIdentifierContexts.CalcMemSize);
3832 end;
3833 
3834 { TIdentifierListItem }
3835 
GetParamTypeListnull3836 function TIdentifierListItem.GetParamTypeList: string;
3837 var
3838   ANode: TCodeTreeNode;
3839 begin
3840   if not (iliParamTypeListValid in Flags) then begin
3841     // Note: if you implement param lists for other than ctnProcedure, check
3842     //       CompareParamList
3843     ANode:=Node;
3844     FParamTypeList:='';
3845     if (ANode<>nil) and (ANode.Desc=ctnProcedure) then begin
3846       try
3847         FParamTypeList:=Tool.ExtractProcHead(ANode,
3848            [phpWithoutClassKeyword,phpWithoutClassName,
3849             phpWithoutName,phpInUpperCase]);
3850         //debugln('TIdentifierListItem.GetParamTypeList A ',GetIdentifier(Identifier),' ',Tool.MainFilename,' ',dbgs(CurNode.StartPos));
3851       except
3852         on E: ECodeToolError do ; // ignore syntax errors
3853       end;
3854     end;
3855     Include(Flags,iliParamTypeListValid);
3856   end;
3857   Result:=FParamTypeList;
3858 end;
3859 
GetParamNameListnull3860 function TIdentifierListItem.GetParamNameList: string;
3861 var
3862   ANode: TCodeTreeNode;
3863 begin
3864   if not (iliParamNameListValid in Flags) then begin
3865     // Note: if you implement param lists for other than ctnProcedure, check
3866     //       CompareParamList
3867     ANode:=Node;
3868     FParamNameList:='';
3869     if (ANode<>nil) and (ANode.Desc=ctnProcedure) then begin
3870       try
3871         FParamNameList:=Tool.ExtractProcHead(ANode,
3872            [phpWithoutClassKeyword,phpWithoutClassName,
3873             phpWithoutName,phpInUpperCase,phpWithParameterNames]);
3874         //debugln('TIdentifierListItem.GetParamNameList A ',GetIdentifier(Identifier),' ',Tool.MainFilename,' ',dbgs(CurNode.StartPos));
3875       except
3876         on E: ECodeToolError do ; // ignore syntax errors
3877       end;
3878     end;
3879     Include(Flags,iliParamNameListValid);
3880   end;
3881   Result:=FParamNameList;
3882 end;
3883 
TIdentifierListItem.GetNodenull3884 function TIdentifierListItem.GetNode: TCodeTreeNode;
3885 begin
3886   Result:=nil;
3887   if Tool=nil then
3888     exit;
3889 
3890   if (iliNodeValid in Flags)
3891   and (FToolNodesDeletedStep<>Tool.NodesDeletedChangeStep) then
3892     Exclude(Flags,iliNodeValid);
3893 
3894   if (not (iliNodeValid in Flags)) then begin
3895     if iliNodeHashValid in Flags then begin
3896       RestoreNode;
3897       if (iliNodeValid in Flags) then begin
3898         Result:=FNode;
3899       end;
3900     end;
3901   end else begin
3902     if FToolNodesDeletedStep=Tool.NodesDeletedChangeStep then begin
3903       Result:=FNode;
3904     end else begin
3905       if not (iliNodeGoneWarned in Flags) then begin
3906         DebugLn(['TIdentifierListItem.GetNode node ',Identifier,' is gone from ',Tool.MainFilename]);
3907         Include(Flags,iliNodeGoneWarned);
3908       end;
3909       FNode:=nil;
3910     end;
3911   end;
3912 end;
3913 
3914 procedure TIdentifierListItem.SetNode(const AValue: TCodeTreeNode);
3915 
3916   procedure RaiseToolMissing;
3917   begin
3918     raise Exception.Create('TIdentifierListItem.SetNode Node without Tool');
3919   end;
3920 
3921 begin
3922   FNode:=AValue;
3923   Include(Flags,iliNodeValid);
3924   Exclude(Flags,iliNodeHashValid);
3925   if (FNode<>nil) and (Tool=nil) then
3926     RaiseToolMissing;
3927   if (Tool<>nil) then
3928     FToolNodesDeletedStep:=Tool.NodesDeletedChangeStep;
3929   if (FNode<>nil) then
3930     StoreNodeHash;
3931 end;
3932 
3933 procedure TIdentifierListItem.SetParamTypeList(const AValue: string);
3934 begin
3935   FParamTypeList:=AValue;
3936   Include(Flags,iliParamTypeListValid);
3937 end;
3938 
3939 procedure TIdentifierListItem.SetParamNameList(const AValue: string);
3940 begin
3941   FParamNameList:=AValue;
3942   Include(Flags,iliParamNameListValid);
3943 end;
3944 
3945 procedure TIdentifierListItem.SetResultType(const AValue: string);
3946 begin
3947   FResultType := AValue;
3948   Include(Flags, iliResultTypeValid);
3949 end;
3950 
AsStringnull3951 function TIdentifierListItem.AsString: string;
3952 var
3953   ANode: TCodeTreeNode;
3954 begin
3955   WriteStr(Result, Compatibility);
3956   if HasChilds then
3957     Result:=Result+' HasChilds'
3958   else
3959     Result:=Result+' HasNoChilds';
3960   Result:=Result+' History='+IntToStr(HistoryIndex);
3961   Result:=Result+' Ident='+Identifier;
3962   Result:=Result+' Lvl='+IntToStr(Level);
3963   if Tool<>nil then
3964     Result:=Result+' File='+Tool.MainFilename;
3965   ANode:=Node;
3966   if ANode<>nil then
3967     Result:=Result+' Node='+ANode.DescAsString
3968       +' "'+StringToPascalConst(copy(Tool.Src,ANode.StartPos,50))+'"';
3969 end;
3970 
3971 procedure TIdentifierListItem.BeautifyIdentifier(IdentList: TIdentifierList);
3972 begin
3973   // can be overridden
3974 end;
3975 
GetDescnull3976 function TIdentifierListItem.GetDesc: TCodeTreeNodeDesc;
3977 var
3978   ANode: TCodeTreeNode;
3979 begin
3980   ANode:=Node;
3981   if ANode<>nil then
3982     Result:=ANode.Desc
3983   else
3984     Result:=DefaultDesc;
3985 end;
3986 
3987 constructor TIdentifierListItem.Create(
3988   NewCompatibility: TIdentifierCompatibility; NewHasChilds: boolean;
3989   NewHistoryIndex: integer; NewIdentifier: PChar; NewLevel: integer;
3990   NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool;
3991   NewDefaultDesc: TCodeTreeNodeDesc);
3992 begin
3993   Compatibility:=NewCompatibility;
3994   if NewHasChilds then Include(FLags,iliHasChilds);
3995   HistoryIndex:=NewHistoryIndex;
3996   Identifier:=GetIdentifier(NewIdentifier);
3997   Level:=NewLevel;
3998   Tool:=NewTool;
3999   Node:=NewNode;
4000   DefaultDesc:=NewDefaultDesc;
4001   BaseExprType:=CleanExpressionType;
4002 end;
4003 
IsProcNodeWithParamsnull4004 function TIdentifierListItem.IsProcNodeWithParams: boolean;
4005 var
4006   ANode: TCodeTreeNode;
4007   StartPos: Integer;
4008 begin
4009   Result:=(GetDesc=ctnProcedure);
4010   if not Result then exit;
4011   if (iliParamNameListValid in Flags) then begin
4012     StartPos:=1;
4013     while (StartPos<=length(FParamTypeList))
4014     and (FParamTypeList[StartPos] in [' ',#9,'(','[']) do
4015       inc(StartPos);
4016     if (StartPos<=length(FParamTypeList))
4017     and (FParamTypeList[StartPos] in [')',']',';']) then
4018       exit(false)
4019     else
4020       exit(true);
4021   end else if (iliParamTypeListValid in Flags) then begin
4022     // the type list does not contain names
4023     // so a () could be empty or (var buf)
4024     StartPos:=1;
4025     while (StartPos<=length(FParamTypeList))
4026     and (FParamTypeList[StartPos] in [' ',#9,'(','[']) do
4027       inc(StartPos);
4028     if (StartPos<=length(FParamTypeList))
4029     and (not (FParamTypeList[StartPos] in [')',']',';'])) then
4030       exit(true);
4031   end;
4032   ANode:=Node;
4033   Result:=(ANode<>nil) and Tool.ProcNodeHasParamList(ANode);
4034 end;
4035 
TIdentifierListItem.IsPropertyWithParamsnull4036 function TIdentifierListItem.IsPropertyWithParams: boolean;
4037 var
4038   ANode: TCodeTreeNode;
4039 begin
4040   if not (iliHasParamListValid in Flags) then begin
4041     Include(Flags,iliHasParamListValid);
4042     ANode:=Node;
4043     if (ANode<>nil) and Tool.PropertyNodeHasParamList(ANode) then
4044       Include(Flags,iliHasParamList)
4045     else
4046       Exclude(Flags,iliHasParamList);
4047   end;
4048   Result:=iliHasParamList in Flags;
4049 end;
4050 
IsPropertyReadOnlynull4051 function TIdentifierListItem.IsPropertyReadOnly: boolean;
4052 var
4053   ANode: TCodeTreeNode;
4054 begin
4055   if not (iliIsReadOnlyValid in Flags) then begin
4056     Include(Flags,iliIsReadOnlyValid);
4057     ANode:=Node;
4058     if (ANode<>nil) and Tool.PropertyHasSpecifier(ANode,'read',false)
4059     and not Tool.PropertyHasSpecifier(ANode,'write',false) then
4060       Include(Flags,iliIsReadOnly)
4061     else
4062       Exclude(Flags,iliIsReadOnly);
4063   end;
4064   Result:=iliIsReadOnly in Flags;
4065 end;
4066 
GetHintModifiersnull4067 function TIdentifierListItem.GetHintModifiers: TPascalHintModifiers;
4068 var
4069   ANode: TCodeTreeNode;
4070 begin
4071   Result:=[];
4072   if not (iliHintModifiersValid in Flags) then begin
4073     Include(Flags,iliHintModifiersValid);
4074     ANode:=Node;
4075     if ANode<>nil then begin
4076       Result:=Tool.GetHintModifiers(ANode);
4077       if phmDeprecated in Result then Include(Flags,iliIsDeprecated);
4078       if phmPlatform in Result then Include(Flags,iliIsPlatform);
4079       if phmLibrary in Result then Include(Flags,iliIsLibrary);
4080       if phmUnimplemented in Result then Include(Flags,iliIsUnimplemented);
4081       if phmExperimental in Result then Include(Flags,iliIsExperimental);
4082     end;
4083   end else begin
4084     if iliIsDeprecated in Flags then Include(Result,phmDeprecated);
4085     if iliIsPlatform in Flags then Include(Result,phmPlatform);
4086     if iliIsLibrary in Flags then Include(Result,phmLibrary);
4087     if iliIsUnimplemented in Flags then Include(Result,phmUnimplemented);
4088     if iliIsExperimental in Flags then Include(Result,phmExperimental);
4089   end;
4090 end;
4091 
TIdentifierListItem.CheckHasChildsnull4092 function TIdentifierListItem.CheckHasChilds: boolean;
4093 // returns true if test was successful
4094 var
4095   ANode: TCodeTreeNode;
4096 begin
4097   Result:=false;
4098   if GetDesc in AllClasses then begin
4099     Result:=true;
4100     exit;
4101   end;
4102   ANode:=Node;
4103   if ANode=nil then exit;
4104   UpdateBaseContext;
4105   if (BaseExprType.Desc=xtContext)
4106     and (BaseExprType.Context.Node<>nil)
4107     and (BaseExprType.Context.Node.Desc in AllClasses)
4108   then
4109     Include(Flags,iliHasChilds);
4110   Result:=true;
4111 end;
4112 
TIdentifierListItem.CanBeAssignednull4113 function TIdentifierListItem.CanBeAssigned: boolean;
4114 var
4115   ANode: TCodeTreeNode;
4116 begin
4117   Result:=false;
4118   ANode:=Node;
4119   if (ANode=nil) then exit;
4120   if (GetDesc=ctnVarDefinition) then
4121     Result:=true;
4122   if (ANode.Desc in [ctnProperty,ctnGlobalProperty]) then begin
4123     if Tool.PropertyHasSpecifier(ANode,'write') then exit(true);
4124     if Tool.PropNodeIsTypeLess(ANode) then begin
4125       exit(true);// ToDo: search the real property definition
4126     end;
4127   end;
4128 end;
4129 
4130 procedure TIdentifierListItem.UpdateBaseContext;
4131 var
4132   Params: TFindDeclarationParams;
4133   ANode: TCodeTreeNode;
4134 begin
4135   if (iliBaseExprTypeValid in Flags) then exit;
4136   Include(Flags,iliBaseExprTypeValid);
4137   BaseExprType:=CleanExpressionType;
4138   BaseExprType.Desc:=xtNone;
4139   ANode:=Node;
4140   if (ANode<>nil) and (Tool<>nil) then begin
4141     Tool.ActivateGlobalWriteLock;
4142     Params:=TFindDeclarationParams.Create(Tool, ANode);
4143     try
4144       if ANode.HasParentOfType(ctnGenericType) then exit;
4145       BaseExprType.Context:=Tool.FindBaseTypeOfNode(Params,ANode);
4146       if (BaseExprType.Context.Node<>nil) then
4147         BaseExprType.Desc:=xtContext;
4148     finally
4149       Params.Free;
4150       Tool.DeactivateGlobalWriteLock;
4151     end;
4152   end;
4153 end;
4154 
TIdentifierListItem.HasChildsnull4155 function TIdentifierListItem.HasChilds: boolean;
4156 begin
4157   Result:=iliHasChilds in Flags;
4158 end;
4159 
HasIndexnull4160 function TIdentifierListItem.HasIndex: boolean;
4161 // check if edged bracket can be used []
4162 var
4163   ANode: TCodeTreeNode;
4164 begin
4165   if not (iliHasIndexValid in Flags) then begin
4166     UpdateBaseContext;
4167     if BaseExprType.Desc in (xtAllStringConvertibles+xtAllWideStringConvertibles)
4168     then begin
4169       // strings, widestrings and PChar
4170       Include(Flags,iliHasIndex);
4171     end else if (BaseExprType.Desc=xtContext) and (BaseExprType.Context.Node<>nil)
4172     then begin
4173       //debugln(['TIdentifierListItem.HasIndex ',BaseExprType.Context.Node.DescAsString]);
4174       ANode:=BaseExprType.Context.Node;
4175       case ANode.Desc of
4176       ctnRangedArrayType,ctnOpenArrayType: Include(Flags,iliHasIndex);
4177       end;
4178     end;
4179   end;
4180   Result:=iliHasIndex in Flags;
4181 end;
4182 
TIdentifierListItem.IsFunctionnull4183 function TIdentifierListItem.IsFunction: boolean;
4184 var
4185   ANode: TCodeTreeNode;
4186 begin
4187   if not (iliIsFunctionValid in Flags) then
4188   begin
4189     ANode := Node;
Nodenull4190     if (ANode <> nil) and Tool.NodeIsFunction(ANode) then
4191       Include(Flags, iliIsFunction);
4192     Include(Flags, iliIsFunctionValid);
4193   end;
innull4194   Result := iliIsFunction in Flags;
4195 end;
4196 
TIdentifierListItem.IsConstructornull4197 function TIdentifierListItem.IsConstructor: boolean;
4198 var
4199   ANode: TCodeTreeNode;
4200 begin
4201   if not (iliIsConstructorValid in Flags) then
4202   begin
4203     ANode := Node;
4204     if (ANode <> nil) and Tool.NodeIsConstructor(ANode) then
4205       Include(Flags, iliIsConstructor);
4206     Include(Flags, iliIsConstructorValid);
4207   end;
4208   Result := iliIsConstructor in Flags;
4209 end;
4210 
TIdentifierListItem.IsDestructornull4211 function TIdentifierListItem.IsDestructor: boolean;
4212 var
4213   ANode: TCodeTreeNode;
4214 begin
4215   if not (iliIsDestructorValid in Flags) then
4216   begin
4217     ANode := Node;
4218     if (ANode <> nil) and Tool.NodeIsDestructor(ANode) then
4219       Include(Flags, iliIsDestructor);
4220     Include(Flags, iliIsDestructorValid);
4221   end;
4222   Result := iliIsDestructor in Flags;
4223 end;
4224 
TIdentifierListItem.IsAbstractMethodnull4225 function TIdentifierListItem.IsAbstractMethod: boolean;
4226 var
4227   ANode: TCodeTreeNode;
4228 begin
4229   if not (iliIsAbstractMethodValid in Flags) then begin
4230     ANode:=Node;
4231     if (ANode<>nil)
4232     and Tool.ProcNodeHasSpecifier(ANode,psABSTRACT) then
4233       Include(Flags,iliIsAbstractMethod);
4234     Include(Flags,iliIsAbstractMethodValid);
4235   end;
4236   Result:=iliIsAbstractMethod in Flags;
4237 end;
4238 
TIdentifierListItem.TryIsAbstractMethodnull4239 function TIdentifierListItem.TryIsAbstractMethod: boolean;
4240 begin
4241   try
4242     Result:=IsAbstractMethod;
4243   except
4244     Result:=false;
4245   end;
4246 end;
4247 
4248 procedure TIdentifierListItem.Clear;
4249 begin
4250   FParamTypeList:='';
4251   FResultType:='';
4252   Compatibility:=icompUnknown;
4253   HistoryIndex:=0;
4254   Identifier:='';
4255   Level:=0;
4256   FNode:=nil;
4257   Tool:=nil;
4258   DefaultDesc:=ctnNone;
4259   Flags:=[];
4260   BaseExprType:=CleanExpressionType;
4261 end;
4262 
4263 procedure TIdentifierListItem.UnbindNode;
4264 begin
4265   if FNode=nil then exit;
4266   StoreNodeHash;
4267   Exclude(Flags,iliNodeValid);
4268   FNode:=nil;
4269 end;
4270 
4271 procedure TIdentifierListItem.StoreNodeHash;
4272 begin
4273   Include(Flags,iliNodeHashValid);
4274   FNodeStartPos:=FNode.StartPos;
4275   FNodeDesc:=FNode.Desc;
4276   FNodeHash:=GetNodeHash(FNode);
4277   //DebugLn(['TIdentifierListItem.StoreNodeHash ',Identifier,' Pos=',FNodeStartPos,' Hash=',FNodeHash]);
4278 end;
4279 
RestoreNodenull4280 function TIdentifierListItem.RestoreNode: boolean;
4281 var
4282   NewNode: TCodeTreeNode;
4283   NewHash: Cardinal;
4284 begin
4285   if not (iliNodeHashValid in Flags) then exit(true);
4286   //DebugLn(['TIdentifierListItem.RestoreNode ',Identifier]);
4287   NewNode:=Tool.BuildSubTreeAndFindDeepestNodeAtPos(FNodeStartPos,false);
4288   Result:=false;
4289   if (NewNode=nil) or (NewNode.StartPos<>FNodeStartPos)
4290   or (NewNode.Desc<>FNodeDesc) then begin
4291     DebugLn(['TIdentifierListItem.RestoreNode not found: ',Identifier]);
4292     Exclude(Flags,iliNodeHashValid);
4293     exit;
4294   end;
4295   NewHash:=GetNodeHash(NewNode);
4296   if NewHash<>FNodeHash then begin
4297     DebugLn(['TIdentifierListItem.RestoreNode hash changed: ',Identifier]);
4298     Exclude(Flags,iliNodeHashValid);
4299     exit;
4300   end;
4301   //DebugLn(['TIdentifierListItem.RestoreNode Success ',Identifier]);
4302   Node:=NewNode;
4303   Result:=true;
4304 end;
4305 
GetNodeHashnull4306 function TIdentifierListItem.GetNodeHash(ANode: TCodeTreeNode): Cardinal;
4307 var
4308   StartPos: LongInt;
4309   EndPos: LongInt;
4310 begin
4311   case ANode.Desc of
4312     ctnVarDefinition,ctnConstDefinition,ctnTypeDefinition,ctnGenericType:
4313       ANode:=Tool.FindDefinitionNameNode(ANode);
4314   end;
4315   if ANode<>nil then
4316   begin
4317     StartPos:=ANode.StartPos;
4318     EndPos:=StartPos+20;
4319     if EndPos>ANode.EndPos then EndPos:=ANode.EndPos;
4320     Result:=crc32(0, @Tool.Src[StartPos], EndPos-StartPos);
4321   end else
4322     Result:=0;
4323 end;
4324 
TIdentifierListItem.CompareParamListnull4325 function TIdentifierListItem.CompareParamList(CompareItem: TIdentifierListItem
4326   ): integer;
4327 var
4328   ANode: TCodeTreeNode;
4329   CmpNode: TCodeTreeNode;
4330 begin
4331   Result:=0;
4332   if Self=CompareItem then exit;
4333   ANode:=Node;
4334   CmpNode:=CompareItem.Node;
4335   if (ANode=CmpNode) then exit;
4336   if (ANode=nil) or (CmpNode=nil) then exit;
4337   if (ANode.Desc<>ctnProcedure) or (CmpNode.Desc<>ctnProcedure) then
4338     exit;
4339   {DbgOut('TIdentifierListItem.CompareParamList ',GetIdentifier(Identifier),'=',GetIdentifier(CompareItem.Identifier));
4340   if Node<>nil then
4341     DbgOut(' Self=',Tool.MainFilename,' ',dbgs(Node.StartPos));
4342   if CompareItem.Node<>nil then
4343     DbgOut(' Other=',CompareItem.Tool.MainFilename,' ',dbgs(CompareItem.Node.StartPos));
4344   debugln('');}
4345   Result:=CompareTextIgnoringSpace(ParamTypeList,CompareItem.ParamTypeList,false);
4346 end;
4347 
TIdentifierListItem.CompareParamListnull4348 function TIdentifierListItem.CompareParamList(
4349   CompareItem: TIdentifierListSearchItem): integer;
4350 begin
4351   if (ParamTypeList='') and (CompareItem.ParamList='') then
4352     exit(0);
4353   Result:=CompareTextIgnoringSpace(ParamTypeList,CompareItem.ParamList,false);
4354 end;
4355 
CalcMemSizenull4356 function TIdentifierListItem.CalcMemSize: PtrUInt;
4357 begin
4358   Result:=PtrUInt(InstanceSize)
4359     +MemSizeString(FParamTypeList)
4360     +SizeOf(FNodeHash)
4361     +MemSizeString(Identifier);
4362 end;
4363 
4364 { TIdentifierHistoryList }
4365 
4366 procedure TIdentifierHistoryList.SetCapacity(const AValue: integer);
4367 begin
4368   if FCapacity=AValue then exit;
4369   FCapacity:=AValue;
4370   if FCapacity<1 then FCapacity:=1;
4371   while (FItems.Count>0) and (FItems.Count>=FCapacity) do
4372     FItems.FreeAndDelete(FItems.FindHighest);
4373 end;
4374 
FindItemnull4375 function TIdentifierHistoryList.FindItem(NewItem: TIdentifierListItem
4376   ): TAVLTreeNode;
4377 begin
4378   if NewItem<>nil then
4379     Result:=FItems.FindKey(NewItem,@CompareIdentItemWithHistListItem)
4380   else
4381     Result:=nil;
4382 end;
4383 
4384 constructor TIdentifierHistoryList.Create;
4385 begin
4386   FItems:=TAVLTree.Create(@CompareIdentHistListItem);
4387   FCapacity:=30;
4388 end;
4389 
4390 destructor TIdentifierHistoryList.Destroy;
4391 begin
4392   Clear;
4393   FItems.Free;
4394   inherited Destroy;
4395 end;
4396 
4397 procedure TIdentifierHistoryList.Clear;
4398 begin
4399   FItems.FreeAndClear;
4400 end;
4401 
4402 procedure TIdentifierHistoryList.Add(NewItem: TIdentifierListItem);
4403 var
4404   OldAVLNode: TAVLTreeNode;
4405   NewHistItem: TIdentHistListItem;
4406   AnAVLNode: TAVLTreeNode;
4407   AdjustIndex: Integer;
4408   AnHistItem: TIdentHistListItem;
4409 begin
4410   if NewItem=nil then exit;
4411   OldAVLNode:=FindItem(NewItem);
4412   {$IFDEF ShowHistory}
4413   DebugLn('TIdentifierHistoryList.Add Count=',Count,' Found=',OldAVLNode<>nil,
4414     ' ITEM: ',NewItem.AsString);
4415   {$ENDIF}
4416   if OldAVLNode<>nil then begin
4417     // already in tree
4418     NewHistItem:=TIdentHistListItem(OldAVLNode.Data);
4419     if NewHistItem.HistoryIndex=0 then exit;
4420     // must be moved -> remove it from the tree
4421     AdjustIndex:=NewHistItem.HistoryIndex;
4422     FItems.Delete(OldAVLNode);
4423   end else begin
4424     // create a new history item
4425     NewHistItem:=TIdentHistListItem.Create;
4426     NewHistItem.Identifier:=NewItem.Identifier;
4427     NewHistItem.NodeDesc:=NewItem.GetDesc;
4428     NewHistItem.ParamList:=NewItem.ParamTypeList;
4429     AdjustIndex:=0;
4430   end;
4431   NewHistItem.HistoryIndex:=0;
4432   // adjust all other HistoryIndex
4433   AnAVLNode:=Fitems.FindLowest;
4434   while AnAVLNode<>nil do begin
4435     AnHistItem:=TIdentHistListItem(AnAVLNode.Data);
4436     if AnHistItem.HistoryIndex>=AdjustIndex then
4437       inc(AnHistItem.HistoryIndex);
4438     AnAVLNode:=FItems.FindSuccessor(AnAVLNode);
4439   end;
4440   if (FItems.Count>0) and (FItems.Count>=FCapacity) then
4441     FItems.FreeAndDelete(FItems.FindHighest);
4442   FItems.Add(NewHistItem);
4443   {$IFDEF ShowHistory}
4444   DebugLn('TIdentifierHistoryList.Added Count=',Count);
4445   {$ENDIF}
4446 end;
4447 
TIdentifierHistoryList.GetHistoryIndexnull4448 function TIdentifierHistoryList.GetHistoryIndex(AnItem: TIdentifierListItem
4449   ): integer;
4450 var
4451   AnAVLNode: TAVLTreeNode;
4452 begin
4453   AnAVLNode:=FindItem(AnItem);
4454   if AnAVLNode=nil then
4455     Result:=33333333  // a very high value
4456   else
4457     Result:=TIdentHistListItem(AnAVLNode.Data).HistoryIndex;
4458 end;
4459 
TIdentifierHistoryList.Countnull4460 function TIdentifierHistoryList.Count: integer;
4461 begin
4462   Result:=FItems.Count;
4463 end;
4464 
CalcMemSizenull4465 function TIdentifierHistoryList.CalcMemSize: PtrUInt;
4466 var
4467   Node: TAVLTreeNode;
4468   Item: TIdentHistListItem;
4469 begin
4470   Result:=PtrUInt(InstanceSize);
4471   if FItems<>nil then begin
4472     {%H-}inc(Result,FItems.Count*SizeOf(TAVLTreeNode));
4473     Node:=FItems.FindLowest;
4474     while Node<>nil do begin
4475       Item:=TIdentHistListItem(Node.Data);
4476       inc(Result,Item.CalcMemSize);
4477       Node:=FItems.FindSuccessor(Node);
4478     end;
4479   end;
4480 end;
4481 
4482 { TCodeContextInfo }
4483 
TCodeContextInfo.GetItemsnull4484 function TCodeContextInfo.GetItems(Index: integer): TCodeContextInfoItem;
4485 begin
4486   Result:=TCodeContextInfoItem(FItems[Index]);
4487 end;
4488 
4489 constructor TCodeContextInfo.Create;
4490 begin
4491   FItems:=TFPList.Create;
4492 end;
4493 
4494 destructor TCodeContextInfo.Destroy;
4495 begin
4496   Clear;
4497   FreeAndNil(FItems);
4498   inherited Destroy;
4499 end;
4500 
TCodeContextInfo.Countnull4501 function TCodeContextInfo.Count: integer;
4502 begin
4503   Result:=FItems.Count;
4504 end;
4505 
Addnull4506 function TCodeContextInfo.Add(const Context: TExpressionType): integer;
4507 var
4508   Item: TCodeContextInfoItem;
4509 begin
4510   Item:=TCodeContextInfoItem.Create;
4511   Item.Expr:=Context;
4512   Result:=FItems.Add(Item);
4513 end;
4514 
AddCompilerProcnull4515 function TCodeContextInfo.AddCompilerProc: integer;
4516 var
4517   Item: TCodeContextInfoItem;
4518 begin
4519   Item:=TCodeContextInfoItem.Create;
4520   Result:=FItems.Add(Item);
4521 end;
4522 
4523 procedure TCodeContextInfo.Clear;
4524 var
4525   i: Integer;
4526 begin
4527   for i:=0 to FItems.Count-1 do
4528     TObject(FItems[i]).Free;
4529   FItems.Clear;
4530 end;
4531 
CalcMemSizenull4532 function TCodeContextInfo.CalcMemSize: PtrUInt;
4533 begin
4534   Result:=PtrUInt(InstanceSize)
4535     +{%H-}PtrUInt(TCodeContextInfoItem)*SizeOf(FItems.Count)
4536     +MemSizeString(FProcName);
4537 end;
4538 
4539 { TIdentifierListSearchItem }
4540 
TIdentifierListSearchItem.CalcMemSizenull4541 function TIdentifierListSearchItem.CalcMemSize: PtrUInt;
4542 begin
4543   Result:=PtrUInt(InstanceSize)
4544     +MemSizeString(ParamList);
4545 end;
4546 
4547 { TIdentHistListItem }
4548 
CalcMemSizenull4549 function TIdentHistListItem.CalcMemSize: PtrUInt;
4550 begin
4551   Result:=PtrUInt(InstanceSize)
4552     +MemSizeString(Identifier)
4553     +MemSizeString(ParamList);
4554 end;
4555 
4556 { TCodeContextInfoItem }
4557 
4558 destructor TCodeContextInfoItem.Destroy;
4559 begin
4560   FreeAndNil(Params);
4561   inherited Destroy;
4562 end;
4563 
AsDebugStringnull4564 function TCodeContextInfoItem.AsDebugString(WithExpr: boolean): string;
4565 var
4566   i: Integer;
4567 begin
4568   Result:=ProcName+'(';
4569   if Params<>nil then
4570     for i:=0 to Params.Count-1 do begin
4571       if i>0 then Result+=',';
4572       Result+=Params[i];
4573     end;
4574   Result+=')';
4575   if ResultType<>'' then Result+=':'+ResultType;
4576   if WithExpr then
4577     Result+=' '+ExprTypeToString(Expr);
4578 end;
4579 
4580 end.
4581 
4582