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