mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-27 01:03:58 +02:00

- If an existing item moves to the front (to HistoryIndex=0) then all Indexes between 0 and the old value need updating (the old value became unused, so items above do not need an update). - If an new item is inserted, all existing items need to be increased.
4718 lines
164 KiB
ObjectPascal
4718 lines
164 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
TIdentCompletionTool enhances the TFindDeclarationTool with the ability
|
|
to create lists of valid identifiers at a specific code position.
|
|
}
|
|
unit IdentCompletionTool;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
{$I codetools.inc}
|
|
|
|
// activate for debug:
|
|
|
|
// mem check
|
|
{ $DEFINE MEM_CHECK}
|
|
|
|
// verbosity
|
|
{ $DEFINE CTDEBUG}
|
|
{ $DEFINE ShowFoundIdents}
|
|
{ $DEFINE ShowFilteredIdents}
|
|
{ $DEFINE ShowHistory}
|
|
{ $DEFINE VerboseCodeContext}
|
|
{ $DEFINE VerboseICGatherUnitNames}
|
|
{ $DEFINE VerboseICGatherKeywords}
|
|
|
|
uses
|
|
{$IFDEF MEM_CHECK}
|
|
MemCheck,
|
|
{$ENDIF}
|
|
Classes, SysUtils, typinfo, crc, Laz_AVL_Tree,
|
|
// LazUtils
|
|
LazFileUtils, LazDbgLog, AvgLvlTree,
|
|
// Codetools
|
|
FileProcs, CodeTree, CodeAtom, CodeCache, CustomCodeTool, CodeToolsStrConsts,
|
|
KeywordFuncLists, BasicCodeTools, LinkScanner, SourceChanger,
|
|
FindDeclarationTool, PascalReaderTool, PascalParserTool, ExprEval;
|
|
|
|
type
|
|
TIdentCompletionTool = class;
|
|
TIdentifierHistoryList = class;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// gathered identifier list
|
|
|
|
TIdentifierCompatibility = (
|
|
icompExact,
|
|
icompCompatible,
|
|
icompUnknown,
|
|
icompIncompatible
|
|
);
|
|
TIdentifierCompatibilities = set of TIdentifierCompatibility;
|
|
|
|
TIdentListItemFlag = (
|
|
iliHasChilds,
|
|
iliBaseExprTypeValid,
|
|
iliIsFunction,
|
|
iliIsFunctionValid,
|
|
iliIsAbstractMethod,
|
|
iliIsAbstractMethodValid,
|
|
iliParamTypeListValid,
|
|
iliParamNameListValid,
|
|
iliNodeValid,
|
|
iliNodeHashValid,
|
|
iliNodeGoneWarned,
|
|
iliIsConstructor,
|
|
iliIsConstructorValid,
|
|
iliIsDestructor,
|
|
iliIsDestructorValid,
|
|
iliKeyword,
|
|
iliResultTypeValid,
|
|
iliHasIndexValid,
|
|
iliHasIndex,
|
|
iliHasParamListValid,
|
|
iliHasParamList,
|
|
iliIsReadOnlyValid,
|
|
iliIsReadOnly,
|
|
iliHintModifiersValid,
|
|
iliIsDeprecated,
|
|
iliIsPlatform,
|
|
iliIsExperimental,
|
|
iliIsUnimplemented,
|
|
iliIsLibrary,
|
|
iliAtCursor, // the item is the identifier at the completion
|
|
iliNeedsAmpersand, //the item has to be prefixed with '&'
|
|
iliHasLowerVisibility
|
|
);
|
|
TIdentListItemFlags = set of TIdentListItemFlag;
|
|
|
|
{ TIdentifierListSearchItem }
|
|
|
|
TIdentifierListSearchItem = class
|
|
public
|
|
Identifier: PChar;
|
|
ParamList: string;
|
|
function CalcMemSize: PtrUInt;
|
|
end;
|
|
|
|
TIdentifierList = class;
|
|
|
|
{ TIdentifierListItem }
|
|
|
|
TIdentifierListItem = class
|
|
private
|
|
FParamTypeList: string;
|
|
FParamNameList: string;
|
|
FNode: TCodeTreeNode;
|
|
FResultType: string;
|
|
FToolNodesDeletedStep: integer;// only valid if iliNodeValid
|
|
FNodeStartPos: integer;
|
|
FNodeDesc: TCodeTreeNodeDesc;
|
|
FNodeHash: Cardinal;
|
|
function GetNode: TCodeTreeNode;
|
|
function GetParamTypeList: string;
|
|
function GetParamNameList: string;
|
|
procedure SetNode(const AValue: TCodeTreeNode);
|
|
procedure SetParamTypeList(const AValue: string);
|
|
procedure SetParamNameList(const AValue: string);
|
|
procedure SetResultType(const AValue: string);
|
|
public
|
|
Compatibility: TIdentifierCompatibility;
|
|
HistoryIndex: integer;
|
|
Identifier: string;
|
|
Level: integer;
|
|
Tool: TFindDeclarationTool;
|
|
DefaultDesc: TCodeTreeNodeDesc;
|
|
Flags: TIdentListItemFlags;
|
|
BaseExprType: TExpressionType;
|
|
function AsString: string;
|
|
procedure BeautifyIdentifier({%H-}IdentList: TIdentifierList); virtual;
|
|
function GetDesc: TCodeTreeNodeDesc;
|
|
constructor Create(NewCompatibility: TIdentifierCompatibility;
|
|
NewHasChilds: boolean; NewHistoryIndex: integer;
|
|
NewIdentifier: PChar; NewLevel: integer;
|
|
NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool;
|
|
NewDefaultDesc: TCodeTreeNodeDesc);
|
|
function IsProcNodeWithParams: boolean;
|
|
function IsPropertyWithParams: boolean;
|
|
function IsPropertyReadOnly: boolean;
|
|
function GetHintModifiers: TPascalHintModifiers;
|
|
function CheckHasChilds: boolean;
|
|
function CanBeAssigned: boolean;
|
|
procedure UpdateBaseContext;
|
|
function HasChilds: boolean;
|
|
function HasIndex: boolean;
|
|
function IsFunction: boolean;
|
|
function IsConstructor: boolean;
|
|
function IsDestructor: boolean;
|
|
function IsAbstractMethod: boolean;
|
|
function TryIsAbstractMethod: boolean;
|
|
procedure Clear;
|
|
procedure UnbindNode;
|
|
procedure StoreNodeHash;
|
|
function RestoreNode: boolean;
|
|
function GetNodeHash(ANode: TCodeTreeNode): Cardinal;
|
|
function CompareParamList(CompareItem: TIdentifierListItem): integer;
|
|
function CompareParamList(CompareItem: TIdentifierListSearchItem): integer;
|
|
function CalcMemSize: PtrUInt; virtual;
|
|
public
|
|
property ParamTypeList: string read GetParamTypeList write SetParamTypeList;
|
|
property ParamNameList: string read GetParamNameList write SetParamNameList;
|
|
property ResultType: string read FResultType write SetResultType;
|
|
property Node: TCodeTreeNode read GetNode write SetNode;
|
|
end;
|
|
TIdentifierListItemClass = class of TIdentifierListItem;
|
|
|
|
TUnitNameSpaceIdentifierListItem = class(TIdentifierListItem)
|
|
public
|
|
FileUnitName: string;
|
|
IdentifierStartInUnitName: Integer;
|
|
|
|
constructor Create(NewCompatibility: TIdentifierCompatibility;
|
|
NewHasChilds: boolean; NewHistoryIndex: integer;
|
|
NewIdentifier: PChar; NewLevel: integer;
|
|
NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool;
|
|
NewDefaultDesc: TCodeTreeNodeDesc;
|
|
NewFileUnitName: PChar;
|
|
NewIdentifierStartInUnitName: Integer);
|
|
function CalcMemSize: PtrUInt; override;
|
|
end;
|
|
TUnitNameSpaceIdentifierListItemClass = class of TUnitNameSpaceIdentifierListItem;
|
|
|
|
TIdentifierListFlag = (
|
|
ilfFilteredListNeedsUpdate,
|
|
ilfUsedToolsNeedsUpdate
|
|
);
|
|
TIdentifierListFlags = set of TIdentifierListFlag;
|
|
|
|
TIdentifierListContextFlag = (
|
|
ilcfStartInStatement, // context starts in statements. e.g. between begin..end
|
|
ilcfStartOfStatement, // atom is start of statement. e.g. 'A|:=' or 'A|;', does not check if A can be assigned
|
|
ilcfStartOfOperand, // atom is start of an operand. e.g. 'A|.B'
|
|
ilcfStartIsSubIdent, // atom in front is point
|
|
ilcfNeedsEndSemicolon, // after context a semicolon is needed. e.g. 'A| end'
|
|
ilcfNoEndSemicolon, // no semicolon after. E.g. 'A| else'
|
|
ilcfNeedsEndComma, // after context a comma is needed. e.g. 'uses sysutil| classes'
|
|
ilcfNeedsDo, // after context a 'do' is needed. e.g. 'with Form1| do'
|
|
ilcfIsExpression, // is expression part of statement. e.g. 'if expr'
|
|
ilcfCanProcDeclaration,// context allows one to declare a procedure/method
|
|
ilcfEndOfLine, // atom at end of line
|
|
ilcfDontAllowProcedures// context doesn't allow procedures (e.g. in function parameter, after other operator, in if codition etc. - Delphi mode supports assignment of procedures!)
|
|
);
|
|
TIdentifierListContextFlags = set of TIdentifierListContextFlag;
|
|
|
|
TIdentComplSortMethod = (icsScopedAlphabetic, icsAlphabetic, icsScopedDeclaration);
|
|
|
|
TOnGatherUserIdentifiersToFilteredList = procedure(Sender: TIdentifierList;
|
|
FilteredList: TFPList; PriorityCount: Integer) of object;
|
|
|
|
TIdentifierList = class
|
|
private
|
|
FContext: TFindContext;
|
|
FIdentComplIncludeKeywords: Boolean;
|
|
FNewMemberVisibility: TCodeTreeNodeDesc;
|
|
FContextFlags: TIdentifierListContextFlags;
|
|
FOnGatherUserIdentifiersToFilteredList: TOnGatherUserIdentifiersToFilteredList;
|
|
FSortForHistory: boolean;
|
|
FSortMethodForCompletion: TIdentComplSortMethod;
|
|
FStartAtom: TAtomPosition;
|
|
FStartAtomBehind: TAtomPosition;
|
|
FStartAtomInFront: TAtomPosition;
|
|
FStartBracketLvl: integer;
|
|
FStartContextPos: TCodeXYPosition;
|
|
FCreatedIdentifiers: TFPList; // list of PChar
|
|
FFilteredList: TFPList; // list of TIdentifierListItem
|
|
FFlags: TIdentifierListFlags;
|
|
FHistory: TIdentifierHistoryList;
|
|
FItems: TAvlTree; // tree of TIdentifierListItem (completely sorted)
|
|
FIdentView: TAVLTree; // tree of TIdentifierListItem sorted for identifiers
|
|
FUsedTools: TAVLTree; // tree of TFindDeclarationTool
|
|
FIdentSearchItem: TIdentifierListSearchItem;
|
|
FPrefix: string;
|
|
FStartContext: TFindContext;
|
|
FContainsFilter: Boolean;
|
|
function CompareIdentListItems({%H-}Tree: TAvlTree; Data1, Data2: Pointer): integer;
|
|
procedure SetHistory(const AValue: TIdentifierHistoryList);
|
|
procedure SetSortForHistory(AValue: boolean);
|
|
procedure SetSortMethodForCompletion(AValue: TIdentComplSortMethod);
|
|
procedure UpdateFilteredList;
|
|
function GetFilteredItems(Index: integer): TIdentifierListItem;
|
|
procedure SetPrefix(const AValue: string);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure Add(NewItem: TIdentifierListItem);
|
|
function Count: integer;
|
|
function GetFilteredCount: integer;
|
|
function HasIdentifier(Identifier: PChar; const ParamList: string): boolean;
|
|
function FindIdentifier(Identifier: PChar; const ParamList: string): TIdentifierListItem;
|
|
function FindIdentifier(Identifier: PChar; PreferProc: boolean): TIdentifierListItem;
|
|
function FindIdentifier(Identifier: PChar): TIdentifierListItem;
|
|
function FindCreatedIdentifier(const Ident: string): integer;
|
|
function CreateIdentifier(const Ident: string): PChar;
|
|
function StartUpAtomInFrontIs(const s: string): boolean;
|
|
function StartUpAtomBehindIs(const s: string): boolean;
|
|
function CompletePrefix(const OldPrefix: string): string;
|
|
function CalcMemSize: PtrUInt;
|
|
public
|
|
property Context: TFindContext read FContext write FContext;
|
|
property ContextFlags: TIdentifierListContextFlags
|
|
read FContextFlags write FContextFlags;
|
|
property NewMemberVisibility: TCodeTreeNodeDesc // identifier is a class member, e.g. a variable or a procedure name
|
|
read FNewMemberVisibility write FNewMemberVisibility;
|
|
property FilteredItems[Index: integer]: TIdentifierListItem read GetFilteredItems;
|
|
property History: TIdentifierHistoryList read FHistory write SetHistory;
|
|
property Prefix: string read FPrefix write SetPrefix;
|
|
property SortForHistory: boolean read FSortForHistory write SetSortForHistory;
|
|
property SortMethodForCompletion: TIdentComplSortMethod read FSortMethodForCompletion
|
|
write SetSortMethodForCompletion;
|
|
|
|
property StartAtom: TAtomPosition read FStartAtom write FStartAtom;
|
|
property StartAtomInFront: TAtomPosition
|
|
read FStartAtomInFront write FStartAtomInFront; // in front of variable, not only of identifier
|
|
property StartAtomBehind: TAtomPosition
|
|
read FStartAtomBehind write FStartAtomBehind; // directly behind
|
|
property StartBracketLvl: integer read FStartBracketLvl write FStartBracketLvl;
|
|
property StartContext: TFindContext read FStartContext write FStartContext;
|
|
property StartContextPos: TCodeXYPosition
|
|
read FStartContextPos write FStartContextPos;
|
|
property ContainsFilter: Boolean read FContainsFilter write FContainsFilter;
|
|
property IdentComplIncludeKeywords: Boolean read FIdentComplIncludeKeywords write FIdentComplIncludeKeywords;
|
|
property OnGatherUserIdentifiersToFilteredList: TOnGatherUserIdentifiersToFilteredList
|
|
read FOnGatherUserIdentifiersToFilteredList write FOnGatherUserIdentifiersToFilteredList;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// history list
|
|
|
|
{ TIdentHistListItem }
|
|
|
|
TIdentHistListItem = class
|
|
public
|
|
Identifier: string;
|
|
NodeDesc: TCodeTreeNodeDesc;
|
|
ParamList: string;
|
|
HistoryIndex: integer;
|
|
function CalcMemSize: PtrUInt;
|
|
end;
|
|
|
|
{ TIdentifierHistoryList }
|
|
|
|
TIdentifierHistoryList = class
|
|
private
|
|
FCapacity: integer;
|
|
FItems: TAVLTree; // tree of TIdentHistListItem
|
|
procedure SetCapacity(const AValue: integer);
|
|
function FindItem(NewItem: TIdentifierListItem): TAVLTreeNode;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure Add(NewItem: TIdentifierListItem);
|
|
function GetHistoryIndex(AnItem: TIdentifierListItem): integer;
|
|
function Count: integer;
|
|
function CalcMemSize: PtrUInt;
|
|
public
|
|
property Capacity: integer read FCapacity write SetCapacity;
|
|
end;
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
{ TCodeContextInfoItem }
|
|
|
|
TCodeContextInfoItem = class
|
|
public
|
|
Expr: TExpressionType;
|
|
// compiler predefined proc
|
|
ProcName: string;
|
|
Params: TStringList;
|
|
ResultType: string;
|
|
destructor Destroy; override;
|
|
function AsDebugString(WithExpr: boolean): string;
|
|
end;
|
|
|
|
{ TCodeContextInfo }
|
|
|
|
TCodeContextInfo = class
|
|
private
|
|
FEndPos: integer;
|
|
FItems: TFPList; // list of TCodeContextInfoItem
|
|
FParameterIndex: integer;
|
|
FProcName: string;
|
|
FProcNameAtom: TAtomPosition;
|
|
FStartPos: integer;
|
|
FTool: TFindDeclarationTool;
|
|
function GetItems(Index: integer): TCodeContextInfoItem;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function Count: integer;
|
|
property Items[Index: integer]: TCodeContextInfoItem read GetItems; default;
|
|
function Add(const Context: TExpressionType): integer;
|
|
function AddCompilerProc: integer;
|
|
procedure Clear;
|
|
property Tool: TFindDeclarationTool read FTool write FTool;
|
|
property ParameterIndex: integer read FParameterIndex write FParameterIndex;// 1 based
|
|
property ProcName: string read FProcName write FProcName;
|
|
property ProcNameAtom: TAtomPosition read FProcNameAtom write FProcNameAtom;
|
|
property StartPos: integer read FStartPos write FStartPos;// context is valid from StartPos to EndPos
|
|
property EndPos: integer read FEndPos write FEndPos;
|
|
|
|
function CalcMemSize: PtrUInt;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// TIdentCompletionTool
|
|
|
|
TOnGatherUserIdentifiers = procedure(Sender: TIdentCompletionTool;
|
|
const ContextFlags: TIdentifierListContextFlags) of object;
|
|
|
|
TIdentCompletionTool = class(TFindDeclarationTool)
|
|
private
|
|
FBeautifier: TBeautifyCodeOptions;
|
|
FLastGatheredIdentParent: TCodeTreeNode;
|
|
FLastGatheredIdentLevel: integer;
|
|
FICTClassAndAncestorsAndExtClassOfHelper: TFPList;// list of PCodeXYPosition
|
|
FIDCTFoundPublicProperties: TAVLTree;// tree of PChar (pointing to the
|
|
// property names in source)
|
|
FIDTFoundMethods: TAVLTree;// tree of TCodeTreeNodeExtension Txt=clean text
|
|
FIDTTreeOfUnitFiles: TAVLTree;// tree of TUnitFileInfo
|
|
FIDTTreeOfUnitFiles_NamespacePath: string;
|
|
FIDTTreeOfUnitFiles_CaseInsensitive: Boolean;
|
|
FIDTTreeOfNamespaces: TAVLTree;// tree of TNameSpaceInfo
|
|
FOnGatherUserIdentifiers: TOnGatherUserIdentifiers;
|
|
procedure AddToTreeOfUnitFileInfo(const AFilename: string);
|
|
procedure AddBaseConstant(const BaseName: PChar);
|
|
procedure AddBaseType(const BaseName: PChar);
|
|
procedure AddCompilerFunction(const AProcName, AParameterList,
|
|
AResultType: PChar);
|
|
procedure AddCompilerProcedure(const AProcName, AParameterList: PChar);
|
|
procedure AddKeyWord(aKeyWord: string);
|
|
protected
|
|
CurrentIdentifierList: TIdentifierList;
|
|
CurrentIdentifierContexts: TCodeContextInfo;
|
|
function CollectAllIdentifiers(Params: TFindDeclarationParams;
|
|
const FoundContext: TFindContext): TIdentifierFoundResult;
|
|
procedure GatherPredefinedIdentifiers(CleanPos: integer;
|
|
const Context, GatherContext: TFindContext);
|
|
procedure GatherUsefulIdentifiers(CleanPos: integer;
|
|
const Context, GatherContext: TFindContext);
|
|
procedure GatherUnitnames(const NameSpacePath: string = '');
|
|
procedure GatherSourceNames(const Context: TFindContext);
|
|
procedure GatherContextKeywords(const Context: TFindContext;
|
|
CleanPos: integer; BeautifyCodeOptions: TBeautifyCodeOptions; const GatherContext: TFindContext);
|
|
procedure GatherUserIdentifiers(const ContextFlags: TIdentifierListContextFlags);
|
|
procedure InitCollectIdentifiers(const CursorPos: TCodeXYPosition;
|
|
var IdentifierList: TIdentifierList);
|
|
function ParseSourceTillCollectionStart(const CursorPos: TCodeXYPosition;
|
|
out CleanCursorPos: integer; out CursorNode: TCodeTreeNode;
|
|
out IdentStartPos, IdentEndPos: integer): boolean;
|
|
function FindIdentifierStartPos(const CursorPos: TCodeXYPosition
|
|
): TCodeXYPosition;
|
|
procedure FindCollectionContext(Params: TFindDeclarationParams;
|
|
IdentStartPos: integer; CursorNode: TCodeTreeNode;
|
|
out ExprType: TExpressionType; out ContextExprStartPos: LongInt;
|
|
out StartInSubContext, HasInheritedKeyword: Boolean);
|
|
function CollectAllContexts(Params: TFindDeclarationParams;
|
|
const FoundContext: TFindContext): TIdentifierFoundResult;
|
|
function CollectAttributeConstructors({%H-}Params: TFindDeclarationParams;
|
|
const FoundContext: TFindContext): TIdentifierFoundResult;
|
|
procedure AddCollectionContext(Tool: TFindDeclarationTool;
|
|
Node: TCodeTreeNode);
|
|
function CheckCursorInCompilerDirective(CursorPos: TCodeXYPosition): boolean;
|
|
procedure AddCompilerDirectiveMacros(Directive: string);
|
|
public
|
|
function GatherAvailableUnitNames(const CursorPos: TCodeXYPosition;
|
|
var IdentifierList: TIdentifierList): Boolean;
|
|
function GatherIdentifiers(const CursorPos: TCodeXYPosition;
|
|
var IdentifierList: TIdentifierList): boolean;
|
|
function FindCodeContext(const CursorPos: TCodeXYPosition;
|
|
out CodeContexts: TCodeContextInfo): boolean;
|
|
function FindAbstractMethods(const CursorPos: TCodeXYPosition;
|
|
out ListOfPCodeXYPosition: TFPList;
|
|
SkipAbstractsInStartClass: boolean = false): boolean;
|
|
function GetValuesOfCaseVariable(const CursorPos: TCodeXYPosition;
|
|
List: TStrings; WithTypeDefIfScoped: boolean = true): boolean;
|
|
property Beautifier: TBeautifyCodeOptions read FBeautifier write FBeautifier;
|
|
|
|
procedure CalcMemSize(Stats: TCTMemStats); override;
|
|
|
|
property OnGatherUserIdentifiers: TOnGatherUserIdentifiers read FOnGatherUserIdentifiers write FOnGatherUserIdentifiers;
|
|
end;
|
|
|
|
function dbgs(Flag: TIdentifierListContextFlag): string; overload;
|
|
function dbgs(Flags: TIdentifierListContextFlags): string; overload;
|
|
|
|
var
|
|
CIdentifierListItem: TIdentifierListItemClass = TIdentifierListItem;
|
|
CUnitNameSpaceIdentifierListItem: TUnitNameSpaceIdentifierListItemClass = TUnitNameSpaceIdentifierListItem;
|
|
|
|
implementation
|
|
|
|
const
|
|
CompilerFuncHistoryIndex = 10;
|
|
CompilerFuncLevel = 10;
|
|
|
|
function CompareIdentListItemsForIdents(Data1, Data2: Pointer): integer;
|
|
var
|
|
Item1: TIdentifierListItem absolute Data1;
|
|
Item2: TIdentifierListItem absolute Data2;
|
|
begin
|
|
// sort alpabetically (lower is better)
|
|
Result:=CompareIdentifierPtrs(Pointer(Item2.Identifier),Pointer(Item1.Identifier));
|
|
if Result<>0 then exit;
|
|
|
|
// then sort for ParamList (lower is better)
|
|
Result:=Item2.CompareParamList(Item1);
|
|
end;
|
|
|
|
function CompareIdentListSearchWithItems(SearchItem, Item: Pointer): integer;
|
|
var
|
|
TheSearchItem: TIdentifierListSearchItem absolute SearchItem;
|
|
TheItem: TIdentifierListItem absolute Item;
|
|
begin
|
|
// sort alpabetically (lower is better)
|
|
Result:=CompareIdentifierPtrs(Pointer(TheItem.Identifier),TheSearchItem.Identifier);
|
|
if Result<>0 then exit;
|
|
|
|
// then sort for ParamList (lower is better)
|
|
Result:=TheItem.CompareParamList(TheSearchItem);
|
|
end;
|
|
|
|
function CompareIdentListSearchWithItemsWithoutParams(SearchItem, Item: Pointer): integer;
|
|
var
|
|
TheSearchItem: TIdentifierListSearchItem absolute SearchItem;
|
|
TheItem: TIdentifierListItem absolute Item;
|
|
begin
|
|
// sort alpabetically (lower is better)
|
|
Result:=CompareIdentifierPtrs(Pointer(TheItem.Identifier),TheSearchItem.Identifier);
|
|
end;
|
|
|
|
function CompareIdentHistListItem(Data1, Data2: Pointer): integer;
|
|
var
|
|
Item1: TIdentHistListItem absolute Data1;
|
|
Item2: TIdentHistListItem absolute Data2;
|
|
begin
|
|
Result:=CompareIdentifiers(PChar(Pointer(Item2.Identifier)),
|
|
PChar(Pointer(Item1.Identifier)));
|
|
if Result<>0 then exit;
|
|
|
|
//debugln('CompareIdentHistListItem ',Item2.Identifier,'=',Item1.Identifier);
|
|
Result:=CompareIdentifiers(PChar(Pointer(Item2.ParamList)),
|
|
PChar(Pointer(Item1.ParamList)));
|
|
end;
|
|
|
|
function CompareIdentItemWithHistListItem(Data1, Data2: Pointer): integer;
|
|
var
|
|
IdentItem: TIdentifierListItem absolute Data1;
|
|
HistItem: TIdentHistListItem absolute Data2;
|
|
begin
|
|
Result:=CompareIdentifierPtrs(Pointer(HistItem.Identifier),
|
|
Pointer(IdentItem.Identifier));
|
|
if Result<>0 then exit;
|
|
|
|
//debugln('CompareIdentItemWithHistListItem ',HistItem.Identifier,'=',GetIdentifier(IdentItem.Identifier));
|
|
Result:=SysUtils.CompareText(HistItem.ParamList,IdentItem.ParamTypeList);
|
|
end;
|
|
|
|
function dbgs(Flag: TIdentifierListContextFlag): string;
|
|
begin
|
|
Result:=GetEnumName(typeinfo(Flag),ord(Flag));
|
|
end;
|
|
|
|
function dbgs(Flags: TIdentifierListContextFlags): string;
|
|
var
|
|
f: TIdentifierListContextFlag;
|
|
begin
|
|
Result:='';
|
|
for f:=Low(TIdentifierListContextFlag) to High(TIdentifierListContextFlag) do
|
|
if f in Flags then begin
|
|
if Result<>'' then Result+=',';
|
|
Result+=dbgs(f);
|
|
end;
|
|
Result:='['+Result+']';
|
|
end;
|
|
|
|
{ TUnitNameSpaceIdentifierListItem }
|
|
|
|
constructor TUnitNameSpaceIdentifierListItem.Create(
|
|
NewCompatibility: TIdentifierCompatibility; NewHasChilds: boolean;
|
|
NewHistoryIndex: integer; NewIdentifier: PChar; NewLevel: integer;
|
|
NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool;
|
|
NewDefaultDesc: TCodeTreeNodeDesc; NewFileUnitName: PChar;
|
|
NewIdentifierStartInUnitName: Integer);
|
|
begin
|
|
inherited Create(NewCompatibility, NewHasChilds, NewHistoryIndex,
|
|
NewIdentifier, NewLevel, NewNode, NewTool, NewDefaultDesc);
|
|
FileUnitName := NewFileUnitName;
|
|
IdentifierStartInUnitName := NewIdentifierStartInUnitName;
|
|
end;
|
|
|
|
function TUnitNameSpaceIdentifierListItem.CalcMemSize: PtrUInt;
|
|
begin
|
|
Result := inherited CalcMemSize
|
|
+MemSizeString(FileUnitName);
|
|
end;
|
|
|
|
{ TIdentifierList }
|
|
|
|
function TIdentifierList.CompareIdentListItems(Tree: TAvlTree; Data1, Data2: Pointer): integer;
|
|
var
|
|
Item1: TIdentifierListItem absolute Data1;
|
|
Item2: TIdentifierListItem absolute Data2;
|
|
begin
|
|
|
|
if SortMethodForCompletion in [icsScopedAlphabetic, icsScopedDeclaration] then begin
|
|
// first sort for Compatibility (lower is better)
|
|
if ord(Item1.Compatibility)<ord(Item2.Compatibility) then begin
|
|
Result:=-1;
|
|
exit;
|
|
end else if ord(Item1.Compatibility)>ord(Item2.Compatibility) then begin
|
|
Result:=1;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if SortForHistory then begin
|
|
// then sort for History (lower is better)
|
|
if Item1.HistoryIndex<Item2.HistoryIndex then begin
|
|
Result:=-1;
|
|
exit;
|
|
end else if Item1.HistoryIndex>Item2.HistoryIndex then begin
|
|
Result:=1;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if SortMethodForCompletion in [icsScopedAlphabetic, icsScopedDeclaration] then begin
|
|
// then sort for Level (i.e. scope, lower is better)
|
|
if Item1.Level<Item2.Level then begin
|
|
Result:=-1;
|
|
exit;
|
|
end else if Item1.Level>Item2.Level then begin
|
|
Result:=1;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if SortMethodForCompletion = icsScopedDeclaration then begin
|
|
if (Item1.Node<>nil) and (Item2.Node<>nil) then
|
|
begin
|
|
if Item1.Node.StartPos<Item2.Node.StartPos then
|
|
begin
|
|
Result:=-1;
|
|
exit;
|
|
end else
|
|
if Item1.Node.StartPos>Item2.Node.StartPos then
|
|
begin
|
|
Result:=1;
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
if (Item1.Node<>nil) xor (Item2.Node<>nil) then begin // One node without source pos
|
|
if (Item1.Node<>nil) then
|
|
Result := 1
|
|
else
|
|
Result := -1;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
Result:=CompareIdentifierPtrs(Pointer(Item2.Identifier),Pointer(Item1.Identifier));
|
|
if Result<>0 then exit;
|
|
|
|
// then sort for ParamList (lower is better)
|
|
Result:=Item2.CompareParamList(Item1);
|
|
end;
|
|
|
|
procedure TIdentifierList.SetSortMethodForCompletion(
|
|
AValue: TIdentComplSortMethod);
|
|
begin
|
|
if FSortMethodForCompletion = AValue then Exit;
|
|
FSortMethodForCompletion := AValue;
|
|
Clear;
|
|
end;
|
|
|
|
procedure TIdentifierList.SetPrefix(const AValue: string);
|
|
begin
|
|
if FPrefix=AValue then exit;
|
|
FPrefix:=AValue;
|
|
Include(FFlags,ilfFilteredListNeedsUpdate);
|
|
end;
|
|
|
|
procedure TIdentifierList.UpdateFilteredList;
|
|
var
|
|
AnAVLNode: TAvlTreeNode;
|
|
CurItem: TIdentifierListItem;
|
|
cPriorityCount: Integer;
|
|
i: PtrInt;
|
|
begin
|
|
if not (ilfFilteredListNeedsUpdate in FFlags) then exit;
|
|
if FFilteredList=nil then FFilteredList:=TFPList.Create;
|
|
FFilteredList.Count:=0;
|
|
FFilteredList.Capacity:=FItems.Count;
|
|
{$IFDEF CTDEBUG}
|
|
DebugLn(['TIdentifierList.UpdateFilteredList Prefix="',Prefix,'"']);
|
|
{$ENDIF}
|
|
AnAVLNode:=FItems.FindLowest;
|
|
cPriorityCount := 0;
|
|
while AnAVLNode<>nil do begin
|
|
CurItem:=TIdentifierListItem(AnAVLNode.Data);
|
|
if CurItem.Identifier<>'' then
|
|
begin
|
|
if FContainsFilter then
|
|
i:=IdentifierPos(PChar(Pointer(Prefix)),PChar(Pointer(CurItem.Identifier)))
|
|
else if ComparePrefixIdent(PChar(Pointer(Prefix)),PChar(Pointer(CurItem.Identifier))) then
|
|
i:=0
|
|
else
|
|
i:=-1;
|
|
if i=0 then begin
|
|
{$IFDEF ShowFilteredIdents}
|
|
DebugLn(['::: FILTERED ITEM ',FFilteredList.Count,' ',CurItem.Identifier]);
|
|
{$ENDIF}
|
|
if (length(Prefix)=length(CurItem.Identifier))
|
|
and (not (iliAtCursor in CurItem.Flags)) then
|
|
// put exact matches at the beginning
|
|
FFilteredList.Insert(0,CurItem)
|
|
else
|
|
FFilteredList.Insert(cPriorityCount, CurItem);
|
|
Inc(cPriorityCount);
|
|
end
|
|
else if i>0 then begin
|
|
{$IFDEF ShowFilteredIdents}
|
|
DebugLn(['::: FILTERED ITEM ',FFilteredList.Count,' ',CurItem.Identifier]);
|
|
{$ENDIF}
|
|
FFilteredList.Add(CurItem);
|
|
end;
|
|
end;
|
|
AnAVLNode:=FItems.FindSuccessor(AnAVLNode);
|
|
end;
|
|
if Assigned(FOnGatherUserIdentifiersToFilteredList) then
|
|
FOnGatherUserIdentifiersToFilteredList(Self, FFilteredList, cPriorityCount);
|
|
{$IFDEF CTDEBUG}
|
|
DebugLn(['TIdentifierList.UpdateFilteredList ',dbgs(FFilteredList.Count),' of ',dbgs(FItems.Count)]);
|
|
{$ENDIF}
|
|
Exclude(FFlags,ilfFilteredListNeedsUpdate);
|
|
end;
|
|
|
|
procedure TIdentifierList.SetHistory(const AValue: TIdentifierHistoryList);
|
|
begin
|
|
if FHistory=AValue then exit;
|
|
FHistory:=AValue;
|
|
end;
|
|
|
|
procedure TIdentifierList.SetSortForHistory(AValue: boolean);
|
|
begin
|
|
if FSortForHistory=AValue then Exit;
|
|
FSortForHistory:=AValue;
|
|
Clear;
|
|
end;
|
|
|
|
function TIdentifierList.GetFilteredItems(Index: integer): TIdentifierListItem;
|
|
begin
|
|
UpdateFilteredList;
|
|
if (Index<0) or (Index>=FFilteredList.Count) then
|
|
Result:=nil
|
|
else
|
|
Result:=TIdentifierListItem(FFilteredList[Index]);
|
|
end;
|
|
|
|
constructor TIdentifierList.Create;
|
|
begin
|
|
FFlags:=[ilfFilteredListNeedsUpdate];
|
|
FItems:=TAvlTree.CreateObjectCompare(@CompareIdentListItems);
|
|
FIdentView:=TAVLTree.Create(@CompareIdentListItemsForIdents);
|
|
FIdentSearchItem:=TIdentifierListSearchItem.Create;
|
|
FCreatedIdentifiers:=TFPList.Create;
|
|
FSortForHistory:=true;
|
|
FSortMethodForCompletion:=icsScopedAlphabetic;
|
|
end;
|
|
|
|
destructor TIdentifierList.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeAndNil(FUsedTools);
|
|
FreeAndNil(FItems);
|
|
FreeAndNil(FIdentView);
|
|
FreeAndNil(FFilteredList);
|
|
FreeAndNil(FIdentSearchItem);
|
|
FreeAndNil(FCreatedIdentifiers);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIdentifierList.Clear;
|
|
var
|
|
i: Integer;
|
|
p: Pointer;
|
|
begin
|
|
fContextFlags:=[];
|
|
fContext:=CleanFindContext;
|
|
FNewMemberVisibility:=ctnNone;
|
|
FStartBracketLvl:=0;
|
|
fStartContext:=CleanFindContext;
|
|
fStartContextPos.Code:=nil;
|
|
fStartContextPos.X:=1;
|
|
fStartContextPos.Y:=1;
|
|
for i:=0 to FCreatedIdentifiers.Count-1 do begin
|
|
p:=FCreatedIdentifiers[i];
|
|
FreeMem(p);
|
|
end;
|
|
FCreatedIdentifiers.Clear;
|
|
FItems.FreeAndClear;
|
|
FIdentView.Clear;
|
|
if FUsedTools<>nil then
|
|
FUsedTools.Clear;
|
|
FFlags:=FFlags+[ilfFilteredListNeedsUpdate,ilfUsedToolsNeedsUpdate];
|
|
end;
|
|
|
|
procedure TIdentifierList.Add(NewItem: TIdentifierListItem);
|
|
var
|
|
AnAVLNode: TAVLTreeNode;
|
|
begin
|
|
if (ilcfDontAllowProcedures in ContextFlags) and (NewItem.GetDesc = ctnProcedure) and
|
|
not (NewItem.IsFunction or NewItem.IsConstructor)
|
|
then
|
|
begin
|
|
NewItem.Free;
|
|
Exit;
|
|
end;
|
|
|
|
AnAVLNode:=FIdentView.FindKey(NewItem,@CompareIdentListItemsForIdents);
|
|
if AnAVLNode=nil then begin
|
|
if History<>nil then
|
|
NewItem.HistoryIndex:=History.GetHistoryIndex(NewItem);
|
|
FItems.Add(NewItem);
|
|
FIdentView.Add(NewItem);
|
|
FFlags:=FFlags+[ilfFilteredListNeedsUpdate,ilfUsedToolsNeedsUpdate];
|
|
end else begin
|
|
// redefined identifier -> ignore
|
|
//DebugLn('TIdentifierList.Add redefined: ',NewItem.AsString);
|
|
NewItem.Free;
|
|
end;
|
|
end;
|
|
|
|
function TIdentifierList.Count: integer;
|
|
begin
|
|
Result:=FItems.Count;
|
|
end;
|
|
|
|
function TIdentifierList.GetFilteredCount: integer;
|
|
begin
|
|
UpdateFilteredList;
|
|
Result:=FFilteredList.Count;
|
|
end;
|
|
|
|
function TIdentifierList.HasIdentifier(Identifier: PChar;
|
|
const ParamList: string): boolean;
|
|
begin
|
|
FIdentSearchItem.Identifier:=Identifier;
|
|
FIdentSearchItem.ParamList:=ParamList;
|
|
Result:=FIdentView.FindKey(FIdentSearchItem,
|
|
@CompareIdentListSearchWithItems)<>nil;
|
|
end;
|
|
|
|
function TIdentifierList.FindIdentifier(Identifier: PChar;
|
|
const ParamList: string): TIdentifierListItem;
|
|
var
|
|
AVLNode: TAVLTreeNode;
|
|
begin
|
|
FIdentSearchItem.Identifier:=Identifier;
|
|
FIdentSearchItem.ParamList:=ParamList;
|
|
AVLNode:=FIdentView.FindKey(FIdentSearchItem,@CompareIdentListSearchWithItems);
|
|
if AVLNode<>nil then
|
|
Result:=TIdentifierListItem(AVLNode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TIdentifierList.FindIdentifier(Identifier: PChar; PreferProc: boolean
|
|
): TIdentifierListItem;
|
|
var
|
|
AVLNode: TAVLTreeNode;
|
|
StartNode: TAVLTreeNode;
|
|
begin
|
|
Result:=nil;
|
|
FIdentSearchItem.Identifier:=Identifier;
|
|
// ignore ParamList (for checking function overloading)
|
|
StartNode:=FIdentView.FindKey(FIdentSearchItem,@CompareIdentListSearchWithItemsWithoutParams);
|
|
if StartNode=nil then exit;
|
|
// identifier found, check preference
|
|
if (TIdentifierListItem(StartNode.Data).GetDesc in [ctnProcedure,ctnProcedureHead])=PreferProc
|
|
then
|
|
exit(TIdentifierListItem(StartNode.Data));
|
|
|
|
// identifier is a (not) proc, find the same identifier that fits PreferProc
|
|
|
|
// search in next nodes
|
|
AVLNode:=StartNode;
|
|
repeat
|
|
AVLNode:=FIdentView.FindSuccessor(AVLNode);
|
|
if (AVLNode=nil)
|
|
or (CompareIdentifiers(Identifier,PChar(TIdentifierListItem(AVLNode.Data).Identifier))<>0)
|
|
then break;
|
|
if (TIdentifierListItem(AVLNode.Data).GetDesc in [ctnProcedure,ctnProcedureHead])=PreferProc
|
|
then
|
|
exit(TIdentifierListItem(AVLNode.Data));
|
|
until false;
|
|
// search in previous nodes
|
|
AVLNode:=StartNode;
|
|
repeat
|
|
AVLNode:=FIdentView.FindPrecessor(AVLNode);
|
|
if (AVLNode=nil)
|
|
or (CompareIdentifiers(Identifier,PChar(TIdentifierListItem(AVLNode.Data).Identifier))<>0)
|
|
then break;
|
|
if (TIdentifierListItem(AVLNode.Data).GetDesc in [ctnProcedure,ctnProcedureHead])=PreferProc
|
|
then
|
|
exit(TIdentifierListItem(AVLNode.Data));
|
|
until false;
|
|
end;
|
|
|
|
function TIdentifierList.FindCreatedIdentifier(const Ident: string): integer;
|
|
begin
|
|
if Ident<>'' then begin
|
|
Result:=FCreatedIdentifiers.Count-1;
|
|
while (Result>=0)
|
|
and (CompareIdentifiers(PChar(Pointer(Ident)),
|
|
PChar(Pointer(FCreatedIdentifiers[Result])))<>0)
|
|
do
|
|
dec(Result);
|
|
end else begin
|
|
Result:=-1;
|
|
end;
|
|
end;
|
|
|
|
function TIdentifierList.FindIdentifier(Identifier: PChar): TIdentifierListItem;
|
|
var
|
|
Node: TAVLTreeNode;
|
|
begin
|
|
FIdentSearchItem.Identifier:=Identifier;
|
|
// ignore ParamList
|
|
Node:=FIdentView.FindKey(FIdentSearchItem,@CompareIdentListSearchWithItemsWithoutParams);
|
|
if Assigned(Node) then
|
|
Result := TIdentifierListItem(Node.Data)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TIdentifierList.CreateIdentifier(const Ident: string): PChar;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Ident<>'' then begin
|
|
i:=FindCreatedIdentifier(Ident);
|
|
if i>=0 then
|
|
Result:=PChar(Pointer(FCreatedIdentifiers[i]))
|
|
else begin
|
|
GetMem(Result,length(Ident)+1);
|
|
Move(Ident[1],Result^,length(Ident)+1);
|
|
FCreatedIdentifiers.Add(Result);
|
|
end;
|
|
end else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TIdentifierList.StartUpAtomInFrontIs(const s: string): boolean;
|
|
begin
|
|
Result:=StartContext.Tool.FreeUpAtomIs(StartAtomInFront,s);
|
|
end;
|
|
|
|
function TIdentifierList.StartUpAtomBehindIs(const s: string): boolean;
|
|
begin
|
|
Result:=StartContext.Tool.FreeUpAtomIs(StartAtomBehind,s);
|
|
end;
|
|
|
|
function TIdentifierList.CompletePrefix(const OldPrefix: string): string;
|
|
// search all identifiers beginning with Prefix
|
|
// and return the biggest shared prefix of all of them
|
|
var
|
|
AnAVLNode: TAvlTreeNode;
|
|
CurItem: TIdentifierListItem;
|
|
FoundFirst: Boolean;
|
|
SamePos: Integer;
|
|
l: Integer;
|
|
begin
|
|
Result:=OldPrefix;
|
|
FoundFirst:=false;
|
|
AnAVLNode:=FItems.FindLowest;
|
|
while AnAVLNode<>nil do begin
|
|
CurItem:=TIdentifierListItem(AnAVLNode.Data);
|
|
if (CurItem.Identifier<>'')
|
|
and ComparePrefixIdent(PChar(Pointer(Prefix)),PChar(Pointer(CurItem.Identifier)))
|
|
and (not (iliAtCursor in CurItem.Flags))
|
|
then begin
|
|
if not FoundFirst then begin
|
|
Result:=CurItem.Identifier;
|
|
FoundFirst:=true;
|
|
end else begin
|
|
SamePos:=length(Prefix)+1;
|
|
l:=length(Result);
|
|
if l>length(CurItem.Identifier) then
|
|
l:=length(CurItem.Identifier);
|
|
while (SamePos<=l)
|
|
and (UpChars[CurItem.Identifier[SamePos]]=UpChars[Result[SamePos]])
|
|
do
|
|
inc(SamePos);
|
|
if SamePos<=length(Result) then begin
|
|
Result:=copy(Result,1,SamePos-1);
|
|
if length(Result)=length(Prefix) then exit;
|
|
end;
|
|
end;
|
|
end;
|
|
AnAVLNode:=FItems.FindSuccessor(AnAVLNode);
|
|
end;
|
|
end;
|
|
|
|
function TIdentifierList.CalcMemSize: PtrUInt;
|
|
var
|
|
i: Integer;
|
|
Node: TAVLTreeNode;
|
|
AvgNode: TAvlTreeNode;
|
|
li: TIdentifierListItem;
|
|
hli: TIdentHistListItem;
|
|
begin
|
|
Result:=PtrUInt(InstanceSize)
|
|
+MemSizeString(FPrefix);
|
|
if FCreatedIdentifiers<>nil then begin
|
|
inc(Result,MemSizeFPList(FCreatedIdentifiers));
|
|
for i:=0 to FCreatedIdentifiers.Count-1 do
|
|
{%H-}inc(Result,GetIdentLen(PChar(FCreatedIdentifiers[i])));
|
|
end;
|
|
if FFilteredList<>nil then begin
|
|
inc(Result,MemSizeFPList(FFilteredList));
|
|
for i:=0 to FFilteredList.Count-1 do
|
|
inc(Result,TIdentifierListItem(FFilteredList[i]).CalcMemSize);
|
|
end;
|
|
if FHistory<>nil then begin
|
|
inc(Result,FHistory.CalcMemSize);
|
|
end;
|
|
if FItems<>nil then begin
|
|
{%H-}inc(Result,FItems.Count*SizeOf(TAvlTreeNode));
|
|
AvgNode:=FItems.FindLowest;
|
|
while AvgNode<>nil do begin
|
|
li:=TIdentifierListItem(AvgNode.Data);
|
|
inc(Result,li.CalcMemSize);
|
|
AvgNode:=AvgNode.Successor;
|
|
end;
|
|
end;
|
|
if FIdentView<>nil then begin
|
|
{%H-}inc(Result,FIdentView.Count*SizeOf(TAVLTreeNode));
|
|
Node:=FIdentView.FindLowest;
|
|
while Node<>nil do begin
|
|
hli:=TIdentHistListItem(Node.Data);
|
|
inc(Result,hli.CalcMemSize);
|
|
Node:=FIdentView.FindSuccessor(Node);
|
|
end;
|
|
end;
|
|
if FIdentSearchItem<>nil then
|
|
inc(Result,FIdentSearchItem.CalcMemSize);
|
|
end;
|
|
|
|
{ TIdentCompletionTool }
|
|
|
|
procedure TIdentCompletionTool.AddToTreeOfUnitFileInfo(const AFilename: string);
|
|
begin
|
|
AddToTreeOfUnitFilesOrNamespaces(FIDTTreeOfUnitFiles,FIDTTreeOfNamespaces,
|
|
FIDTTreeOfUnitFiles_NamespacePath,AFilename,FIDTTreeOfUnitFiles_CaseInsensitive,false);
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.AddCompilerProcedure(const AProcName, AParameterList: PChar);
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
begin
|
|
//DebugLn(['AddCompilerProcedure ',AProcName,' ',ilcfStartOfStatement in CurrentIdentifierList.ContextFlags]);
|
|
if (ilcfDontAllowProcedures in CurrentIdentifierList.ContextFlags) then exit;
|
|
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompUnknown,
|
|
false,
|
|
CompilerFuncHistoryIndex,
|
|
AProcName,
|
|
CompilerFuncLevel,
|
|
nil,
|
|
nil,
|
|
ctnProcedure);
|
|
NewItem.ParamTypeList:=AParameterList;
|
|
NewItem.ParamNameList:=AParameterList;
|
|
NewItem.Flags:=NewItem.Flags+[iliParamTypeListValid,iliParamNameListValid];
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.AddKeyWord(aKeyWord: string);
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
begin
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompExact,false,0,
|
|
CurrentIdentifierList.CreateIdentifier(aKeyWord),
|
|
1000,nil,nil,ctnNone);
|
|
include(NewItem.Flags,iliKeyword);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.AddCompilerFunction(const AProcName, AParameterList,
|
|
AResultType: PChar);
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
begin
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompUnknown,
|
|
false,
|
|
CompilerFuncHistoryIndex,
|
|
AProcName,
|
|
CompilerFuncLevel,
|
|
nil,
|
|
nil,
|
|
ctnProcedure);
|
|
NewItem.ParamTypeList:=AParameterList;
|
|
NewItem.ParamNameList:=AParameterList;
|
|
NewItem.ResultType:=AResultType;
|
|
NewItem.Flags:=NewItem.Flags+[iliParamTypeListValid,iliParamNameListValid,
|
|
iliIsFunction,iliIsFunctionValid,iliResultTypeValid];
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.AddBaseType(const BaseName: PChar);
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
begin
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompUnknown,
|
|
false,
|
|
CompilerFuncHistoryIndex,
|
|
BaseName,
|
|
CompilerFuncLevel,
|
|
nil,
|
|
nil,
|
|
ctnTypeDefinition);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.AddBaseConstant(const BaseName: PChar);
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
begin
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompUnknown,
|
|
false,
|
|
CompilerFuncHistoryIndex,
|
|
BaseName,
|
|
CompilerFuncLevel,
|
|
nil,
|
|
nil,
|
|
ctnConstant);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
function TIdentCompletionTool.CollectAllIdentifiers(
|
|
Params: TFindDeclarationParams; const FoundContext: TFindContext
|
|
): TIdentifierFoundResult;
|
|
var
|
|
Ident: PChar;
|
|
CurContextParent: TCodeTreeNode;
|
|
|
|
function ProtectedNodeIsInAllowedClass: boolean;
|
|
var
|
|
CurClassNode: TCodeTreeNode;
|
|
FoundClassContext: TFindContext;
|
|
begin
|
|
Result:=false;
|
|
if (FICTClassAndAncestorsAndExtClassOfHelper<>nil) then begin
|
|
// start of the identifier completion is in a method or class
|
|
// => all protected ancestor classes are allowed as well.
|
|
CurClassNode:=FoundContext.Node;
|
|
while (CurClassNode<>nil)
|
|
and (not (CurClassNode.Desc in AllClasses)) do
|
|
CurClassNode:=CurClassNode.Parent;
|
|
if CurClassNode=nil then exit;
|
|
FoundClassContext:=CreateFindContext(Params.NewCodeTool,CurClassNode);
|
|
if IndexOfFindContext(FICTClassAndAncestorsAndExtClassOfHelper,@FoundClassContext)>=0 then begin
|
|
// this class node is the class or one of the ancestors of the class or extended class of the helper+ancestors
|
|
// of the start context of the identifier completion
|
|
exit(true);
|
|
end;
|
|
end;
|
|
//DebugLn(['ProtectedNodeIsInAllowedClass hidden: ',FindContextToString(FoundContext)]);
|
|
end;
|
|
|
|
function PropertyIsOverridenPublicPublish: boolean;
|
|
begin
|
|
// protected properties can be made public in child classes.
|
|
//debugln('PropertyIsOverridenPublicPublish Identifier=',GetIdentifier(Ident),' Find=',dbgs((FIDCTFoundPublicProperties<>nil) and (FIDCTFoundPublicProperties.Find(Ident)<>nil)));
|
|
if FIDCTFoundPublicProperties<>nil then begin
|
|
if FIDCTFoundPublicProperties.Find(Ident)<>nil then begin
|
|
// there is a public/published property with the same name
|
|
exit(true);
|
|
end;
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
procedure SavePublicPublishedProperty;
|
|
begin
|
|
if FIDCTFoundPublicProperties=nil then begin
|
|
// create tree
|
|
FIDCTFoundPublicProperties:=
|
|
TAVLTree.Create(TListSortCompare(@CompareIdentifiers))
|
|
end else if FIDCTFoundPublicProperties.Find(Ident)<>nil then begin
|
|
// identifier is already public
|
|
exit;
|
|
end;
|
|
FIDCTFoundPublicProperties.Add(Ident);
|
|
//debugln('SavePublicPublishedProperty Identifier=',GetIdentifier(Ident),' Find=',dbgs(FIDCTFoundPublicProperties.Find(Ident)<>nil));
|
|
end;
|
|
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
Node: TCodeTreeNode;
|
|
ProtectedForeignClass: Boolean;
|
|
Lvl: LongInt;
|
|
NamePos: TAtomPosition;
|
|
HasLowerVisibility: Boolean;
|
|
begin
|
|
// proceed searching ...
|
|
Result:=ifrProceedSearch;
|
|
|
|
{$IFDEF ShowFoundIdents}
|
|
if FoundContext.Tool=Self then
|
|
DebugLn('::: COLLECT IDENT ',FoundContext.Node.DescAsString,
|
|
' "',StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50)),'"'
|
|
,' '+dbgs(fdfIgnoreUsedUnits in Params.Flags));
|
|
{$ENDIF}
|
|
|
|
CurContextParent:=FoundContext.Node.GetFindContextParent;
|
|
if FLastGatheredIdentParent<>CurContextParent then begin
|
|
// new context level
|
|
FLastGatheredIdentParent:=CurContextParent;
|
|
inc(FLastGatheredIdentLevel);
|
|
end;
|
|
|
|
Lvl:=FLastGatheredIdentLevel;
|
|
HasLowerVisibility:=False;
|
|
|
|
ProtectedForeignClass:=false;
|
|
if FoundContext.Tool=Self then begin
|
|
// identifier is in the same unit
|
|
//DebugLn('::: COLLECT IDENT in SELF ',FoundContext.Node.DescAsString,
|
|
// ' "',dbgstr(FoundContext.Tool.Src,FoundContext.Node.StartPos,50),'"'
|
|
// ,' fdfIgnoreUsedUnits='+dbgs(fdfIgnoreUsedUnits in Params.Flags));
|
|
if (FoundContext.Node=CurrentIdentifierList.StartContext.Node)
|
|
or (FoundContext.Node=CurrentIdentifierList.Context.Node)
|
|
or (FoundContext.Node.StartPos=CurrentIdentifierList.StartAtom.StartPos)
|
|
then begin
|
|
// found identifier is in cursor node
|
|
// => do not show it
|
|
exit;
|
|
end;
|
|
end else begin
|
|
// identifier is in another unit
|
|
Node:=FoundContext.Node.Parent;
|
|
if (Node<>nil) and (Node.Desc in AllClassSubSections) then
|
|
Node:=Node.Parent;
|
|
if (Node<>nil) and (Node.Desc in AllClassBaseSections) then begin
|
|
//debugln(['TIdentCompletionTool.CollectAllIdentifiers Node=',Node.DescAsString,' Context=',CurrentIdentifierList.Context.Node.DescAsString,' CtxVis=',NodeDescToStr(CurrentIdentifierList.NewMemberVisibility)]);
|
|
if (CurrentIdentifierList.NewMemberVisibility<>ctnNone)
|
|
and (CurrentIdentifierList.NewMemberVisibility<Node.Desc)
|
|
and (FoundContext.Node.Desc
|
|
in ([ctnProcedure,ctnProcedureHead,ctnProperty]+AllClassSections))
|
|
then begin
|
|
// the user wants to override a method or property
|
|
// => ignore all with a higher visibility, because fpc does not allow
|
|
// to downgrade the visibility and will give a hint when trying
|
|
//---- No, allow visibility downgrading to reduce confusion tha CodeTools do not list those functions.
|
|
//---- FPC actually allows it although it shows a warning
|
|
//debugln(['TIdentCompletionTool.CollectAllIdentifiers skipping member, because it would downgrade: ',dbgstr(FoundContext.Tool.ExtractNode(FoundContext.Node,[]),1,30)]);
|
|
HasLowerVisibility:=True;
|
|
end;
|
|
case Node.Desc of
|
|
ctnClassPrivate:
|
|
begin
|
|
// skip private definitions in other units
|
|
exit;
|
|
end;
|
|
ctnClassProtected:
|
|
begin
|
|
// protected definitions are only accessible from descendants
|
|
// or if visibility was raised (e.g. property)
|
|
if ProtectedNodeIsInAllowedClass then begin
|
|
// protected node in an ancestor => allowed
|
|
//debugln('TIdentCompletionTool.CollectAllIdentifiers ALLOWED Protected in ANCESTOR '+StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50)));
|
|
end else if (FoundContext.Node.Desc=ctnProperty) then begin
|
|
// protected property: maybe the visibility was raised => continue
|
|
ProtectedForeignClass:=true;
|
|
//debugln('TIdentCompletionTool.CollectAllIdentifiers MAYBE Protected made Public '+StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50)));
|
|
end else begin
|
|
// otherwise: treat as private
|
|
//debugln('TIdentCompletionTool.CollectAllIdentifiers FORBIDDEN Protected '+StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50)));
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Ident:=nil;
|
|
case FoundContext.Node.Desc of
|
|
|
|
ctnTypeDefinition,ctnGenericType:
|
|
begin
|
|
Node:=FoundContext.Node.FirstChild;
|
|
if FoundContext.Node.Desc=ctnTypeDefinition then
|
|
Ident:=@FoundContext.Tool.Src[FoundContext.Node.StartPos]
|
|
else begin
|
|
// generic
|
|
if Node=nil then exit;
|
|
Ident:=@FoundContext.Tool.Src[Node.StartPos];
|
|
end;
|
|
if Node=nil then begin
|
|
// type without definition
|
|
end;
|
|
if (Node<>nil)
|
|
and (Node.Desc in AllClasses)
|
|
and ((ctnsForwardDeclaration and Node.SubDesc)>0)
|
|
then begin
|
|
// forward definition of a class
|
|
if CurrentIdentifierList.FindIdentifier(Ident,'')<>nil then begin
|
|
// the real class is already in the list => skip forward
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
ctnGenericParameter:
|
|
Ident:=@FoundContext.Tool.Src[FoundContext.Node.StartPos];
|
|
|
|
ctnVarDefinition,ctnConstDefinition,ctnEnumIdentifier,ctnLabel,ctnGlobalProperty:
|
|
Ident:=@FoundContext.Tool.Src[FoundContext.Node.StartPos];
|
|
|
|
ctnProcedure,ctnProcedureHead:
|
|
//do not list class constructors and destructors
|
|
if not FoundContext.Tool.NodeIsClassConstructorOrDestructor(FoundContext.Node) then
|
|
begin
|
|
Ident:=FoundContext.Tool.GetProcNameIdentifier(FoundContext.Node);
|
|
NewItem := CurrentIdentifierList.FindIdentifier(Ident,true);
|
|
if (NewItem<>nil) and (NewItem.Tool<>nil) then begin
|
|
if (NewItem.GetNode<>nil) then begin
|
|
if (FoundContext.Node.Parent.Desc in AllClassBaseSections)
|
|
<> (NewItem.Node.Parent.Desc in AllClassBaseSections)
|
|
then
|
|
exit; // class members hide normal procs and nested procs hide class members
|
|
if (Lvl <> NewItem.Level) then begin
|
|
// there is a previous declaration on a different level
|
|
if (NewItem.Node.Desc<>ctnProcedure)
|
|
or (not NewItem.Tool.ProcNodeHasSpecifier(NewItem.Node, psOVERLOAD))
|
|
then
|
|
exit; // there is a previous declaration without 'overload'
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
ctnProperty:
|
|
begin
|
|
Ident:=FoundContext.Tool.GetPropertyNameIdentifier(FoundContext.Node);
|
|
if FoundContext.Tool.PropNodeIsTypeLess(FoundContext.Node) then begin
|
|
if FoundContext.Node.Parent.Desc in [ctnClassPublic,ctnClassPublished]
|
|
then
|
|
SavePublicPublishedProperty;
|
|
// do not show properties without types (e.g. property Color;)
|
|
// only show the real definition, which will follow in the ancestor
|
|
exit;
|
|
end;
|
|
if (FoundContext.Node.Parent.Desc=ctnClassPrivate)
|
|
and (FoundContext.Tool<>Self)
|
|
and (not PropertyIsOverridenPublicPublish) then begin
|
|
// a private property in another unit, that was not
|
|
// made public/publish later
|
|
// => skip
|
|
exit;
|
|
end;
|
|
if (FoundContext.Node.Parent.Desc=ctnClassProtected)
|
|
and ProtectedForeignClass
|
|
and (not PropertyIsOverridenPublicPublish) then begin
|
|
// a protected property in another unit, that was not
|
|
// made public/publish later
|
|
// => skip
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
ctnRecordCase:
|
|
Ident:=@FoundContext.Tool.Src[Params.NewCleanPos];
|
|
|
|
ctnUseUnitNamespace,ctnUseUnitClearName:
|
|
if (FoundContext.Tool=Self) then begin
|
|
Ident:=@Src[FoundContext.Node.StartPos];
|
|
end;
|
|
|
|
ctnUnit,ctnProgram,ctnLibrary,ctnPackage:
|
|
if (FoundContext.Tool=Self)
|
|
and GetSourceNamePos(NamePos) then
|
|
Ident:=@Src[NamePos.StartPos];
|
|
|
|
end;
|
|
if Ident=nil then exit;
|
|
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompUnknown,
|
|
false,
|
|
0,
|
|
Ident,
|
|
Lvl,
|
|
FoundContext.Node,
|
|
FoundContext.Tool,
|
|
ctnNone);
|
|
|
|
//Add the '&' character to prefixed identifiers
|
|
if (Ident^='&') and (IsIdentStartChar[Ident[1]]) then
|
|
Include(NewItem.Flags,iliNeedsAmpersand);
|
|
|
|
// found identifier is in cursor node
|
|
if (FoundContext.Node=CurrentIdentifierList.StartContext.Node) then
|
|
Include(NewItem.Flags,iliAtCursor);
|
|
|
|
// method has lower visibility
|
|
if HasLowerVisibility then
|
|
Include(NewItem.Flags,iliHasLowerVisibility);
|
|
|
|
{$IFDEF ShowFoundIdents}
|
|
if FoundContext.Tool=Self then
|
|
DebugLn(' IDENT COLLECTED: ',NewItem.AsString);
|
|
{$ENDIF}
|
|
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.GatherPredefinedIdentifiers(CleanPos: integer;
|
|
const Context, GatherContext: TFindContext);
|
|
// Add predefined identifiers
|
|
|
|
function StatementLevel: integer;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
Result:=0;
|
|
ANode:=Context.Node;
|
|
while (ANode<>nil) and (not (ANode.Desc in [ctnBeginBlock,ctnAsmBlock])) do
|
|
begin
|
|
ANode:=ANode.Parent;
|
|
inc(Result);
|
|
end;
|
|
if ANode=nil then Result:=0;
|
|
end;
|
|
|
|
procedure AddSystemUnit(const AnUnitName: PChar);
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
begin
|
|
NewItem:=CUnitNameSpaceIdentifierListItem.Create(
|
|
icompUnknown,
|
|
false,
|
|
CompilerFuncHistoryIndex,
|
|
AnUnitName,
|
|
CompilerFuncLevel,
|
|
nil,
|
|
nil,
|
|
ctnUseUnitClearName,
|
|
AnUnitName,
|
|
1);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
ProcNode: TCodeTreeNode;
|
|
HiddenUnits: String;
|
|
p: PChar;
|
|
SystemTool: TFindDeclarationTool;
|
|
I: TExpressionTypeDesc;
|
|
InSystemContext: Boolean;
|
|
FPCFulVersion: LongInt;
|
|
begin
|
|
if CleanPos=0 then ;
|
|
|
|
SystemTool := FindCodeToolForUsedUnit('System','',False);
|
|
InSystemContext :=
|
|
(ilcfStartOfOperand in CurrentIdentifierList.ContextFlags) or
|
|
((ilcfStartIsSubIdent in CurrentIdentifierList.ContextFlags) and
|
|
(GatherContext.Tool<>nil) and (GatherContext.Node<>nil) and (SystemTool<>nil) and
|
|
(GatherContext.Tool = SystemTool) and (GatherContext.Node = SystemTool.FindInterfaceNode));
|
|
|
|
if InSystemContext and (Context.Node.Desc in AllPascalStatements) then
|
|
begin
|
|
// see fpc/compiler/psystem.pp
|
|
FPCFulVersion:=StrToIntDef(Scanner.Values['FPC_FULLVERSION'],0);
|
|
AddCompilerProcedure('Assert','Condition:Boolean;const Message:String');
|
|
AddCompilerFunction('Assigned','P:Pointer','Boolean');
|
|
AddCompilerFunction('Addr','var X','Pointer');
|
|
AddCompilerFunction('BitSizeOf','Identifier','Integer');
|
|
AddCompilerProcedure('Break','');
|
|
AddCompilerFunction('Concat','S1:String;S2:String[...;Sn:String]', 'String');
|
|
if FPCFulVersion>=30100 then
|
|
AddCompilerFunction('Concat','A1:Array;[...;An:Array]', 'Array');
|
|
AddCompilerProcedure('Continue','');
|
|
if FPCFulVersion>=30100 then
|
|
begin
|
|
// FromPosition and Count parameters are optional
|
|
AddCompilerFunction('Copy','const S:string[;FromPosition,Count:SizeInt]', 'string');
|
|
AddCompilerFunction('Copy','const A:array[;FromPosition,Count:SizeInt]', 'string');
|
|
end else
|
|
begin
|
|
AddCompilerFunction('Copy','const S:string;FromPosition,Count:SizeInt', 'string');
|
|
AddCompilerFunction('Copy','const A:array;FromPosition,Count:SizeInt', 'string');
|
|
end;
|
|
AddCompilerProcedure('Dec','var X:Ordinal;N:Integer=1');
|
|
AddCompilerFunction('Default','T:Type','const');
|
|
if FPCFulVersion>=30100 then //Delete and Insert are available as intrinsic since FPC 3.1
|
|
begin
|
|
AddCompilerProcedure('Delete','var S:string;Index,Count:Integer');
|
|
AddCompilerProcedure('Delete','var A:array;Index,Count:Integer');
|
|
AddCompilerProcedure('Insert','const Source:string;var Dest:string;Index:Integer');
|
|
AddCompilerProcedure('Insert','Item; var A:array;Index:Integer');
|
|
end;
|
|
AddCompilerProcedure('Dispose','var X:Pointer');
|
|
AddCompilerProcedure('Exclude','var S:Set;X:Ordinal');
|
|
AddCompilerProcedure('Exit','');
|
|
AddCompilerProcedure('Finalize','var X');
|
|
AddCompilerFunction('get_frame','','Pointer');
|
|
AddCompilerFunction('High','Arg:TypeOrVariable','Ordinal');
|
|
AddCompilerProcedure('Inc','var X:Ordinal;N:Integer=1');
|
|
AddCompilerProcedure('Include','var S:Set;X:Ordinal');
|
|
AddCompilerProcedure('Initialize','var X');
|
|
AddCompilerFunction('Length','S:String','SizeInt');
|
|
AddCompilerFunction('Length','A:Array','SizeInt');
|
|
AddCompilerFunction('Low','Arg:TypeOrVariable','Ordinal');
|
|
AddCompilerProcedure('New','var X:Pointer');
|
|
AddCompilerFunction('ObjCSelector','String','SEL');
|
|
AddCompilerFunction('Ofs','var X','LongInt');
|
|
AddCompilerFunction('Ord','X:Ordinal', 'Integer');
|
|
AddCompilerProcedure('Pack','A:Array;N:Integer;var A:Array');
|
|
AddCompilerFunction('Pred','X:Ordinal', 'Ordinal');
|
|
AddCompilerProcedure('Read','');
|
|
AddCompilerProcedure('ReadLn','');
|
|
AddCompilerProcedure('ReadStr','S:String;var Args:Arguments');
|
|
AddCompilerFunction('Seg','var X','LongInt');
|
|
AddCompilerProcedure('SetLength','var S:String;NewLength:SizeInt');
|
|
AddCompilerProcedure('SetLength','var A:Array;NewLength:SizeInt');
|
|
if Scanner.Values.IsDefined('FPC_HAS_CPSTRING') then begin
|
|
AddCompilerProcedure('SetString','out S:RawByteString;Buf:PAnsiChar;Len:SizeInt');
|
|
AddCompilerProcedure('SetString','out S:AnsiString;Buf:PAnsiChar;Len:SizeInt');
|
|
AddCompilerProcedure('SetString','out S:AnsiString;Buf:PWideChar;Len:SizeInt');
|
|
AddCompilerProcedure('SetString','out S:ShortString;Buf:PChar;Len:SizeInt');
|
|
AddCompilerProcedure('SetString','out S:UnicodeString;Buf:PUnicodeChar;Len:SizeInt');
|
|
AddCompilerProcedure('SetString','out S:UnicodeString;Buf:PChar;Len:SizeInt');
|
|
AddCompilerProcedure('SetString','out S:WideString;Buf:PWideChar;Len:SizeInt');
|
|
AddCompilerProcedure('SetString','out S:WideString;Buf:PChar;Len:SizeInt');
|
|
end;
|
|
AddCompilerFunction('SizeOf','Identifier','Integer');
|
|
AddCompilerFunction('Slice','var A:Array;Count:Integer','Array');
|
|
AddCompilerProcedure('Str','const X[:Width[:Decimals]];var S:String');
|
|
AddCompilerFunction('Succ','X:Ordinal', 'Ordinal');
|
|
AddCompilerFunction('TypeInfo','Identifier', 'Pointer');
|
|
AddCompilerFunction('GetTypeKind','Identifier', 'TTypeKind');
|
|
AddCompilerFunction('IsManagedType','Identifier', 'Boolean');
|
|
AddCompilerFunction('IsConstValue','const Value', 'Boolean');
|
|
AddCompilerFunction('TypeOf','Identifier', 'Pointer');
|
|
AddCompilerProcedure('Val','S:String;var V;var Code:Integer');
|
|
AddCompilerFunction('Unaligned','var X','var'); // Florian declaration :)
|
|
AddCompilerProcedure('Unpack','A:Array;var A:Array;N:Integer');
|
|
AddCompilerProcedure('Write','Args:Arguments');
|
|
AddCompilerProcedure('WriteLn','Args:Arguments');
|
|
AddCompilerProcedure('WriteStr','var S:String;Args:Arguments');
|
|
if Scanner.PascalCompiler=pcPas2js then begin
|
|
AddCompilerFunction('Str','const X[:Width[:Decimals]]','string');
|
|
AddCompilerFunction('AWait','const Expr: T','T');
|
|
AddCompilerFunction('AWait','aType; p: TJSPromise','aType');
|
|
end;
|
|
end;
|
|
|
|
if (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags) and
|
|
(Context.Node.Desc in AllPascalStatements)
|
|
then
|
|
begin
|
|
if (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags)
|
|
and Context.Tool.NodeIsInAMethod(Context.Node)
|
|
and (not CurrentIdentifierList.HasIdentifier('Self','')) then begin
|
|
// method body -> add 'Self'
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompUnknown,
|
|
true,
|
|
1,
|
|
'Self',
|
|
StatementLevel,
|
|
nil,
|
|
nil,
|
|
ctnVarDefinition);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
ProcNode:=Context.Node.GetNodeOfType(ctnProcedure);
|
|
if (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags)
|
|
and Context.Tool.NodeIsFunction(ProcNode)
|
|
and (not CurrentIdentifierList.HasIdentifier('Result','')) then begin
|
|
// function body -> add 'Result'
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompUnknown,
|
|
true,
|
|
1,
|
|
'Result',
|
|
StatementLevel,
|
|
nil,
|
|
nil,
|
|
ctnVarDefinition);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
end;
|
|
|
|
// system types
|
|
if InSystemContext then
|
|
begin
|
|
for I in [xtChar..xtPointer, xtLongint..xtByte, xtVariant] do
|
|
AddBaseType(PChar(ExpressionTypeDescNames[I]));
|
|
if not (ilcfStartInStatement in CurrentIdentifierList.ContextFlags) then
|
|
for I in [xtFile, xtText] do
|
|
AddBaseType(PChar(ExpressionTypeDescNames[I]));
|
|
if Scanner.PascalCompiler=pcPas2js then begin
|
|
for I in xtAllPas2JSExtraTypes do
|
|
AddBaseType(PChar(ExpressionTypeDescNames[I]));
|
|
end;
|
|
AddBaseConstant('True');
|
|
AddBaseConstant('False');
|
|
//the nil constant doesn't belong to system context, therefore it is added in next step
|
|
end;
|
|
if (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags) then
|
|
begin
|
|
AddBaseConstant(PChar(ExpressionTypeDescNames[xtNil]));
|
|
// system units
|
|
HiddenUnits:=Scanner.GetHiddenUsedUnits;
|
|
if HiddenUnits<>'' then begin
|
|
p:=PChar(HiddenUnits);
|
|
while p^<>#0 do begin
|
|
while p^=',' do inc(p);
|
|
if GetIdentLen(p)>0 then
|
|
AddSystemUnit(p);
|
|
while not (p^ in [',',#0]) do inc(p);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.GatherUsefulIdentifiers(CleanPos: integer;
|
|
const Context, GatherContext: TFindContext);
|
|
|
|
procedure AddPropertyProc(ProcName: string);
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
begin
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompExact,true,0,
|
|
CurrentIdentifierList.CreateIdentifier(ProcName),
|
|
0,nil,nil,ctnProcedure);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
var
|
|
PropertyName: String;
|
|
begin
|
|
//debugln(['TIdentCompletionTool.GatherUsefulIdentifiers ',CleanPosToStr(CleanPos),' ',dbgsFC(Context)]);
|
|
GatherPredefinedIdentifiers(CleanPos,Context,GatherContext);
|
|
if Context.Node.Desc=ctnProperty then begin
|
|
PropertyName:=ExtractPropName(Context.Node,false);
|
|
//debugln('TIdentCompletionTool.GatherUsefulIdentifiers Property ',PropertyName);
|
|
MoveCursorToCleanPos(CleanPos);
|
|
ReadPriorAtom;
|
|
//debugln(['TIdentCompletionTool.GatherUsefulIdentifiers Atom=',GetAtom]);
|
|
if UpAtomIs('READ') then begin
|
|
// add the default class completion 'read' specifier function
|
|
AddPropertyProc(Beautifier.PropertyReadIdentPrefix+PropertyName);
|
|
end;
|
|
if UpAtomIs('WRITE') then begin
|
|
// add the default class completion 'write' specifier function
|
|
AddPropertyProc(Beautifier.PropertyWriteIdentPrefix+PropertyName);
|
|
end;
|
|
if (UpAtomIs('READ') or UpAtomIs('WRITE'))
|
|
and (Context.Tool.FindClassOrInterfaceNode(Context.Node)<>nil)
|
|
then begin
|
|
// add the default class completion 'read'/'write' specifier variable
|
|
AddPropertyProc(Beautifier.PrivateVariablePrefix+PropertyName);
|
|
end;
|
|
if UpAtomIs('STORED') then begin
|
|
// add the default class completion 'stored' specifier function
|
|
AddPropertyProc(PropertyName+Beautifier.PropertyStoredIdentPostfix);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.GatherUserIdentifiers(
|
|
const ContextFlags: TIdentifierListContextFlags);
|
|
begin
|
|
if Assigned(FOnGatherUserIdentifiers) then
|
|
FOnGatherUserIdentifiers(Self, ContextFlags);
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.GatherUnitnames(const NameSpacePath: string);
|
|
|
|
procedure GatherUnitsFromSet;
|
|
begin
|
|
// collect all unit files in fpc unit paths
|
|
DirectoryCache.IterateFPCUnitsInSet(@AddToTreeOfUnitFileInfo);
|
|
end;
|
|
|
|
var
|
|
UnitPath, SrcPath: string;
|
|
BaseDir: String;
|
|
ANode: TAVLTreeNode;
|
|
UnitFileInfo: TUnitFileInfo;
|
|
NewItem: TUnitNameSpaceIdentifierListItem;
|
|
UnitExt: String;
|
|
SrcExt: String;
|
|
CurSourceName: String;
|
|
NameSpaceInfo: TNameSpaceInfo;
|
|
begin
|
|
UnitPath:='';
|
|
SrcPath:='';
|
|
GatherUnitAndSrcPath(UnitPath,SrcPath);
|
|
CurSourceName:=GetSourceName;
|
|
//DebugLn('TIdentCompletionTool.GatherUnitnames CurSourceName="',CurSourceName,'" UnitPath="',UnitPath,'" SrcPath="',SrcPath,'"');
|
|
BaseDir:=ExtractFilePath(MainFilename);
|
|
FIDTTreeOfUnitFiles:=nil;
|
|
FIDTTreeOfNamespaces:=nil;
|
|
try
|
|
// search in unitpath
|
|
FIDTTreeOfUnitFiles_CaseInsensitive := true;
|
|
FIDTTreeOfUnitFiles_NamespacePath := NameSpacePath;
|
|
{$IFDEF VerboseICGatherUnitNames}
|
|
FIDTTreeOfUnitFiles:=TAVLTree.Create(@CompareUnitFileInfos);
|
|
{$ENDIF}
|
|
|
|
UnitExt:=PascalCompilerUnitExt[Scanner.PascalCompiler];
|
|
if Scanner.CompilerMode=cmMacPas then
|
|
UnitExt:=UnitExt+';p';
|
|
GatherUnitFiles(BaseDir,UnitPath,UnitExt,NameSpacePath,false,true,FIDTTreeOfUnitFiles, FIDTTreeOfNamespaces);
|
|
{$IFDEF VerboseICGatherUnitNames}
|
|
debugln(['TIdentCompletionTool.GatherUnitnames UnitPath ',FIDTTreeOfUnitFiles.Count]);
|
|
{$ENDIF}
|
|
// search in srcpath
|
|
SrcExt:=PascalCompilerSrcExt[Scanner.PascalCompiler];
|
|
if Scanner.CompilerMode=cmMacPas then
|
|
SrcExt:=SrcExt+';p';
|
|
GatherUnitFiles(BaseDir,SrcPath,SrcExt,NameSpacePath,false,true,FIDTTreeOfUnitFiles, FIDTTreeOfNamespaces);
|
|
{$IFDEF VerboseICGatherUnitNames}
|
|
debugln(['TIdentCompletionTool.GatherUnitnames Plus SrcPath ',FIDTTreeOfUnitFiles.Count]);
|
|
{$ENDIF}
|
|
// add default units
|
|
GatherUnitsFromSet;
|
|
{$IFDEF VerboseICGatherUnitNames}
|
|
debugln(['TIdentCompletionTool.GatherUnitnames Plus FPC units ',FIDTTreeOfUnitFiles.Count]);
|
|
{$ENDIF}
|
|
// create list
|
|
if FIDTTreeOfUnitFiles<>nil then
|
|
begin
|
|
ANode:=FIDTTreeOfUnitFiles.FindLowest;
|
|
while ANode<>nil do begin
|
|
UnitFileInfo:=TUnitFileInfo(ANode.Data);
|
|
ANode:=FIDTTreeOfUnitFiles.FindSuccessor(ANode);
|
|
if CompareText(PChar(Pointer(UnitFileInfo.FileUnitName)), Length(UnitFileInfo.FileUnitName),
|
|
PChar(Pointer(CurSourceName)), Length(CurSourceName), False)=0
|
|
then
|
|
continue;
|
|
NewItem:=CUnitNameSpaceIdentifierListItem.Create(
|
|
icompCompatible,true,0,
|
|
CurrentIdentifierList.CreateIdentifier(UnitFileInfo.FileUnitNameWithoutNamespace),
|
|
0,nil,nil,ctnUnit, PChar(UnitFileInfo.FileUnitName), UnitFileInfo.IdentifierStartInUnitName);
|
|
if NewItem.IdentifierStartInUnitName < 1 then
|
|
NewItem.IdentifierStartInUnitName := 1;
|
|
{$IFDEF VerboseICGatherUnitNames}
|
|
//debugln(['TIdentCompletionTool.GatherUnitnames Add ',UnitFileInfo.FileUnitName,' NewCount=',CurrentIdentifierList]);
|
|
{$ENDIF}
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
end;
|
|
if FIDTTreeOfNamespaces<>nil then
|
|
begin
|
|
ANode:=FIDTTreeOfNamespaces.FindLowest;
|
|
while ANode<>nil do begin
|
|
NameSpaceInfo:=TNameSpaceInfo(ANode.Data);
|
|
NewItem:=CUnitNameSpaceIdentifierListItem.Create(
|
|
icompCompatible,true,0,
|
|
CurrentIdentifierList.CreateIdentifier(NameSpaceInfo.NameSpace),
|
|
0,nil,nil,ctnUseUnitNamespace, PChar(NameSpaceInfo.UnitName),
|
|
NameSpaceInfo.IdentifierStartInUnitName);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
ANode:=FIDTTreeOfNamespaces.FindSuccessor(ANode);
|
|
end;
|
|
end;
|
|
finally
|
|
FreeTreeOfUnitFiles(FIDTTreeOfUnitFiles);
|
|
FreeTreeOfUnitFiles(FIDTTreeOfNamespaces);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.GatherSourceNames(const Context: TFindContext);
|
|
|
|
procedure Add(const SrcName: string);
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
begin
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompExact,true,0,
|
|
CurrentIdentifierList.CreateIdentifier(SrcName),
|
|
0,nil,nil,Context.Node.Desc);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
var
|
|
NewSourceName: String;
|
|
FileSourceName: String;
|
|
begin
|
|
// add the unitname as in the filename and as in the source
|
|
FileSourceName:=ExtractFilenameOnly(MainFilename);
|
|
NewSourceName:=GetSourceName(false);
|
|
//DebugLn('TIdentCompletionTool.GatherSourceNames FileSourceName=',FileSourceName,' NewSourceName=',NewSourceName);
|
|
if (FileSourceName<>lowercase(FileSourceName)) then begin
|
|
// the file is not written lowercase => case is important, ignore source name
|
|
Add(FileSourceName);
|
|
end else if (SysUtils.CompareText(NewSourceName,FileSourceName)<>0) then begin
|
|
// source name is not correct => only use file name
|
|
Add(FileSourceName);
|
|
end else if NewSourceName=FileSourceName then begin
|
|
// both are the same => add only one
|
|
Add(FileSourceName);
|
|
end else begin
|
|
// both are valid, just different in case
|
|
// the filename is written lowercase
|
|
// => prefer the source name
|
|
Add(NewSourceName);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.GatherContextKeywords(
|
|
const Context: TFindContext; CleanPos: integer;
|
|
BeautifyCodeOptions: TBeautifyCodeOptions; const GatherContext: TFindContext);
|
|
type
|
|
TPropertySpecifier = (
|
|
psIndex,psRead,psWrite,psStored,psImplements,psDefault,psNoDefault
|
|
);
|
|
TPropertySpecifiers = set of TPropertySpecifier;
|
|
|
|
procedure Add(Keyword: string);
|
|
var
|
|
NewItem: TIdentifierListItem;
|
|
begin
|
|
KeyWord:=BeautifyCodeOptions.BeautifyKeyWord(Keyword);
|
|
NewItem:=CIdentifierListItem.Create(
|
|
icompExact,false,0,
|
|
CurrentIdentifierList.CreateIdentifier(Keyword),
|
|
1000,nil,nil,ctnNone);
|
|
include(NewItem.Flags,iliKeyword);
|
|
CurrentIdentifierList.Add(NewItem);
|
|
end;
|
|
|
|
procedure AddSpecifiers(Forbidden: TPropertySpecifiers);
|
|
begin
|
|
if not (psIndex in Forbidden) then Add('index');
|
|
if not (psRead in Forbidden) then Add('read');
|
|
if not (psWrite in Forbidden) then Add('write');
|
|
if not (psStored in Forbidden) then Add('stored');
|
|
if not (psImplements in Forbidden) then Add('implements');
|
|
if not (psDefault in Forbidden) then Add('default');
|
|
if not (psNoDefault in Forbidden) then Add('nodefault');
|
|
end;
|
|
|
|
procedure CheckProperty(PropNode: TCodeTreeNode);
|
|
var
|
|
Forbidden: TPropertySpecifiers;
|
|
begin
|
|
if not MoveCursorToPropType(PropNode) then exit;
|
|
if CleanPos<CurPos.EndPos then exit;
|
|
ReadNextAtom;
|
|
if CurPos.Flag=cafPoint then begin
|
|
ReadNextAtom;
|
|
if CurPos.Flag<>cafWord then exit;
|
|
ReadNextAtom;
|
|
end;
|
|
Forbidden:=[];
|
|
repeat
|
|
if CleanPos<=CurPos.EndPos then begin
|
|
AddSpecifiers(Forbidden);
|
|
exit;
|
|
end;
|
|
if (not (psIndex in Forbidden)) and UpAtomIs('INDEX') then begin
|
|
ReadNextAtom;
|
|
Include(Forbidden,psIndex);
|
|
end else if (not (psRead in Forbidden)) and UpAtomIs('READ') then begin
|
|
ReadNextAtom;
|
|
Forbidden:=Forbidden+[psIndex..psRead];
|
|
end else if (not (psWrite in Forbidden)) and UpAtomIs('WRITE') then begin
|
|
ReadNextAtom;
|
|
Forbidden:=Forbidden+[psIndex..psWrite];
|
|
end else if (not (psImplements in Forbidden)) and UpAtomIs('IMPLEMENTS')
|
|
then begin
|
|
ReadNextAtom;
|
|
exit;
|
|
end else if (not (psStored in Forbidden)) and UpAtomIs('STORED') then
|
|
begin
|
|
ReadNextAtom;
|
|
Forbidden:=Forbidden+[psIndex..psImplements];
|
|
end else if (not (psDefault in Forbidden)) and UpAtomIs('DEFAULT') then
|
|
begin
|
|
ReadNextAtom;
|
|
exit;
|
|
end else if (not (psNoDefault in Forbidden)) and UpAtomIs('NODEFAULT') then
|
|
begin
|
|
ReadNextAtom;
|
|
exit;
|
|
end else if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen] then begin
|
|
if not ReadTilBracketClose(false) then exit;
|
|
end else
|
|
ReadNextAtom;
|
|
until (CleanPos<CurPos.StartPos) or (CurPos.EndPos>SrcLen);
|
|
end;
|
|
|
|
procedure AddMethodSpecifiers;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to IsKeyWordMethodSpecifier.Count-1 do
|
|
Add(IsKeyWordMethodSpecifier.GetItem(i).KeyWord+';');
|
|
end;
|
|
|
|
procedure AddProcSpecifiers;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to IsKeyWordProcedureSpecifier.Count-1 do
|
|
Add(IsKeyWordProcedureSpecifier.GetItem(i).KeyWord+';');
|
|
end;
|
|
|
|
procedure AddProcTypeSpecifiers;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to IsKeyWordProcedureTypeSpecifier.Count-1 do
|
|
Add(IsKeyWordProcedureTypeSpecifier.GetItem(i).KeyWord+';');
|
|
end;
|
|
|
|
var
|
|
Node, SubNode, NodeInFront: TCodeTreeNode;
|
|
p, AtomStartPos, AtomEndPos: Integer;
|
|
NodeBehind, LastChild: TCodeTreeNode;
|
|
NotStartOfOp: Boolean;
|
|
begin
|
|
try
|
|
AtomStartPos:=CleanPos;
|
|
AtomEndPos:=CleanPos;
|
|
NodeInFront:=nil;
|
|
|
|
Node:=Context.Node;
|
|
if Node<>nil then begin
|
|
MoveCursorToNearestAtom(CleanPos);
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords MoveCursorToNearestAtom Node=',Node.DescAsString,' Atom="',GetAtom,'"']);
|
|
{$ENDIF}
|
|
ReadNextAtom;
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords MoveCursorToNearestAtom+ReadNextAtom Node=',Node.DescAsString,' Atom="',GetAtom,'"']);
|
|
{$ENDIF}
|
|
AtomStartPos:=CurPos.StartPos;
|
|
AtomEndPos:=CurPos.EndPos;
|
|
if CleanPos<=AtomEndPos then begin
|
|
// CleanPos is within an atom
|
|
while (Node.Parent<>nil)
|
|
and (AtomStartPos=Node.StartPos) do
|
|
// at the start of the node -> the node is created by the atom at cursor
|
|
// use parent as context
|
|
Node:=Node.Parent;
|
|
|
|
// get node in front
|
|
ReadPriorAtomSafe(AtomStartPos);
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords prioratom=',CleanPosToStr(CurPos.StartPos),'="',GetAtom(CurPos),'"']);
|
|
{$ENDIF}
|
|
if CurPos.StartPos>0 then
|
|
NodeInFront:=FindDeepestNodeAtPos(CurPos.StartPos,false);
|
|
end else begin
|
|
// CleanPos is between an atom
|
|
NodeInFront:=FindDeepestNodeAtPos(AtomEndPos,false);
|
|
end;
|
|
end;
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords Node=',Node.DescAsString,' Atom="',GetAtom,'"']);
|
|
{$ENDIF}
|
|
|
|
NodeBehind:=nil;
|
|
MoveCursorToCleanPos(AtomStartPos);
|
|
ReadNextAtom;
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords nextatom=',CleanPosToStr(CurPos.StartPos),'=',GetAtom(CurPos)]);
|
|
{$ENDIF}
|
|
if CurPos.StartPos>CleanPos then
|
|
NodeBehind:=FindDeepestNodeAtPos(CurPos.StartPos,false);
|
|
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords CASE Node=',Node.DescAsString,' NodeInFront=',NodeInFront.DescAsString,' NodeBehind=',NodeBehind.DescAsString]);
|
|
{$ENDIF}
|
|
|
|
case Node.Desc of
|
|
ctnClass,ctnObject,ctnRecordType,ctnObjCCategory,ctnObjCClass,
|
|
ctnClassHelper, ctnRecordHelper, ctnTypeHelper,
|
|
ctnClassPrivate,ctnClassProtected,ctnClassPublic,ctnClassPublished:
|
|
begin
|
|
Add('public');
|
|
Add('private');
|
|
Add('protected');
|
|
Add('published');
|
|
Add('procedure');
|
|
Add('function');
|
|
Add('property');
|
|
if (Node.Desc=ctnClass) or (Node.Parent.Desc=ctnClass) then begin
|
|
Add('constructor');
|
|
Add('destructor');
|
|
end;
|
|
if (Node.Desc=ctnRecordType) or (Node.Parent.Desc=ctnRecordType) then begin
|
|
Add('case');
|
|
end;
|
|
LastChild:=Node.LastChild;
|
|
if (LastChild<>nil) and (CleanPos>LastChild.StartPos)
|
|
and (LastChild.EndPos>LastChild.StartPos)
|
|
and (LastChild.EndPos<Srclen) then begin
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords end of class section ',dbgstr(copy(Src,Node.LastChild.EndPos-10,10))]);
|
|
{$ENDIF}
|
|
SubNode:=LastChild;
|
|
if SubNode.Desc=ctnProperty then begin
|
|
CheckProperty(SubNode);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
ctnClassInterface,ctnDispinterface,ctnObjCProtocol,ctnCPPClass:
|
|
begin
|
|
Add('procedure');
|
|
Add('function');
|
|
end;
|
|
|
|
ctnInterface,ctnImplementation:
|
|
begin
|
|
if (Node.FirstChild=nil)
|
|
or ((Node.FirstChild.Desc<>ctnUsesSection)
|
|
and (Node.FirstChild.StartPos>=CleanPos))
|
|
then
|
|
Add('uses');
|
|
Add('type');
|
|
Add('var');
|
|
Add('const');
|
|
Add('label');
|
|
Add('procedure');
|
|
Add('function');
|
|
Add('resourcestring');
|
|
if Node.Desc=ctnInterface then begin
|
|
Add('property');
|
|
end;
|
|
if (NodeBehind=nil)
|
|
or (NodeBehind.Desc in [ctnInitialization,ctnFinalization,ctnEndPoint,ctnBeginBlock])
|
|
then begin
|
|
if Node.Desc=ctnInterface then
|
|
Add('implementation');
|
|
Add('initialization');
|
|
Add('finalization');
|
|
end;
|
|
end;
|
|
|
|
ctnInitialization:
|
|
if (NodeBehind=nil)
|
|
or (NodeBehind.Desc in [ctnInitialization,ctnFinalization,ctnEndPoint,ctnBeginBlock])
|
|
then begin
|
|
Add('finalization');
|
|
Add('begin');
|
|
end;
|
|
|
|
ctnProcedure:
|
|
begin
|
|
Add('begin');
|
|
Add('type');
|
|
Add('var');
|
|
Add('const');
|
|
Add('label');
|
|
Add('procedure');
|
|
Add('function');
|
|
if not (ilcfDontAllowProcedures in CurrentIdentifierList.ContextFlags)
|
|
and (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags)
|
|
then begin
|
|
Add('asm');
|
|
end;
|
|
end;
|
|
|
|
ctnProcedureHead:
|
|
begin
|
|
MoveCursorBehindProcName(Node);
|
|
p:=CurPos.StartPos;
|
|
while (p>=1) and (Src[p] in [' ',#9]) do dec(p);
|
|
if CleanPos>=p then
|
|
AddMethodSpecifiers;
|
|
end;
|
|
|
|
ctnVarDefinition:
|
|
if Node.Parent.Desc in [ctnClass,ctnObject,ctnRecordType,ctnObjCCategory,ctnObjCClass]
|
|
+AllClassBaseSections
|
|
then begin
|
|
Add('public');
|
|
Add('private');
|
|
Add('protected');
|
|
Add('published');
|
|
Add('procedure');
|
|
Add('function');
|
|
Add('property');
|
|
if [cmsObjectiveC1,cmsObjectiveC2]*Scanner.CompilerModeSwitches<>[] then
|
|
begin
|
|
Add('required');
|
|
Add('optional');
|
|
end;
|
|
if (Node.Desc=ctnClass) or (Node.Parent.Desc=ctnClass) then begin
|
|
Add('constructor');
|
|
Add('destructor');
|
|
end;
|
|
if (Node.Desc=ctnRecordType) or (Node.Parent.Desc=ctnRecordType) then begin
|
|
Add('case');
|
|
end;
|
|
end
|
|
else
|
|
if Node.Parent.Desc = ctnOnBlock then
|
|
begin
|
|
Add('do');
|
|
end;
|
|
|
|
ctnTypeSection,ctnVarSection,ctnConstSection,ctnLabelSection,ctnResStrSection,
|
|
ctnLibrary,ctnProgram:
|
|
begin
|
|
Add('type');
|
|
Add('const');
|
|
Add('var');
|
|
Add('resourcestring');
|
|
Add('label');
|
|
Add('procedure');
|
|
Add('function');
|
|
Add('property');
|
|
if Node.Desc=ctnLibrary then begin
|
|
Add('initialization');
|
|
Add('finalization');
|
|
Add('begin');
|
|
end;
|
|
end;
|
|
|
|
ctnWithVariable, ctnOnBlock:
|
|
begin
|
|
Add('do');
|
|
end;
|
|
|
|
ctnBeginBlock,ctnWithStatement,ctnOnStatement:
|
|
//ctnInitialization,ctnFinalization: //AllPascalStatements
|
|
begin
|
|
if CurrentIdentifierList.IdentComplIncludeKeywords and
|
|
(GatherContext.Node <> nil) and
|
|
(GatherContext.Node.Desc in [ctnBeginBlock,ctnWithStatement,ctnOnStatement])
|
|
then
|
|
begin
|
|
if not (ilcfDontAllowProcedures in CurrentIdentifierList.ContextFlags)
|
|
and (ilcfStartOfStatement in CurrentIdentifierList.ContextFlags)
|
|
then begin
|
|
Add('asm');
|
|
Add('begin');
|
|
Add('case');
|
|
Add('except');
|
|
Add('finally');
|
|
Add('for');
|
|
Add('goto');
|
|
Add('if');
|
|
Add('raise');
|
|
Add('repeat');
|
|
Add('try');
|
|
Add('until');
|
|
Add('while');
|
|
Add('with');
|
|
end;
|
|
if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags)
|
|
and not (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags)
|
|
and (CurrentIdentifierList.StartBracketLvl = 0)
|
|
then begin
|
|
Add('else');
|
|
Add('end');
|
|
Add('then');
|
|
Add('do');
|
|
Add('downto');
|
|
Add('of');
|
|
Add('on');
|
|
Add('to');
|
|
end;
|
|
if (ilcfStartOfStatement in CurrentIdentifierList.ContextFlags)
|
|
or (CurrentIdentifierList.ContextFlags * [ilcfStartInStatement, ilcfStartOfOperand] = [ilcfStartInStatement, ilcfStartOfOperand])
|
|
then
|
|
begin
|
|
Add('inherited');
|
|
end;
|
|
if (CurrentIdentifierList.ContextFlags * [ilcfIsExpression, ilcfStartInStatement] <> []) then
|
|
begin
|
|
NotStartOfOp := not (ilcfStartOfOperand in CurrentIdentifierList.ContextFlags);
|
|
if not NotStartOfOp then begin
|
|
MoveCursorToAtomPos(CurrentIdentifierList.StartAtomInFront);
|
|
NotStartOfOp := AtomIsNumber or AtomIsRealNumber;
|
|
end;
|
|
if NotStartOfOp then
|
|
begin
|
|
Add('and');
|
|
Add('or');
|
|
Add('xor');
|
|
Add('div');
|
|
Add('in');
|
|
Add('as');
|
|
Add('is');
|
|
Add('mod');
|
|
Add('shl');
|
|
Add('shr');
|
|
end;
|
|
Add('not');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
ctnProperty:
|
|
CheckProperty(Node);
|
|
|
|
end;
|
|
|
|
if NodeInFront<>nil then begin
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords Check NodeInFront=',NodeInFront.DescAsString]);
|
|
{$ENDIF}
|
|
SubNode:=NodeInFront;
|
|
while (SubNode<>nil) and (SubNode.EndPos<=CleanPos) do begin
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords Check NodeInFront SubNode=',SubNode.DescAsString]);
|
|
{$ENDIF}
|
|
if (SubNode.Desc=ctnProcedureHead) then begin
|
|
// e.g. in interface: procedure DoIt; v|
|
|
// procedure head postfix modifiers
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords SubNode.Parent=',SubNode.Parent.DescAsString]);
|
|
{$ENDIF}
|
|
if SubNode.Parent.Desc=ctnProcedure then begin
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords SubNode.Parent.Parent=',SubNode.Parent.Parent.DescAsString]);
|
|
{$ENDIF}
|
|
if SubNode.Parent.Parent.Desc in (AllClasses+AllClassBaseSections) then
|
|
AddMethodSpecifiers
|
|
else
|
|
AddProcSpecifiers;
|
|
end else if SubNode.Parent.Desc=ctnProcedureType then begin
|
|
AddProcTypeSpecifiers;
|
|
end;
|
|
break;
|
|
end;
|
|
SubNode:=SubNode.Parent;
|
|
{$IFDEF VerboseICGatherKeywords}
|
|
if (SubNode<>nil) and (SubNode.EndPos>CleanPos) then
|
|
debugln(['TIdentCompletionTool.GatherContextKeywords EndOfCheck NodeInFront SubNode=',SubNode.DescAsString]);
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
except
|
|
// ignore parser errors
|
|
on E: ECodeToolError do ;
|
|
on E: ELinkScannerError do ;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.InitCollectIdentifiers(
|
|
const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList);
|
|
var
|
|
StartContext: TFindContext;
|
|
begin
|
|
if IdentifierList=nil then IdentifierList:=TIdentifierList.Create;
|
|
CurrentIdentifierList:=IdentifierList;
|
|
CurrentIdentifierList.Clear;
|
|
FLastGatheredIdentParent:=nil;
|
|
FLastGatheredIdentLevel:=0;
|
|
CurrentIdentifierList.StartContextPos:=CursorPos;
|
|
StartContext := CurrentIdentifierList.StartContext;
|
|
StartContext.Tool := Self;
|
|
CurrentIdentifierList.StartContext:=StartContext;
|
|
end;
|
|
|
|
function TIdentCompletionTool.ParseSourceTillCollectionStart(
|
|
const CursorPos: TCodeXYPosition; out CleanCursorPos: integer;
|
|
out CursorNode: TCodeTreeNode; out IdentStartPos, IdentEndPos: integer): boolean;
|
|
var
|
|
StartContext: TFindContext;
|
|
ContextPos: Integer;
|
|
begin
|
|
Result:=false;
|
|
CleanCursorPos:=0;
|
|
CursorNode:=nil;
|
|
IdentStartPos:=0;
|
|
IdentEndPos:=0;
|
|
|
|
// build code tree
|
|
{$IFDEF CTDEBUG}
|
|
DebugLn(['TIdentCompletionTool.ParseSourceTillCollectionStart A CursorPos=',dbgs(CursorPos)]);
|
|
{$ENDIF}
|
|
BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanCursorPos,
|
|
[btSetIgnoreErrorPos]);
|
|
// Return if CleanCursorPos is before Tree.Root.StartNode.
|
|
// For example a comment at the beginning of a unit.
|
|
if (Tree.Root=nil) or (Tree.Root.StartPos>CleanCursorPos) then
|
|
Exit;
|
|
if FindDeepestNodeAtPos(CleanCursorPos,false)=nil then
|
|
begin
|
|
debugln(['TIdentCompletionTool.ParseSourceTillCollectionStart',
|
|
' BuildTreeAndGetCleanPos worked, but no node found.',
|
|
' CursorPos=',dbgs(CursorPos),' CleanCursorPos=',CleanCursorPos,
|
|
' ScannedRange=',dbgs(ScannedRange),
|
|
' Scanner.ScannedRange=',dbgs(Scanner.ScannedRange),
|
|
' IgnoreErrorAfterValid=',IgnoreErrorAfterValid
|
|
]);
|
|
if IgnoreErrorAfterValid then
|
|
debugln([' IgnoreErrorAfter=',dbgs(IgnoreErrorAfter),' IgnoreErrorAfterCleanedPos=',IgnoreErrorAfterCleanedPos,' CleanPosIsAfterIgnorePos=',CleanPosIsAfterIgnorePos(CleanCursorPos)]);
|
|
if CursorPos.Y<=CursorPos.Code.LineCount then
|
|
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)]);
|
|
CursorNode:=Tree.Root;
|
|
if CursorNode=nil then begin
|
|
debugln([' no nodes']);
|
|
end else begin
|
|
while CursorNode.NextBrother<>nil do
|
|
CursorNode:=CursorNode.NextBrother;
|
|
while CursorNode<>nil do begin
|
|
debugln([' Node=',CursorNode.DescAsString,',Start=',CursorNode.StartPos,',End=',CursorNode.EndPos,',Src="...',dbgstr(RightStr(ExtractNode(CursorNode,[]),100)),'"']);
|
|
CursorNode:=CursorNode.LastChild;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// find node at position
|
|
ContextPos:=CleanCursorPos;
|
|
// The context node might be in front of the CleanCursorPos
|
|
// For example: A.|end; In this case the statement ends at the point.
|
|
// Check the atom in front
|
|
ReadPriorAtomSafe(CleanCursorPos);
|
|
if (CurPos.Flag<>cafNone) then begin
|
|
if (CurPos.Flag in [cafPoint,cafRoundBracketOpen,cafEdgedBracketOpen])
|
|
or UpAtomIs('INHERITED') then
|
|
ContextPos:=CurPos.StartPos;
|
|
end;
|
|
|
|
CursorNode:=BuildSubTreeAndFindDeepestNodeAtPos(ContextPos,true);
|
|
if CurrentIdentifierList<>nil then begin
|
|
StartContext:=CurrentIdentifierList.StartContext;
|
|
StartContext.Node:=CursorNode;
|
|
CurrentIdentifierList.StartContext:=StartContext;
|
|
end;
|
|
|
|
// get identifier position
|
|
if CursorPos.Code.LineColIsOutside(CursorPos.Y,CursorPos.X) then begin
|
|
IdentStartPos:=CleanCursorPos;
|
|
IdentEndPos:=CleanCursorPos;
|
|
end else begin
|
|
GetIdentStartEndAtPosition(Src,CleanCursorPos,IdentStartPos,IdentEndPos);
|
|
end;
|
|
//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)]);
|
|
if CursorPos.X>CursorPos.Code.GetLineLength(CursorPos.Y-1)+1 then
|
|
IdentStartPos:=IdentEndPos;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TIdentCompletionTool.FindIdentifierStartPos(
|
|
const CursorPos: TCodeXYPosition): TCodeXYPosition;
|
|
var
|
|
p: integer;
|
|
IdentStartPos, IdentEndPos: integer;
|
|
begin
|
|
CursorPos.Code.LineColToPosition(CursorPos.Y,CursorPos.X,p);
|
|
if p<1 then
|
|
RaiseException(20170421201041,ctsCursorPosOutsideOfCode);
|
|
if CursorPos.X<=CursorPos.Code.GetLineLength(CursorPos.Y-1)+1 then begin
|
|
GetIdentStartEndAtPosition(CursorPos.Code.Source,p,IdentStartPos,IdentEndPos);
|
|
end else begin
|
|
IdentStartPos:=p;
|
|
IdentEndPos:=p;
|
|
end;
|
|
Result:=CursorPos;
|
|
if IdentStartPos>0 then
|
|
dec(Result.X,p-IdentStartPos);
|
|
//DebugLn(['TIdentCompletionTool.FindIdentifierStartPos ',dbgstr(copy(CursorPos.Code.Source,IdentStartPos,20))]);
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.FindCollectionContext(
|
|
Params: TFindDeclarationParams; IdentStartPos: integer;
|
|
CursorNode: TCodeTreeNode; out ExprType: TExpressionType; out
|
|
ContextExprStartPos: LongInt; out StartInSubContext,
|
|
HasInheritedKeyword: Boolean);
|
|
|
|
function GetContextExprStartPos(IdentStartPos: integer;
|
|
ContextNode: TCodeTreeNode): integer;
|
|
begin
|
|
MoveCursorToCleanPos(IdentStartPos);
|
|
ReadPriorAtom;
|
|
HasInheritedKeyword := UpAtomIs('INHERITED');
|
|
if (CurPos.Flag=cafPoint)
|
|
or HasInheritedKeyword then begin
|
|
Result:=FindStartOfTerm(IdentStartPos,NodeTermInType(ContextNode));
|
|
if Result<ContextNode.StartPos then
|
|
Result:=ContextNode.StartPos;
|
|
end else
|
|
Result:=IdentStartPos;
|
|
MoveCursorToCleanPos(Result);
|
|
ReadNextAtom;
|
|
case ContextNode.Desc of
|
|
ctnProperty:
|
|
// check for special property keywords
|
|
if WordIsPropertySpecifier.DoItCaseInsensitive(Src,
|
|
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
|
|
then
|
|
// do not resolve property specifiers
|
|
Result:=IdentStartPos;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
IgnoreCurContext: Boolean;
|
|
GatherContext: TFindContext;
|
|
begin
|
|
GatherContext:=CreateFindContext(Self,CursorNode);
|
|
ExprType := CleanExpressionType;
|
|
|
|
IgnoreCurContext:=false;
|
|
//DebugLn(['TIdentCompletionTool.FindCollectionContext IdentStartPos=',dbgstr(copy(Src,IdentStartPos,20)),' ',CursorNode.DescAsString]);
|
|
ContextExprStartPos:=GetContextExprStartPos(IdentStartPos,CursorNode);
|
|
if GatherContext.Node.Desc=ctnWithVariable then begin
|
|
if GatherContext.Node.PriorBrother<>nil then
|
|
GatherContext.Node:=GatherContext.Node.PriorBrother
|
|
else
|
|
GatherContext.Node:=GatherContext.Node.Parent;
|
|
end
|
|
else if (GatherContext.Node.GetNodeOfType(ctnClassInheritance)<>nil) then
|
|
begin
|
|
while not (GatherContext.Node.Desc in AllClasses) do
|
|
GatherContext.Node:=GatherContext.Node.Parent;
|
|
GatherContext.Node:=GatherContext.Node.Parent;
|
|
IgnoreCurContext:=true;
|
|
end else if GatherContext.Node.Desc=ctnIdentifier then begin
|
|
IgnoreCurContext:=true;
|
|
end;
|
|
|
|
StartInSubContext:=false;
|
|
//DebugLn(['TIdentCompletionTool.FindCollectionContext ContextExprStartPos=',ContextExprStartPos,' "',dbgstr(copy(Src,ContextExprStartPos,20)),'" IdentStartPos="',dbgstr(copy(Src,IdentStartPos,20)),'" Gather=',FindContextToString(GatherContext)]);
|
|
if ContextExprStartPos<IdentStartPos then begin
|
|
MoveCursorToCleanPos(IdentStartPos);
|
|
Params.ContextNode:=CursorNode;
|
|
Params.SetIdentifier(Self,nil,nil);
|
|
Params.Flags:=[fdfExceptionOnNotFound,
|
|
fdfSearchInParentNodes,fdfSearchInAncestors,fdfSearchInHelpers,fdfTypeType];
|
|
if IgnoreCurContext then
|
|
Params.Flags:=Params.Flags+[fdfIgnoreCurContextNode];
|
|
ExprType:=FindExpressionTypeOfTerm(ContextExprStartPos,IdentStartPos,
|
|
Params,false);
|
|
if ExprType.Desc=xtContext then begin
|
|
GatherContext:=ExprType.Context;
|
|
//debugln(['TIdentCompletionTool.FindCollectionContext ',ExprTypeToString(ExprType)]);
|
|
StartInSubContext:=true;
|
|
end else begin
|
|
// for example "string.|"
|
|
GatherContext:=CleanFindContext;
|
|
end;
|
|
end;
|
|
ExprType.Context := GatherContext;
|
|
end;
|
|
|
|
function TIdentCompletionTool.CollectAllContexts(
|
|
Params: TFindDeclarationParams; const FoundContext: TFindContext
|
|
): TIdentifierFoundResult;
|
|
begin
|
|
Result:=ifrProceedSearch;
|
|
if FoundContext.Node=nil then exit;
|
|
//DebugLn(['TIdentCompletionTool.CollectAllContexts ',FoundContext.Node.DescAsString]);
|
|
case FoundContext.Node.Desc of
|
|
ctnProcedure:
|
|
begin
|
|
//DebugLn('TIdentCompletionTool.CollectAllContexts Found Proc CurrentContexts.ProcNameAtom.StartPos=',dbgs(CurrentIdentifierContexts.ProcNameAtom.StartPos));
|
|
if (CurrentIdentifierContexts.ProcName='') then exit;
|
|
FoundContext.Tool.MoveCursorToProcName(FoundContext.Node,true);
|
|
//DebugLn(['TIdentCompletionTool.CollectAllContexts ProcName=',GetIdentifier(@FoundContext.Tool.Src[FoundContext.Tool.CurPos.StartPos])]);
|
|
if FoundContext.Tool.CompareSrcIdentifiers(
|
|
FoundContext.Tool.CurPos.StartPos,
|
|
PChar(CurrentIdentifierContexts.ProcName))
|
|
then begin
|
|
// method without 'overload' hides inherited one
|
|
if not FoundContext.Tool.ProcNodeHasSpecifier(FoundContext.Node, psOVERLOAD) then
|
|
Exclude(Params.Flags, fdfSearchInAncestors);
|
|
end else exit;
|
|
end;
|
|
ctnProperty,ctnGlobalProperty:
|
|
begin
|
|
if (CurrentIdentifierContexts.ProcName='') then exit;
|
|
FoundContext.Tool.MoveCursorToPropName(FoundContext.Node);
|
|
if not FoundContext.Tool.CompareSrcIdentifiers(
|
|
FoundContext.Tool.CurPos.StartPos,
|
|
PChar(CurrentIdentifierContexts.ProcName))
|
|
then exit;
|
|
end;
|
|
ctnVarDefinition:
|
|
begin
|
|
//debugln(['TIdentCompletionTool.CollectAllContexts ',FoundContext.Tool.ExtractNode(FoundContext.Node,[])]);
|
|
if (CurrentIdentifierContexts.ProcName='') then exit;
|
|
if not FoundContext.Tool.CompareSrcIdentifiers(
|
|
FoundContext.Node.StartPos,
|
|
PChar(CurrentIdentifierContexts.ProcName))
|
|
then exit;
|
|
end;
|
|
else
|
|
exit;
|
|
end;
|
|
{$IFDEF VerboseCodeContext}
|
|
DebugLn(['TIdentCompletionTool.CollectAllContexts add ',FoundContext.Node.DescAsString]);
|
|
{$ENDIF}
|
|
AddCollectionContext(FoundContext.Tool,FoundContext.Node);
|
|
end;
|
|
|
|
function TIdentCompletionTool.CollectAttributeConstructors(
|
|
Params: TFindDeclarationParams; const FoundContext: TFindContext
|
|
): TIdentifierFoundResult;
|
|
begin
|
|
Result:=ifrProceedSearch;
|
|
if FoundContext.Node=nil then exit;
|
|
{$IFDEF VerboseCodeContext}
|
|
//DebugLn(['TIdentCompletionTool.CollectAttributeConstructors ',FoundContext.Node.DescAsString]);
|
|
{$ENDIF}
|
|
case FoundContext.Node.Desc of
|
|
ctnProcedure:
|
|
begin
|
|
{$IFDEF VerboseCodeContext}
|
|
//DebugLn('TIdentCompletionTool.CollectAttributeConstructors Found Proc ',FoundContext.Tool.ExtractProcName(FoundContext.Node,[]),' ',FoundContext.Tool.CleanPosToStr(FoundContext.Node.StartPos,true));
|
|
{$ENDIF}
|
|
if (CurrentIdentifierContexts.ProcName='') then exit;
|
|
if FoundContext.Tool.NodeIsConstructor(FoundContext.Node) then begin
|
|
{$IFDEF VerboseCodeContext}
|
|
DebugLn('TIdentCompletionTool.CollectAttributeConstructors Found Constructor ',FoundContext.Tool.ExtractProcName(FoundContext.Node,[]),' ',FoundContext.Tool.CleanPosToStr(FoundContext.Node.StartPos,true));
|
|
{$ENDIF}
|
|
AddCollectionContext(FoundContext.Tool,FoundContext.Node);
|
|
end;
|
|
// ToDo: method without 'overload' hides inherited one
|
|
//if not FoundContext.Tool.ProcNodeHasSpecifier(FoundContext.Node, psOVERLOAD) then
|
|
// Exclude(Params.Flags, fdfSearchInAncestors);
|
|
end;
|
|
else
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.AddCollectionContext(Tool: TFindDeclarationTool;
|
|
Node: TCodeTreeNode);
|
|
begin
|
|
if CurrentIdentifierContexts=nil then
|
|
CurrentIdentifierContexts:=TCodeContextInfo.Create;
|
|
CurrentIdentifierContexts.Add(CreateExpressionType(xtContext,xtNone,
|
|
CreateFindContext(Tool,Node)));
|
|
//DebugLn('TIdentCompletionTool.AddCollectionContext ',Node.DescAsString,' ',ExtractNode(Node,[]));
|
|
end;
|
|
|
|
function TIdentCompletionTool.CheckCursorInCompilerDirective(CursorPos: TCodeXYPosition
|
|
): boolean;
|
|
var
|
|
Line: String;
|
|
p: Integer;
|
|
EndPos: Integer;
|
|
InnerStart: Integer;
|
|
Directive: String;
|
|
ms: TCompilerModeSwitch;
|
|
cm: TCompilerMode;
|
|
OptimizerSwitch: TOptimizerSwitch;
|
|
SrcType: TCodeTreeNodeDesc;
|
|
Compiler: TPascalCompiler;
|
|
begin
|
|
Result:=false;
|
|
Line:=CursorPos.Code.GetLine(CursorPos.Y-1,false);
|
|
p:=1;
|
|
while p<=length(Line) do begin
|
|
p:=FindNextCompilerDirective(Line,p,Scanner.NestedComments);
|
|
if p>length(Line) then exit;
|
|
EndPos:=FindCommentEnd(Line,p,Scanner.NestedComments);
|
|
if (CursorPos.X>p) and (CursorPos.X<EndPos) then begin
|
|
// in a directive
|
|
Result:=true;
|
|
InnerStart:=p;
|
|
if Line[InnerStart]='{' then
|
|
inc(InnerStart,2)
|
|
else
|
|
inc(InnerStart,3);
|
|
//debugln(['TIdentCompletionTool.IsInCompilerDirective InnerStart=',InnerStart,' X=',CursorPos.X]);
|
|
SrcType:=GetSourceType;
|
|
Compiler:=Scanner.PascalCompiler;
|
|
if (InnerStart=CursorPos.X)
|
|
or ((CursorPos.X>=InnerStart) and (InnerStart<=length(Line))
|
|
and (CursorPos.X<=InnerStart+GetIdentLen(@Line[InnerStart])))
|
|
then begin
|
|
// at start of directive
|
|
// see fpc/compiler/scandir.pas (incomplete list, e.g. Define is missing there)
|
|
AddKeyWord('A1');
|
|
AddKeyWord('A2');
|
|
AddKeyWord('A4');
|
|
AddKeyWord('A8');
|
|
AddKeyWord('Align');
|
|
AddKeyWord('AlignAssertions');
|
|
AddKeyWord('AppID');
|
|
AddKeyWord('AppName');
|
|
AddKeyWord('AppType');
|
|
AddKeyWord('AsmMode');
|
|
AddKeyWord('Assertions');
|
|
AddKeyWord('BitPacking');
|
|
AddKeyWord('BoolEval');
|
|
AddKeyWord('Calling');
|
|
AddKeyWord('CheckLowAddrLoads');
|
|
AddKeyWord('CheckPointer');
|
|
AddKeyWord('CodeAlign');
|
|
AddKeyWord('Codepage');
|
|
AddKeyWord('COperators');
|
|
AddKeyWord('Copyright');
|
|
AddKeyWord('D');
|
|
AddKeyWord('DebugInfo');
|
|
AddKeyWord('Define');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('DefinitionInfo');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('DenyPackageUnit');
|
|
if (Compiler=pcDelphi) and (SrcType=ctnPackage) then
|
|
AddKeyWord('DesignOnly');
|
|
AddKeyWord('Description');
|
|
if (Compiler=pcPas2js) then begin
|
|
AddKeyWord('DispatchField');
|
|
AddKeyWord('DispatchStrField');
|
|
end;
|
|
AddKeyWord('ElIfC');
|
|
AddKeyWord('Else');
|
|
AddKeyWord('ElseC');
|
|
AddKeyWord('ElseIf');
|
|
AddKeyWord('EndC');
|
|
AddKeyWord('EndIf');
|
|
AddKeyWord('EndRegion');
|
|
AddKeyWord('Error');
|
|
AddKeyWord('ErrorC');
|
|
AddKeyWord('ExtendedSyntax');
|
|
if (Compiler=pcDelphi) and (SrcType in [ctnProgram,ctnLibrary,ctnPackage]) then
|
|
AddKeyWord('Extension');
|
|
AddKeyWord('ExternalSym');
|
|
AddKeyWord('F');
|
|
AddKeyWord('Fatal');
|
|
AddKeyWord('FPUType');
|
|
AddKeyWord('FrameworkPath');
|
|
AddKeyWord('Goto');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('HighCharUnicode');
|
|
AddKeyWord('Hint');
|
|
AddKeyWord('Hints');
|
|
AddKeyWord('HPPEmit');
|
|
AddKeyWord('HugeCode');
|
|
AddKeyWord('HugePointerArithmetikNormalization');
|
|
AddKeyWord('HugePointerComparisonNormalization');
|
|
AddKeyWord('HugePointerNormalization');
|
|
AddKeyWord('IEEEErrors');
|
|
AddKeyWord('IfC');
|
|
AddKeyWord('IfDef');
|
|
AddKeyWord('IfEnd');
|
|
AddKeyWord('IfNDef');
|
|
AddKeyWord('IfOpt');
|
|
AddKeyWord('ImageBase');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('ImplicitBuild');
|
|
AddKeyWord('ImplicitExceptions');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('ImportedData');
|
|
AddKeyWord('Include');
|
|
AddKeyWord('IncludePath');
|
|
AddKeyWord('Info');
|
|
AddKeyWord('Inline');
|
|
AddKeyWord('Interfaces');
|
|
AddKeyWord('IOChecks');
|
|
AddKeyWord('L');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('LegacyIfEnd');
|
|
AddKeyWord('LibExport');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('LibPrefix');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('LibPostfix');
|
|
AddKeyWord('LibraryPath');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('LibVersion');
|
|
AddKeyWord('Link');
|
|
AddKeyWord('LinkFramework');
|
|
AddKeyWord('LinkLib');
|
|
AddKeyWord('LocalSymbols');
|
|
AddKeyWord('LongStrings');
|
|
AddKeyWord('M');
|
|
AddKeyWord('Macro');
|
|
AddKeyWord('MaxFPURegisters');
|
|
AddKeyWord('MaxStackSize');
|
|
AddKeyWord('Memory');
|
|
AddKeyWord('Message');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('MethodInfo');
|
|
AddKeyWord('MinEnumSize');
|
|
AddKeyWord('MinFPConstPrec');
|
|
AddKeyWord('MMX');
|
|
AddKeyWord('Mode');
|
|
AddKeyWord('ModeSwitch');
|
|
AddKeyWord('NameSpace');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('NoInclude');
|
|
AddKeyWord('Note');
|
|
AddKeyWord('Notes');
|
|
AddKeyWord('ObjectChecks');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('ObjExportAll');
|
|
AddKeyWord('ObjectPath');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('ObjTypeName');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('OldTypeLayout');
|
|
AddKeyWord('OpenStrings');
|
|
AddKeyWord('Optimization');
|
|
AddKeyWord('Output_Format');
|
|
AddKeyWord('OV');
|
|
AddKeyWord('OverflowChecks');
|
|
AddKeyWord('PackEnum');
|
|
AddKeyWord('PackRecords');
|
|
AddKeyWord('PackSet');
|
|
AddKeyWord('PIC');
|
|
AddKeyWord('PointerMath');
|
|
AddKeyWord('Pop');
|
|
AddKeyWord('Profile');
|
|
AddKeyWord('Push');
|
|
AddKeyWord('R');
|
|
AddKeyWord('RangeChecks');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('RealCompatibility');
|
|
AddKeyWord('ReferenceInfo');
|
|
AddKeyWord('Region');
|
|
AddKeyWord('Resource');
|
|
AddKeyWord('RTTI');
|
|
if (Compiler=pcDelphi) and (SrcType=ctnPackage) then
|
|
AddKeyWord('RunOnly');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('SafeDivide');
|
|
AddKeyWord('SafeFPUExceptions');
|
|
AddKeyWord('Saturation');
|
|
AddKeyWord('ScopedEnums');
|
|
AddKeyWord('ScreenName');
|
|
AddKeyWord('SetC');
|
|
AddKeyWord('SetPEFlags');
|
|
AddKeyWord('SetPEOptFlags');
|
|
AddKeyWord('SetPEOSVersion');
|
|
AddKeyWord('SetPESubSysVersion');
|
|
AddKeyWord('SetPEUserVersion');
|
|
AddKeyWord('SmartLink');
|
|
AddKeyWord('StackFrames');
|
|
AddKeyWord('Stop');
|
|
AddKeyWord('StringChecks');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('StrongLinkTypes');
|
|
AddKeyWord('Syscall');
|
|
AddKeyWord('TargetSwitch');
|
|
AddKeyWord('ThreadName');
|
|
AddKeyWord('TypedAddress');
|
|
AddKeyWord('TypeInfo');
|
|
AddKeyWord('GetTypeKind');
|
|
AddKeyWord('UnDef');
|
|
AddKeyWord('UnitPath');
|
|
AddKeyWord('VarParaCopyOutCheck');
|
|
AddKeyWord('VarPropSetter');
|
|
AddKeyWord('VarStringChecks');
|
|
AddKeyWord('Wait');
|
|
AddKeyWord('Warn');
|
|
AddKeyWord('Warning');
|
|
AddKeyWord('Warnings');
|
|
AddKeyWord('WeakPackageUnit');
|
|
AddKeyWord('WriteableConst'); // unusual spelling in fpc
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('ExtendedCompatibility');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('ExtendedSyntax');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('ExternalSym');
|
|
if Compiler=pcDelphi then
|
|
AddKeyWord('ExcessPrecision');
|
|
AddKeyWord('Z1');
|
|
AddKeyWord('Z2');
|
|
AddKeyWord('Z4');
|
|
AddKeyWord('ZeroBasedStrings');
|
|
end else if InnerStart<=length(Line) then begin
|
|
// in parameter of directive
|
|
Directive:=lowercase(GetIdentifier(@Line[InnerStart]));
|
|
if (Directive='ifdef')
|
|
or (Directive='ifndef')
|
|
or (Directive='if')
|
|
or (Directive='elseif')
|
|
or (Directive='ifc')
|
|
then begin
|
|
AddCompilerDirectiveMacros(Directive);
|
|
end else if Directive='modeswitch' then begin
|
|
for ms:=low(TCompilerModeSwitch) to high(TCompilerModeSwitch) do
|
|
AddKeyWord(lowercase(CompilerModeSwitchNames[ms]));
|
|
end else if Directive='mode' then begin
|
|
for cm:=low(TCompilerMode) to high(TCompilerMode) do
|
|
AddKeyWord(lowercase(CompilerModeNames[cm]));
|
|
end else if Directive='warn' then begin
|
|
AddKeyWord('constructing_abstract');
|
|
AddKeyWord('implicit_variants');
|
|
AddKeyWord('no_retval');
|
|
AddKeyWord('symbol_deprecated');
|
|
AddKeyWord('symbol_experimental');
|
|
AddKeyWord('symbol_library');
|
|
AddKeyWord('symbol_platform');
|
|
AddKeyWord('symbol_unimplemented');
|
|
AddKeyWord('unit_deprecated');
|
|
AddKeyWord('unit_experimental');
|
|
AddKeyWord('unit_library');
|
|
AddKeyWord('unit_platform');
|
|
AddKeyWord('unit_unimplemented');
|
|
AddKeyWord('zero_nil_compat');
|
|
AddKeyWord('implicit_string_cast');
|
|
AddKeyWord('implicit_variants');
|
|
AddKeyWord('no_retval');
|
|
AddKeyWord('symbol_deprecated');
|
|
AddKeyWord('symbol_experimental');
|
|
AddKeyWord('symbol_library');
|
|
AddKeyWord('symbol_platform');
|
|
AddKeyWord('symbol_unimplemented');
|
|
AddKeyWord('unit_deprecated');
|
|
AddKeyWord('unit_experimental');
|
|
AddKeyWord('unit_library');
|
|
AddKeyWord('unit_platform');
|
|
AddKeyWord('unit_unimplemented');
|
|
AddKeyWord('zero_nil_compat');
|
|
AddKeyWord('implicit_string_cast');
|
|
AddKeyWord('implicit_string_cast_loss');
|
|
AddKeyWord('explicit_string_cast');
|
|
AddKeyWord('explicit_string_cast_loss');
|
|
AddKeyWord('cvt_narrowing_string_lost');
|
|
end else if (Directive='i') or (Directive='include') then begin
|
|
AddKeyWord('Date');
|
|
AddKeyWord('FPCTarget');
|
|
AddKeyWord('FPCTargetOS');
|
|
AddKeyWord('FPCTargetCPU');
|
|
AddKeyWord('FPCVersion');
|
|
AddKeyWord('Time');
|
|
AddKeyWord('CurrentRoutine'); // since FPC 3.1+
|
|
AddKeyWord('Line'); // since FPC 3.1+
|
|
end else if (Directive='codepage') then begin
|
|
// see fpcsrc/compiler/widestr.pas
|
|
AddKeyWord('UTF8');
|
|
AddKeyWord('cp1250');
|
|
AddKeyWord('cp1251');
|
|
AddKeyWord('cp1252');
|
|
AddKeyWord('cp1253');
|
|
AddKeyWord('cp1254');
|
|
AddKeyWord('cp1255');
|
|
AddKeyWord('cp1256');
|
|
AddKeyWord('cp1257');
|
|
AddKeyWord('cp1258');
|
|
AddKeyWord('cp437');
|
|
AddKeyWord('cp646');
|
|
AddKeyWord('cp850');
|
|
AddKeyWord('cp852');
|
|
AddKeyWord('cp856');
|
|
AddKeyWord('cp866');
|
|
AddKeyWord('cp874');
|
|
AddKeyWord('cp8859_1');
|
|
AddKeyWord('cp8859_2');
|
|
AddKeyWord('cp8859_5');
|
|
end else if Directive='interfaces' then begin
|
|
AddKeyWord('COM');
|
|
AddKeyWord('CORBA');
|
|
end else if Directive='optimization' then begin
|
|
for OptimizerSwitch in TOptimizerSwitch do
|
|
AddKeyWord(OptimizerSwitchStr[OptimizerSwitch]);
|
|
end;
|
|
end;
|
|
exit;
|
|
end;
|
|
p:=EndPos;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.AddCompilerDirectiveMacros(Directive: string);
|
|
var
|
|
Macros: TStringToStringTree;
|
|
StrItem: PStringToStringItem;
|
|
CodeBufs: TAVLTree;
|
|
AVLNode: TAVLTreeNode;
|
|
|
|
procedure Add(e: TExpressionEvaluator);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to e.Count-1 do
|
|
Macros[e.Names(i)]:=e.Values(i);
|
|
end;
|
|
|
|
procedure AddExprWords(CodeBuf: TCodeBuffer);
|
|
var
|
|
CurSrc: String;
|
|
p: Integer;
|
|
sp: PChar;
|
|
NamePos: PChar;
|
|
EndP: PChar;
|
|
CurName: String;
|
|
begin
|
|
p:=1;
|
|
CurSrc:=CodeBuf.Source;
|
|
while p<=length(CurSrc) do begin
|
|
p:=FindNextCompilerDirective(CurSrc,p,Scanner.NestedComments);
|
|
if p>length(CurSrc) then break;
|
|
sp:=@CurSrc[p];
|
|
p:=FindCommentEnd(CurSrc,p,Scanner.NestedComments);
|
|
// skip comment start
|
|
if sp^='{' then inc(sp,2)
|
|
else if sp^='(' then inc(sp,3);
|
|
if not IsIdentStartChar[sp^] then break;
|
|
NamePos:=sp;
|
|
inc(sp,GetIdentLen(NamePos));
|
|
if sp^=#0 then break;
|
|
if (CompareIdentifiers(NamePos,'ifdef')=0)
|
|
or (CompareIdentifiers(NamePos,'ifndef')=0)
|
|
or (CompareIdentifiers(NamePos,'if')=0)
|
|
or (CompareIdentifiers(NamePos,'ifc')=0)
|
|
or (CompareIdentifiers(NamePos,'elseif')=0)
|
|
or (CompareIdentifiers(NamePos,'elifc')=0)
|
|
or (CompareIdentifiers(NamePos,'define')=0)
|
|
or (CompareIdentifiers(NamePos,'unde')=0)
|
|
or (CompareIdentifiers(NamePos,'setc')=0)
|
|
then begin
|
|
// add all identifiers in directive
|
|
if p>length(CurSrc) then
|
|
EndP:=PChar(CurSrc)+length(CurSrc)
|
|
else
|
|
EndP:=@CurSrc[p];
|
|
while (sp<EndP) do begin
|
|
if IsIdentStartChar[sp^] then begin
|
|
CurName:=GetIdentifier(sp);
|
|
if (CompareIdentifiers(sp,'defined')<>0)
|
|
and (CompareIdentifiers(sp,'undefined')<>0) then begin
|
|
if not Macros.Contains(CurName) then begin
|
|
Macros[CurName]:='';
|
|
end;
|
|
end;
|
|
inc(sp,length(CurName));
|
|
end else begin
|
|
inc(sp);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
CodeBufs:=nil;
|
|
Macros:=TStringToStringTree.Create(false);
|
|
try
|
|
Add(Scanner.InitialValues);
|
|
Add(Scanner.Values);
|
|
if (Directive='if') or (Directive='elseif')
|
|
or (Directive='ifc') or (Directive='elifc') then begin
|
|
AddCompilerFunction('defined','','boolean');
|
|
AddCompilerFunction('undefined','','boolean');
|
|
end;
|
|
|
|
// add all words of all directives in unit
|
|
CodeBufs:=Scanner.CreateTreeOfSourceCodes;
|
|
AVLNode:=CodeBufs.FindLowest;
|
|
while AVLNode<>nil do begin
|
|
AddExprWords(TCodeBuffer(AVLNode.Data));
|
|
AVLNode:=CodeBufs.FindSuccessor(AVLNode);
|
|
end;
|
|
|
|
for StrItem in Macros do
|
|
AddKeyWord(StrItem^.Name);
|
|
finally
|
|
CodeBufs.Free;
|
|
Macros.Free;
|
|
end;
|
|
end;
|
|
|
|
function TIdentCompletionTool.GatherAvailableUnitNames(const CursorPos: TCodeXYPosition;
|
|
var IdentifierList: TIdentifierList): Boolean;
|
|
begin
|
|
Result:=false;
|
|
|
|
try
|
|
InitCollectIdentifiers(CursorPos, IdentifierList);
|
|
|
|
GatherUnitNames;
|
|
Result:=true;
|
|
|
|
finally
|
|
CurrentIdentifierList:=nil;
|
|
end;
|
|
end;
|
|
|
|
function TIdentCompletionTool.GatherIdentifiers(
|
|
const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList
|
|
): boolean;
|
|
var
|
|
CleanCursorPos, IdentStartPos, IdentEndPos: integer;
|
|
CursorNode: TCodeTreeNode;
|
|
Params: TFindDeclarationParams;
|
|
GatherContext: TFindContext;
|
|
ContextExprStartPos: Integer;
|
|
StartInSubContext: Boolean;
|
|
StartPosOfVariable: LongInt;
|
|
CursorContext: TFindContext;
|
|
IdentStartXY: TCodeXYPosition;
|
|
InFrontOfDirective, HasInheritedKeyword: Boolean;
|
|
ExprType: TExpressionType;
|
|
IdentifierPath: string;
|
|
|
|
procedure CheckProcedureDeclarationContext;
|
|
var
|
|
Node: TCodeTreeNode;
|
|
Can: Boolean;
|
|
begin
|
|
//DebugLn(['CheckProcedureDeclarationContext ',CursorNode.DescAsString]);
|
|
Node:=CursorNode;
|
|
Can:=false;
|
|
if (Node.Parent<>nil)
|
|
and (Node.Parent.Desc in AllClassSections)
|
|
and (Node.Desc=ctnVarDefinition)
|
|
and (CurrentIdentifierList.StartAtomBehind.Flag<>cafColon) then begin
|
|
{ cursor is at a class variable definition without type
|
|
for example:
|
|
|
|
public
|
|
MouseM|
|
|
end;
|
|
}
|
|
Can:=true;
|
|
end
|
|
else if (((Node.Desc=ctnProcedure) and (not NodeIsMethodBody(Node)))
|
|
or ((Node.Desc=ctnProcedureHead) and (not NodeIsMethodBody(Node.Parent))))
|
|
and (not (CurrentIdentifierList.StartAtomBehind.Flag
|
|
in [cafEdgedBracketOpen,cafRoundBracketOpen]))
|
|
then begin
|
|
// for example: procedure DoSomething|
|
|
Can:=true;
|
|
end
|
|
else if Node.Desc in (AllClassBaseSections+AllSourceTypes
|
|
+[ctnInterface,ctnImplementation])
|
|
then begin
|
|
//DebugLn(['TIdentCompletionTool.CheckProcedureDeclarationContext ilcfCanProcDeclaration']);
|
|
Can:=true;
|
|
end;
|
|
if Can then
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfCanProcDeclaration];
|
|
end;
|
|
|
|
begin
|
|
Result:=false;
|
|
|
|
ActivateGlobalWriteLock;
|
|
try
|
|
try
|
|
InitCollectIdentifiers(CursorPos,IdentifierList);
|
|
IdentStartXY:=FindIdentifierStartPos(CursorPos);
|
|
if CheckCursorInCompilerDirective(IdentStartXY) then exit(true);
|
|
|
|
if not ParseSourceTillCollectionStart(IdentStartXY,CleanCursorPos,CursorNode,
|
|
IdentStartPos,IdentEndPos) then
|
|
Exit;
|
|
Params:=TFindDeclarationParams.Create(Self,CursorNode);
|
|
try
|
|
if CleanCursorPos=0 then ;
|
|
if IdentStartPos>0 then begin
|
|
MoveCursorToCleanPos(IdentStartPos);
|
|
ReadNextAtom;
|
|
CurrentIdentifierList.StartAtom:=CurPos;
|
|
end;
|
|
|
|
MoveCursorToCleanPos(IdentStartPos);
|
|
ReadPriorAtom;
|
|
IdentifierPath := '';
|
|
while CurPos.Flag = cafPoint do
|
|
begin
|
|
ReadPriorAtom;
|
|
if CurPos.Flag <> cafWord then
|
|
Break;
|
|
IdentifierPath := GetUpAtom + '.' + IdentifierPath;
|
|
ReadPriorAtom;
|
|
end;
|
|
|
|
// find context
|
|
GatherContext:=CreateFindContext(Self,CursorNode);
|
|
{$IFDEF CTDEBUG}
|
|
DebugLn('TIdentCompletionTool.GatherIdentifiers B',
|
|
' CleanCursorPos=',CleanPosToStr(CleanCursorPos),
|
|
' IdentStartPos=',CleanPosToStr(IdentStartPos),' IdentEndPos=',CleanPosToStr(IdentEndPos),
|
|
' Ident=',copy(Src,IdentStartPos,IdentEndPos-IdentStartPos),
|
|
' GatherContext=',FindContextToString(GatherContext));
|
|
{$ENDIF}
|
|
CurrentIdentifierList.NewMemberVisibility:=GetClassVisibility(CursorNode);
|
|
if CursorNode.Desc in [ctnUsesSection,ctnUseUnit,ctnUseUnitNamespace,ctnUseUnitClearName] then begin
|
|
GatherUnitNames(IdentifierPath);
|
|
MoveCursorToCleanPos(IdentEndPos);
|
|
ReadNextAtom;
|
|
if (CurPos.Flag=cafWord) and (not UpAtomIs('IN')) then begin
|
|
// add comma
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfNeedsEndComma];
|
|
end;
|
|
end else if (CursorNode.Desc in AllSourceTypes)
|
|
and (PositionsInSameLine(Src,CursorNode.StartPos,IdentStartPos)) then begin
|
|
GatherSourceNames(GatherContext);
|
|
end else begin
|
|
FindCollectionContext(Params,IdentStartPos,CursorNode,
|
|
ExprType,ContextExprStartPos,StartInSubContext,
|
|
HasInheritedKeyword);
|
|
//debugln(['TIdentCompletionTool.GatherIdentifiers FindCollectionContext ',ExprTypeToString(ExprType)]);
|
|
|
|
GatherContext := ExprType.Context;
|
|
// find class and ancestors if existing (needed for protected identifiers)
|
|
if (GatherContext.Tool = Self) or HasInheritedKeyword then
|
|
begin
|
|
FindContextClassAndAncestorsAndExtendedClassOfHelper(IdentStartXY, FICTClassAndAncestorsAndExtClassOfHelper);
|
|
end;
|
|
|
|
// check for incomplete context
|
|
|
|
// context bracket level
|
|
CurrentIdentifierList.StartBracketLvl:=
|
|
GetBracketLvl(Src,CursorNode.StartPos,IdentStartPos,
|
|
Scanner.NestedComments);
|
|
if CursorNode.Desc in AllPascalStatements then begin
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfStartInStatement];
|
|
end;
|
|
|
|
// context in front of
|
|
StartPosOfVariable:=FindStartOfTerm(IdentStartPos,NodeTermInType(CursorNode));
|
|
if StartPosOfVariable>0 then begin
|
|
if StartPosOfVariable=IdentStartPos then begin
|
|
// cursor is at start of an operand
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfStartOfOperand];
|
|
end else begin
|
|
MoveCursorToCleanPos(IdentStartPos);
|
|
ReadPriorAtom;
|
|
if CurPos.Flag=cafPoint then
|
|
// cursor is behind a point
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfStartIsSubIdent];
|
|
end;
|
|
MoveCursorToCleanPos(StartPosOfVariable);
|
|
ReadPriorAtom;
|
|
CurrentIdentifierList.StartAtomInFront:=CurPos;
|
|
if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags)
|
|
then begin
|
|
// check if LValue
|
|
if (CurPos.Flag in [cafSemicolon,cafEnd,cafColon])
|
|
or UpAtomIs('BEGIN')
|
|
or UpAtomIs('TRY') or UpAtomIs('FINALLY') or UpAtomIs('EXCEPT')
|
|
or UpAtomIs('FOR') or UpAtomIs('DO') or UpAtomIs('THEN')
|
|
or UpAtomIs('REPEAT') or UpAtomIs('ASM') or UpAtomIs('ELSE')
|
|
then begin
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfStartOfStatement];
|
|
end;
|
|
// check if expression
|
|
if UpAtomIs('IF') or UpAtomIs('CASE') or UpAtomIs('WHILE')
|
|
or UpAtomIs('UNTIL')
|
|
then begin
|
|
// todo: check at start of expression, not only in front of variable
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfIsExpression, ilcfDontAllowProcedures];
|
|
end;
|
|
// check if procedure is allowed
|
|
if (CurPos.Flag in [cafEdgedBracketOpen, cafEqual, cafOtherOperator])
|
|
or ((Scanner.CompilerMode<>cmDelphi) and (CurPos.Flag in [cafAssignment, cafComma, cafRoundBracketOpen])) // "MyEvent := MyProc;" and "o.Test(MyProc)" is supported only in Delphi mode
|
|
then
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfDontAllowProcedures];
|
|
end;
|
|
end;
|
|
// context behind
|
|
if (IdentEndPos<SrcLen) then begin
|
|
MoveCursorToCleanPos(IdentEndPos);
|
|
//debugln(['TIdentCompletionTool.GatherIdentifiers "',dbgstr(Src,IdentStartPos,IdentEndPos-IdentStartPos),'"']);
|
|
InFrontOfDirective:=(CurPos.StartPos<SrcLen) and (Src[CurPos.StartPos]='{')
|
|
and (Src[CurPos.StartPos+1]='$');
|
|
ReadNextAtom;
|
|
|
|
// check end of line
|
|
if (not InFrontOfDirective)
|
|
and (CursorPos.Code.LineColIsOutside(CursorPos.Y,CursorPos.X)
|
|
or (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
|
|
then
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfEndOfLine];
|
|
|
|
CurrentIdentifierList.StartAtomBehind:=CurPos;
|
|
// check if a semicolon is needed or forbidden at the end
|
|
if InFrontOfDirective
|
|
or (CurrentIdentifierList.StartBracketLvl>0)
|
|
or (CurPos.Flag in [cafSemicolon, cafEqual, cafColon, cafComma,
|
|
cafPoint, cafRoundBracketOpen, cafRoundBracketClose,
|
|
cafEdgedBracketOpen, cafEdgedBracketClose])
|
|
or ((CurPos.Flag in [cafWord,cafNone])
|
|
and (UpAtomIs('ELSE')
|
|
or UpAtomIs('THEN')
|
|
or UpAtomIs('DO')
|
|
or UpAtomIs('TO')
|
|
or UpAtomIs('OF')
|
|
or WordIsBinaryOperator.DoItCaseInsensitive(Src,
|
|
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
|
|
then begin
|
|
// do not add semicolon
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfNoEndSemicolon];
|
|
end;
|
|
// check if in statement
|
|
if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags) then
|
|
begin
|
|
// check if a semicolon is needed at the end
|
|
if (not (ilcfNoEndSemicolon in CurrentIdentifierList.ContextFlags))
|
|
then begin
|
|
// check if a semicolon is needed at the end
|
|
if (CurPos.Flag in [cafEnd])
|
|
or WordIsBlockKeyWord.DoItCaseInsensitive(Src,
|
|
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
|
|
or ((CurPos.Flag=cafWord)
|
|
and (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
|
|
then begin
|
|
// add semicolon
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfNeedsEndSemicolon];
|
|
end;
|
|
end;
|
|
end;
|
|
// check missing 'do' after 'with'
|
|
if CurrentIdentifierList.StartUpAtomInFrontIs('WITH')
|
|
and (not CurrentIdentifierList.StartUpAtomBehindIs('DO'))
|
|
and (not CurrentIdentifierList.StartUpAtomBehindIs('AS'))
|
|
and (CurrentIdentifierList.StartBracketLvl=0)
|
|
and (not (CurrentIdentifierList.StartAtomBehind.Flag in
|
|
[cafComma,cafPoint,cafRoundBracketOpen,cafEdgedBracketOpen]))
|
|
and (not CurrentIdentifierList.StartUpAtomBehindIs('^'))
|
|
then
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfNeedsDo];
|
|
end else begin
|
|
// end of source
|
|
CurrentIdentifierList.ContextFlags:=
|
|
CurrentIdentifierList.ContextFlags+[ilcfEndOfLine];
|
|
end;
|
|
|
|
CursorContext:=CreateFindContext(Self,CursorNode);
|
|
GatherContextKeywords(CursorContext, IdentStartPos, Beautifier, GatherContext); //note: coth:
|
|
|
|
// search and gather identifiers in context
|
|
if (GatherContext.Tool<>nil) and (GatherContext.Node<>nil) then begin
|
|
{$IFDEF CTDEBUG}
|
|
DebugLn('TIdentCompletionTool.GatherIdentifiers D CONTEXT: ',
|
|
GatherContext.Tool.MainFilename,
|
|
' ',GatherContext.Node.DescAsString,
|
|
' "',StringToPascalConst(copy(GatherContext.Tool.Src,GatherContext.Node.StartPos,50)),'"');
|
|
{$ENDIF}
|
|
|
|
// gather all identifiers in context
|
|
Params.ContextNode:=GatherContext.Node;
|
|
Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
|
|
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
|
|
if (Params.ContextNode.Desc=ctnInterface) and StartInSubContext then
|
|
Include(Params.Flags,fdfIgnoreUsedUnits);
|
|
if not StartInSubContext then
|
|
Include(Params.Flags,fdfSearchInParentNodes);
|
|
if Params.ContextNode.Desc in AllClasses then
|
|
Exclude(Params.Flags,fdfSearchInParentNodes);
|
|
{$IFDEF CTDEBUG}
|
|
DebugLn('TIdentCompletionTool.GatherIdentifiers F');
|
|
{$ENDIF}
|
|
CurrentIdentifierList.Context:=GatherContext;
|
|
if GatherContext.Node.Desc=ctnIdentifier then
|
|
Params.Flags:=Params.Flags+[fdfIgnoreCurContextNode];
|
|
GatherContext.Tool.FindIdentifierInContext(Params);
|
|
end else
|
|
if ExprType.Desc in xtAllTypeHelperTypes then
|
|
begin
|
|
// gather all identifiers in cursor context for basic types (strings etc.)
|
|
Params.ContextNode:=CursorNode;
|
|
Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
|
|
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
|
|
CurrentIdentifierList.Context:=CursorContext;
|
|
FindIdentifierInBasicTypeHelpers(ExprType.Desc, Params);
|
|
end;
|
|
|
|
// check for procedure/method declaration context
|
|
CheckProcedureDeclarationContext;
|
|
|
|
// add useful identifiers
|
|
{$IFDEF CTDEBUG}
|
|
DebugLn('TIdentCompletionTool.GatherIdentifiers G');
|
|
{$ENDIF}
|
|
GatherUsefulIdentifiers(IdentStartPos,CursorContext,GatherContext);
|
|
end;
|
|
|
|
Result:=true;
|
|
finally
|
|
FreeListOfPFindContext(FICTClassAndAncestorsAndExtClassOfHelper);
|
|
FreeAndNil(FIDCTFoundPublicProperties);
|
|
Params.Free;
|
|
ClearIgnoreErrorAfter;
|
|
end;
|
|
finally
|
|
GatherUserIdentifiers(CurrentIdentifierList.ContextFlags);
|
|
end;
|
|
finally
|
|
DeactivateGlobalWriteLock;
|
|
CurrentIdentifierList:=nil;
|
|
end;
|
|
{$IFDEF CTDEBUG}
|
|
DebugLn(['TIdentCompletionTool.GatherIdentifiers END ']);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TIdentCompletionTool.FindCodeContext(const CursorPos: TCodeXYPosition;
|
|
out CodeContexts: TCodeContextInfo): boolean;
|
|
var
|
|
CleanCursorPos: integer;
|
|
CursorNode: TCodeTreeNode;
|
|
Params: TFindDeclarationParams;
|
|
|
|
procedure AddPredefinedProcs(CurrentContexts: TCodeContextInfo;
|
|
ProcNameAtom: TAtomPosition);
|
|
|
|
procedure AddCompilerProc(const AProcName: string;
|
|
const Params: string; const ResultType: string = '');
|
|
var
|
|
i: LongInt;
|
|
Item: TCodeContextInfoItem;
|
|
begin
|
|
if CompareIdentifiers(PChar(AProcName),@Src[ProcNameAtom.StartPos])<>0
|
|
then exit;
|
|
i:=CurrentContexts.AddCompilerProc;
|
|
Item:=CurrentContexts[i];
|
|
Item.ProcName:=AProcName;
|
|
Item.ResultType:=ResultType;
|
|
Item.Params:=TStringList.Create;
|
|
Item.Params.Delimiter:=';';
|
|
Item.Params.StrictDelimiter:=true;
|
|
Item.Params.DelimitedText:=Params;
|
|
end;
|
|
|
|
var
|
|
IsPointedSystem: Boolean = False;
|
|
FPCFullVersion: LongInt;
|
|
begin
|
|
MoveCursorToAtomPos(ProcNameAtom);
|
|
ReadPriorAtom;
|
|
if (CurPos.Flag = cafPoint) then
|
|
begin
|
|
ReadPriorAtom;
|
|
IsPointedSystem := UpAtomIs('SYSTEM');
|
|
end;
|
|
if (CurPos.Flag in [cafEnd,cafSemicolon,cafEqual,cafComma,cafColon,
|
|
cafRoundBracketOpen,cafEdgedBracketOpen,cafAssignment,cafOtherOperator])
|
|
or IsPointedSystem
|
|
or UpAtomIs('BEGIN')
|
|
or UpAtomIs('TRY') or UpAtomIs('FINALLY') or UpAtomIs('EXCEPT')
|
|
or UpAtomIs('ASM')
|
|
or UpAtomIs('REPEAT') or UpAtomIs('UNTIL') or UpAtomIs('WHILE') or UpAtomIs('DO')
|
|
or UpAtomIs('IF') or UpAtomIs('THEN') or UpAtomIs('ELSE')
|
|
then begin
|
|
// see fpc/compiler/psystem.pp
|
|
FPCFullVersion:=StrToIntDef(Scanner.Values['FPC_FULLVERSION'],0);
|
|
AddCompilerProc('Assert','Condition:Boolean;const Message:String');
|
|
AddCompilerProc('Assigned','P:Pointer','Boolean');
|
|
AddCompilerProc('Addr','var X','Pointer');
|
|
AddCompilerProc('BitSizeOf','Identifier','Integer');
|
|
AddCompilerProc('Concat','S1:String;S2:String[...;Sn:String]', 'String');
|
|
if FPCFullVersion>=30100 then // FromPosition and Count parameters are optional
|
|
begin
|
|
AddCompilerProc('Concat','A1:Array[;...An:Array]', 'Array');
|
|
AddCompilerProc('Copy','const S:string[;FromPosition,Count:Integer]', 'string');
|
|
AddCompilerProc('Copy','const A:array[;FromPosition,Count:Integer]', 'string');
|
|
end else
|
|
begin
|
|
AddCompilerProc('Copy','const S:string;FromPosition,Count:Integer', 'string');
|
|
AddCompilerProc('Copy','const A:array;FromPosition,Count:Integer', 'string');
|
|
end;
|
|
AddCompilerProc('Dec','var X:Ordinal;N:Integer=1');
|
|
AddCompilerProc('Default','T:Type','const');
|
|
AddCompilerProc('Dispose','var X:Pointer');
|
|
AddCompilerProc('Exclude','var S:Set;X:Ordinal');
|
|
AddCompilerProc('Exit','ResultValue:Ordinal=Result');
|
|
AddCompilerProc('Finalize','var X');
|
|
AddCompilerProc('get_frame','','Pointer');
|
|
AddCompilerProc('High','Arg:TypeOrVariable','Ordinal');
|
|
if FPCFullVersion>=30100 then //Delete and Insert are available as intrinsic since FPC 3.1
|
|
begin
|
|
AddCompilerProc('Delete','var S:string;Index,Count:Integer');
|
|
AddCompilerProc('Delete','var A:array;Index,Count:Integer');
|
|
AddCompilerProc('Insert','const Source:string;var Dest:string;Index:Integer');
|
|
AddCompilerProc('Insert','Item; var A:array;Index:Integer');
|
|
end;
|
|
AddCompilerProc('Inc','var X:Ordinal;N:Integer=1');
|
|
AddCompilerProc('Include','var S:Set;X:Ordinal');
|
|
AddCompilerProc('Initialize','var X');
|
|
AddCompilerProc('Length','S:String','Integer');
|
|
AddCompilerProc('Length','A:Array','Integer');
|
|
AddCompilerProc('Low','Arg:TypeOrVariable','Ordinal');
|
|
AddCompilerProc('New','var X:Pointer');
|
|
AddCompilerProc('Ofs','var X','LongInt');
|
|
AddCompilerProc('Ord','X:Ordinal', 'Integer');
|
|
AddCompilerProc('Pack','A:Array;N:Integer;var A:Array');
|
|
AddCompilerProc('Pred','X:Ordinal', 'Ordinal');
|
|
AddCompilerProc('Read','');
|
|
AddCompilerProc('ReadLn','');
|
|
AddCompilerProc('ReadStr','S:String;var Args:Arguments');
|
|
AddCompilerProc('Seg','var X','LongInt');
|
|
AddCompilerProc('SetLength','var S:String;NewLength:Integer');
|
|
AddCompilerProc('SetLength','var A:Array;NewLength:Integer');
|
|
if Scanner.Values.IsDefined('FPC_HAS_CPSTRING') then begin
|
|
AddCompilerProc('SetString','out S:RawByteString;Buf:PAnsiChar;Len:SizeInt');
|
|
AddCompilerProc('SetString','out S:AnsiString;Buf:PAnsiChar;Len:SizeInt');
|
|
AddCompilerProc('SetString','out S:AnsiString;Buf:PWideChar;Len:SizeInt');
|
|
AddCompilerProc('SetString','out S:ShortString;Buf:PChar;Len:SizeInt');
|
|
AddCompilerProc('SetString','out S:UnicodeString;Buf:PUnicodeChar;Len:SizeInt');
|
|
AddCompilerProc('SetString','out S:UnicodeString;Buf:PChar;Len:SizeInt');
|
|
AddCompilerProc('SetString','out S:WideString;Buf:PWideChar;Len:SizeInt');
|
|
AddCompilerProc('SetString','out S:WideString;Buf:PChar;Len:SizeInt');
|
|
end;
|
|
AddCompilerProc('SizeOf','Identifier','Integer');
|
|
AddCompilerProc('Slice','var A:Array;Count:Integer','Array');
|
|
AddCompilerProc('Str','const X[:Width[:Decimals]];var S:String');
|
|
AddCompilerProc('Succ','X:Ordinal', 'Ordinal');
|
|
AddCompilerProc('TypeInfo','Identifier', 'Pointer');
|
|
AddCompilerProc('GetTypeKind','Identifier', 'TTypeKind');
|
|
AddCompilerProc('IsManagedType','Identifier', 'Boolean');
|
|
AddCompilerProc('IsConstValue','const Value', 'Boolean');
|
|
AddCompilerProc('TypeOf','Identifier', 'Pointer');
|
|
AddCompilerProc('Val','S:String;var V;var Code:Integer');
|
|
AddCompilerProc('Unaligned','var X','var');
|
|
AddCompilerProc('Unpack','A:Array;var A:Array;N:Integer');
|
|
AddCompilerProc('Write','Args:Arguments');
|
|
AddCompilerProc('WriteLn','Args:Arguments');
|
|
AddCompilerProc('WriteStr','var S:String;Args:Arguments');
|
|
end;
|
|
end;
|
|
|
|
function CheckContextIsParameter(var Ok: boolean): boolean;
|
|
// returns true, on error or context is parameter
|
|
var
|
|
VarNameAtom, ProcNameAtom: TAtomPosition;
|
|
ParameterIndex, StartPos: integer;
|
|
ContextExprStartPos: LongInt;
|
|
StartInSubContext, HasInheritedKeyword, IsAttributeParams: Boolean;
|
|
ExprType: TExpressionType;
|
|
AttribParamNode: TCodeTreeNode;
|
|
begin
|
|
Result:=false;
|
|
IsAttributeParams:=false;
|
|
if (CursorNode.Desc=ctnParamsRound)
|
|
and (CursorNode.Parent.Desc=ctnAttribParam) then begin
|
|
IsAttributeParams:=true;
|
|
AttribParamNode:=CursorNode.Parent;
|
|
StartPos:=AttribParamNode.StartPos;
|
|
end else if CursorNode.GetNodeOfTypes([ctnBeginBlock,ctnInitialization,ctnFinalization])<>nil
|
|
then begin
|
|
StartPos:=CursorNode.StartPos;
|
|
end else begin
|
|
// not in a begin..end block
|
|
DebugLn(['TIdentCompletionTool.FindCodeContext.CheckContextIsParameter not in a begin block "',CursorNode.DescAsString,'"']);
|
|
exit;
|
|
end;
|
|
// check if cursor is in a parameter list
|
|
if not CheckParameterSyntax(StartPos, CleanCursorPos,
|
|
VarNameAtom, ProcNameAtom, ParameterIndex)
|
|
then begin
|
|
if VarNameAtom.StartPos=0 then ;
|
|
DebugLn(['TIdentCompletionTool.FindCodeContext.CheckContextIsParameter not in a parameter list']);
|
|
exit;
|
|
end;
|
|
//DebugLn('CheckContextIsParameter Variable=',GetAtom(VarNameAtom),' Proc=',GetAtom(ProcNameAtom),' ParameterIndex=',dbgs(ParameterIndex));
|
|
|
|
// it is a parameter -> create context
|
|
Result:=true;
|
|
if CurrentIdentifierContexts=nil then
|
|
CurrentIdentifierContexts:=TCodeContextInfo.Create;
|
|
CurrentIdentifierContexts.Tool:=Self;
|
|
CurrentIdentifierContexts.ParameterIndex:=ParameterIndex+1;
|
|
CurrentIdentifierContexts.ProcNameAtom:=ProcNameAtom;
|
|
CurrentIdentifierContexts.ProcName:=GetAtom(ProcNameAtom);
|
|
|
|
MoveCursorToAtomPos(ProcNameAtom);
|
|
ReadNextAtom; // read opening bracket
|
|
CurrentIdentifierContexts.StartPos:=CurPos.EndPos;
|
|
// read closing bracket
|
|
if ReadTilBracketClose(false) then
|
|
CurrentIdentifierContexts.EndPos:=CurPos.StartPos
|
|
else
|
|
CurrentIdentifierContexts.EndPos:=SrcLen+1;
|
|
|
|
if IsAttributeParams then begin
|
|
debugln(['CheckContextIsParameter AttribParamNode={',ExtractNode(AttribParamNode,[]),'}']);
|
|
Params.Flags:=fdfDefaultForExpressions+[fdfSkipClassForward];
|
|
Params.Identifier:=@Src[ProcNameAtom.StartPos];
|
|
Params.ContextNode:=AttribParamNode.FirstChild;
|
|
ExprType:=FindExpressionTypeOfTerm(AttribParamNode.StartPos,ProcNameAtom.EndPos,Params,false);
|
|
{$IFDEF VerboseCodeContext}
|
|
debugln(['CheckContextIsParameter Attribute: ',ExprTypeToString(ExprType)]);
|
|
{$ENDIF}
|
|
if (ExprType.Context.Node = nil) or (ExprType.Context.Tool = nil) then
|
|
exit;
|
|
CurrentIdentifierList.Context:=ExprType.Context;
|
|
Params.ContextNode:=ExprType.Context.Node;
|
|
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
|
|
Params.SetIdentifier(Self,'*',@CollectAttributeConstructors);
|
|
ExprType.Context.Tool.FindIdentifierInContext(Params);
|
|
end else begin
|
|
AddPredefinedProcs(CurrentIdentifierContexts,ProcNameAtom);
|
|
FindCollectionContext(Params,ProcNameAtom.StartPos,CursorNode,
|
|
ExprType,ContextExprStartPos,StartInSubContext,
|
|
HasInheritedKeyword);
|
|
|
|
if ContextExprStartPos=0 then ;
|
|
{$IFDEF VerboseCodeContext}
|
|
DebugLn(['CheckContextIsParameter StartInSubContext=',StartInSubContext,' ',ExprTypeToString(ExprType),' "',copy(ExprType.Context.Tool.Src,ExprType.Context.Node.StartPos-20,25),'"']);
|
|
{$ENDIF}
|
|
if (ExprType.Context.Node = nil) or (ExprType.Context.Tool = nil) then
|
|
begin
|
|
if ExprType.Desc in xtAllIdentPredefinedTypes then
|
|
begin
|
|
ExprType.Context.Node := CursorNode;
|
|
ExprType.Context.Tool := Self;
|
|
end else
|
|
Exit;
|
|
end;
|
|
|
|
Params.ContextNode:=ExprType.Context.Node;
|
|
if IsAttributeParams then begin
|
|
Params.SetIdentifier(Self,'*',@CollectAttributeConstructors);
|
|
end else begin
|
|
Params.SetIdentifier(Self,@Src[ProcNameAtom.StartPos],@CollectAllContexts);
|
|
end;
|
|
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable,fdfSearchInHelpers];
|
|
if not StartInSubContext then
|
|
Include(Params.Flags,fdfSearchInParentNodes);
|
|
CurrentIdentifierList.Context:=ExprType.Context;
|
|
{$IFDEF VerboseCodeContext}
|
|
DebugLn('CheckContextIsParameter searching procedures, properties and variables ...');
|
|
{$ENDIF}
|
|
if ExprType.Desc in xtAllTypeHelperTypes then
|
|
ExprType.Context.Tool.FindIdentifierInBasicTypeHelpers(ExprType.Desc, Params)
|
|
else
|
|
ExprType.Context.Tool.FindIdentifierInContext(Params);
|
|
end;
|
|
|
|
// gather declarations of all parameter lists
|
|
{$IFDEF VerboseCodeContext}
|
|
DebugLn('CheckContextIsParameter END');
|
|
{$ENDIF}
|
|
Ok:=true;
|
|
end;
|
|
|
|
var
|
|
IdentifierList: TIdentifierList;
|
|
IdentStartPos, IdentEndPos: integer;
|
|
begin
|
|
Result:=false;
|
|
CodeContexts:=nil;
|
|
IdentifierList:=nil;
|
|
CurrentIdentifierContexts:=CodeContexts;
|
|
|
|
ActivateGlobalWriteLock;
|
|
try
|
|
InitCollectIdentifiers(CursorPos,IdentifierList);
|
|
if not ParseSourceTillCollectionStart(CursorPos,CleanCursorPos,CursorNode,
|
|
IdentStartPos,IdentEndPos) then
|
|
Exit;
|
|
Params:=TFindDeclarationParams.Create(Self, CursorNode);
|
|
try
|
|
if IdentStartPos=0 then ;
|
|
if IdentEndPos=0 then ;
|
|
|
|
// find class and ancestors if existing (needed for protected identifiers)
|
|
FindContextClassAndAncestorsAndExtendedClassOfHelper(CursorPos,
|
|
FICTClassAndAncestorsAndExtClassOfHelper);
|
|
|
|
if CursorNode<>nil then begin
|
|
if not CheckContextIsParameter(Result) then begin
|
|
DebugLn(['TIdentCompletionTool.FindCodeContext cursor not at parameter']);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if CurrentIdentifierContexts=nil then begin
|
|
// create default
|
|
AddCollectionContext(Self,CursorNode);
|
|
end;
|
|
|
|
Result:=true;
|
|
finally
|
|
if Result then begin
|
|
CodeContexts:=CurrentIdentifierContexts;
|
|
CurrentIdentifierContexts:=nil;
|
|
end else begin
|
|
FreeAndNil(CurrentIdentifierContexts);
|
|
end;
|
|
FreeListOfPFindContext(FICTClassAndAncestorsAndExtClassOfHelper);
|
|
FreeAndNil(FIDCTFoundPublicProperties);
|
|
Params.Free;
|
|
ClearIgnoreErrorAfter;
|
|
end;
|
|
finally
|
|
DeactivateGlobalWriteLock;
|
|
FreeAndNil(CurrentIdentifierList);
|
|
end;
|
|
end;
|
|
|
|
function TIdentCompletionTool.FindAbstractMethods(
|
|
const CursorPos: TCodeXYPosition; out ListOfPCodeXYPosition: TFPList;
|
|
SkipAbstractsInStartClass: boolean): boolean;
|
|
const
|
|
ProcAttr = [phpWithoutClassKeyword,phpWithHasDefaultValues];
|
|
FlagIsAbstract = 0;
|
|
FlagIsImplemented = 1;
|
|
var
|
|
ImplementedInterfaces: TStringToPointerTree;
|
|
SearchedAncestors: TAVLTree;
|
|
Procs: TAVLTree; // tree of TCodeTreeNodeExtension
|
|
|
|
procedure AddProc(ATool: TFindDeclarationTool; ProcNode: TCodeTreeNode;
|
|
IsAbstract: boolean);
|
|
var
|
|
ProcText: String;
|
|
AVLNode: TAVLTreeNode;
|
|
NodeExt: TCodeTreeNodeExtension;
|
|
begin
|
|
ProcText:=ATool.ExtractProcHead(ProcNode,ProcAttr);
|
|
AVLNode:=FindCodeTreeNodeExtAVLNode(Procs,ProcText);
|
|
if AVLNode<>nil then begin
|
|
// known proc
|
|
NodeExt:=TCodeTreeNodeExtension(AVLNode.Data);
|
|
//debugln(['AddProc "',ProcText,'" WasImplemented=',NodeExt.Flags=1,' IsAbstract=',IsAbstract]);
|
|
if NodeExt.Flags=FlagIsImplemented then
|
|
exit; // already implemented
|
|
if IsAbstract then
|
|
exit; // already abstract
|
|
NodeExt.Flags:=FlagIsImplemented;
|
|
NodeExt.Node:=ProcNode;
|
|
NodeExt.Data:=ATool;
|
|
end else begin
|
|
// new method
|
|
//debugln(['AddProc "',ProcText,'" New IsAbstract=',IsAbstract]);
|
|
NodeExt:=TCodeTreeNodeExtension.Create;
|
|
NodeExt.Node:=ProcNode;
|
|
NodeExt.Data:=ATool;
|
|
NodeExt.Txt:=ProcText;
|
|
if IsAbstract then
|
|
NodeExt.Flags:=FlagIsAbstract
|
|
else
|
|
NodeExt.Flags:=FlagIsImplemented;
|
|
Procs.Add(NodeExt);
|
|
end;
|
|
end;
|
|
|
|
procedure CollectImplements(ClassNode: TCodeTreeNode);
|
|
var
|
|
Node: TCodeTreeNode;
|
|
StopNode: TCodeTreeNode;
|
|
InterfaceName: String;
|
|
begin
|
|
Node:=ClassNode.FirstChild;
|
|
StopNode:=ClassNode.NextSkipChilds;
|
|
while Node<>StopNode do begin
|
|
if Node.Desc in AllClassBaseSections then begin
|
|
Node:=Node.Next;
|
|
continue;
|
|
end else if Node.Desc=ctnProperty then begin
|
|
if PropertyHasSpecifier(Node,'IMPLEMENTS',false) then begin
|
|
ReadNextAtom;
|
|
while AtomIsIdentifier do begin
|
|
InterfaceName:=GetAtom;
|
|
ReadNextAtom;
|
|
if CurPos.Flag=cafPoint then begin
|
|
ReadNextAtom;
|
|
AtomIsIdentifierE(true);
|
|
InterfaceName+='.'+GetAtom;
|
|
ReadNextAtom;
|
|
end;
|
|
//debugln(['CollectImplements ',InterfaceName]);
|
|
ImplementedInterfaces[InterfaceName]:=Node;
|
|
if CurPos.Flag<>cafComma then break;
|
|
ReadNextAtom;
|
|
end;
|
|
end;
|
|
end else if Node.Desc=ctnProcedure then begin
|
|
if ProcNodeHasSpecifier(Node,psABSTRACT) then begin
|
|
if not SkipAbstractsInStartClass then
|
|
AddProc(Self,Node,true);
|
|
end else begin
|
|
AddProc(Self,Node,false);
|
|
end;
|
|
end;
|
|
Node:=Node.NextSkipChilds;
|
|
end;
|
|
end;
|
|
|
|
procedure CollectAncestors(aTool: TFindDeclarationTool;
|
|
ClassNode: TCodeTreeNode; IsStartClass: boolean); forward;
|
|
|
|
procedure CollectAncestor(ATool: TFindDeclarationTool;
|
|
InheritanceNode: TCodeTreeNode; SearchedAncestors: TAVLTree;
|
|
IsStartClass: boolean);
|
|
var
|
|
Params: TFindDeclarationParams;
|
|
ClassNode: TCodeTreeNode;
|
|
StopNode: TCodeTreeNode;
|
|
Node: TCodeTreeNode;
|
|
IsInterface: Boolean;
|
|
begin
|
|
//debugln(['CollectAncestor Ancestor=',ATool.ExtractIdentifierWithPoints(InheritanceNode.StartPos,false)]);
|
|
Params:=TFindDeclarationParams.Create;
|
|
try
|
|
if not ATool.FindAncestorOfClassInheritance(InheritanceNode,Params,true)
|
|
then exit;
|
|
ATool:=Params.NewCodeTool;
|
|
ClassNode:=Params.NewNode;
|
|
if SearchedAncestors.Find(ClassNode)<>nil then
|
|
exit; // already searched
|
|
SearchedAncestors.Add(ClassNode);
|
|
// check all procs of this ancestor
|
|
StopNode:=ClassNode.NextSkipChilds;
|
|
Node:=ClassNode.FirstChild;
|
|
IsInterface:=ClassNode.Desc in AllClassInterfaces;
|
|
if IsInterface and (not IsStartClass) then
|
|
exit;
|
|
while Node<>StopNode do begin
|
|
if Node.Desc in AllClassBaseSections then begin
|
|
Node:=Node.Next;
|
|
continue;
|
|
end else if Node.Desc=ctnProcedure then begin
|
|
if IsInterface
|
|
or ATool.ProcNodeHasSpecifier(Node,psABSTRACT) then
|
|
AddProc(ATool,Node,true)
|
|
else
|
|
AddProc(ATool,Node,false);
|
|
end;
|
|
Node:=Node.NextSkipChilds;
|
|
end;
|
|
CollectAncestors(ATool,ClassNode,false);
|
|
finally
|
|
Params.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure CollectAncestors(aTool: TFindDeclarationTool;
|
|
ClassNode: TCodeTreeNode; IsStartClass: boolean);
|
|
var
|
|
InheritanceNode: TCodeTreeNode;
|
|
AncestorName: String;
|
|
Node: TCodeTreeNode;
|
|
begin
|
|
//debugln(['CollectAncestors of Class=',aTool.ExtractClassName(ClassNode,false)]);
|
|
InheritanceNode:=ATool.FindInheritanceNode(ClassNode);
|
|
if (InheritanceNode=nil)
|
|
or (InheritanceNode.FirstChild=nil) then begin
|
|
// no ancestors
|
|
exit;
|
|
end;
|
|
Node:=InheritanceNode.FirstChild;
|
|
while Node<>nil do begin
|
|
InheritanceNode:=Node;
|
|
Node:=Node.NextBrother;
|
|
if InheritanceNode.Desc=ctnIdentifier then begin
|
|
if IsStartClass then begin
|
|
AncestorName:=ATool.ExtractIdentifierWithPoints(InheritanceNode.StartPos,true);
|
|
if ImplementedInterfaces.FindNode(AncestorName)<>nil then
|
|
continue;
|
|
end;
|
|
CollectAncestor(ATool,InheritanceNode,SearchedAncestors,IsStartClass);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
CleanCursorPos: integer;
|
|
CursorNode: TCodeTreeNode;
|
|
ClassNode: TCodeTreeNode;
|
|
AVLNode: TAVLTreeNode;
|
|
NodeExt: TCodeTreeNodeExtension;
|
|
ProcXYPos: TCodeXYPosition;
|
|
ATool: TFindDeclarationTool;
|
|
begin
|
|
Result:=false;
|
|
ListOfPCodeXYPosition:=nil;
|
|
ImplementedInterfaces:=nil;
|
|
Procs:=nil;
|
|
SearchedAncestors:=nil;
|
|
try
|
|
BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanCursorPos,
|
|
[btSetIgnoreErrorPos]);
|
|
|
|
// find node at position
|
|
CursorNode:=BuildSubTreeAndFindDeepestNodeAtPos(CleanCursorPos,true);
|
|
|
|
// if cursor is on type node, find class node
|
|
if CursorNode.Desc=ctnTypeDefinition then
|
|
CursorNode:=CursorNode.FirstChild
|
|
else if CursorNode.Desc=ctnGenericType then
|
|
CursorNode:=CursorNode.LastChild
|
|
else
|
|
CursorNode:=FindClassOrInterfaceNode(CursorNode);
|
|
|
|
if (CursorNode=nil)
|
|
or (not (CursorNode.Desc in AllClassObjects))
|
|
or ((CursorNode.SubDesc and ctnsForwardDeclaration)>0) then begin
|
|
MoveCursorToCleanPos(CleanCursorPos);
|
|
RaiseException(20170421201053,'TIdentCompletionTool.FindAbstractMethods cursor is not in a class');
|
|
end;
|
|
ClassNode:=CursorNode;
|
|
|
|
// search class for implemented interfaces and method
|
|
ImplementedInterfaces:=TStringToPointerTree.Create(false);
|
|
Procs:=TAVLTree.Create(@CompareCodeTreeNodeExt);
|
|
CollectImplements(ClassNode);
|
|
|
|
// search all ancestors
|
|
SearchedAncestors:=TAVLTree.Create;
|
|
SearchedAncestors.Add(ClassNode);
|
|
CollectAncestors(Self,ClassNode,true);
|
|
|
|
// AddCodePosition for each abstract method
|
|
AVLNode:=Procs.FindLowest;
|
|
while AVLNode<>nil do begin
|
|
NodeExt:=TCodeTreeNodeExtension(AVLNode.Data);
|
|
if NodeExt.Flags=FlagIsAbstract then begin
|
|
ATool:=TFindDeclarationTool(NodeExt.Data);
|
|
if not ATool.CleanPosToCaret(NodeExt.Node.StartPos,ProcXYPos) then
|
|
raise Exception.Create('TIdentCompletionTool.FindAbstractMethods inconsistency');
|
|
AddCodePosition(ListOfPCodeXYPosition,ProcXYPos);
|
|
end;
|
|
AVLNode:=Procs.FindSuccessor(AVLNode);
|
|
end;
|
|
|
|
Result:=true;
|
|
finally
|
|
DisposeAVLTree(Procs);
|
|
ImplementedInterfaces.Free;
|
|
SearchedAncestors.Free;
|
|
end;
|
|
end;
|
|
|
|
function TIdentCompletionTool.GetValuesOfCaseVariable(
|
|
const CursorPos: TCodeXYPosition; List: TStrings; WithTypeDefIfScoped: boolean
|
|
): boolean;
|
|
var
|
|
CleanCursorPos: integer;
|
|
CursorNode: TCodeTreeNode;
|
|
CaseAtom: TAtomPosition;
|
|
Params: TFindDeclarationParams;
|
|
EndPos: LongInt;
|
|
ExprType: TExpressionType;
|
|
Node: TCodeTreeNode;
|
|
Tool: TFindDeclarationTool;
|
|
EnumPrefix: string;
|
|
begin
|
|
Result:=false;
|
|
ActivateGlobalWriteLock;
|
|
Params:=nil;
|
|
try
|
|
BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanCursorPos,
|
|
[btSetIgnoreErrorPos]);
|
|
|
|
// find node at position
|
|
CursorNode:=BuildSubTreeAndFindDeepestNodeAtPos(CleanCursorPos,true);
|
|
|
|
// find keyword case
|
|
MoveCursorToNodeStart(CursorNode);
|
|
CaseAtom:=CleanAtomPosition;
|
|
repeat
|
|
ReadNextAtom;
|
|
if UpAtomIs('CASE') then
|
|
CaseAtom:=CurPos
|
|
until (CurPos.EndPos>SrcLen) or (CurPos.EndPos>CleanCursorPos);
|
|
if CaseAtom.StartPos<1 then begin
|
|
debugln(['TIdentCompletionTool.GetValuesOfCaseVariable "case" not found']);
|
|
exit;
|
|
end;
|
|
|
|
// find case variable
|
|
EndPos:=FindEndOfExpression(CaseAtom.EndPos);
|
|
if EndPos>CleanCursorPos then
|
|
EndPos:=CleanCursorPos;
|
|
//DebugLn(['TIdentCompletionTool.GetValuesOfCaseVariable Expr=',dbgstr(copy(Src,CaseAtom.EndPos,EndPos-CaseAtom.EndPos))]);
|
|
|
|
Params:=TFindDeclarationParams.Create(Self, CursorNode);
|
|
Params.Flags:=fdfDefaultForExpressions+[fdfFunctionResult];
|
|
ExprType:=FindExpressionTypeOfTerm(CaseAtom.EndPos,EndPos,Params,true);
|
|
//DebugLn(['TIdentCompletionTool.GetValuesOfCaseVariable Type=',ExprTypeToString(ExprType)]);
|
|
|
|
if ExprType.Desc=xtContext then begin
|
|
// resolve aliases and properties
|
|
Params.Clear;
|
|
Params.Flags:=fdfDefaultForExpressions;
|
|
ExprType.Context:=ExprType.Context.Tool.FindBaseTypeOfNode(Params,
|
|
ExprType.Context.Node);
|
|
end;
|
|
|
|
case ExprType.Desc of
|
|
|
|
xtBoolean,
|
|
xtByteBool,xtWordBool,xtLongBool,xtQWordBool,
|
|
xtBoolean8,xtBoolean16,xtBoolean32,xtBoolean64:
|
|
begin
|
|
List.Add('True');
|
|
List.Add('False');
|
|
end;
|
|
|
|
xtContext:
|
|
begin
|
|
Node:=ExprType.Context.Node;
|
|
Tool:=ExprType.Context.Tool;
|
|
if Node=nil then exit;
|
|
case Node.Desc of
|
|
|
|
ctnEnumerationType:
|
|
begin
|
|
if WithTypeDefIfScoped
|
|
and (Tool.Scanner.GetDirectiveValueAt(sdScopedEnums, Node.StartPos) = '1') then
|
|
begin
|
|
Tool.MoveCursorToCleanPos(Node.Parent.StartPos);
|
|
Tool.ReadNextAtom;
|
|
EnumPrefix := Tool.GetAtom+'.';
|
|
end else
|
|
EnumPrefix := '';
|
|
|
|
Node:=Node.FirstChild;
|
|
while Node<>nil do begin
|
|
List.Add(EnumPrefix+GetIdentifier(@Tool.Src[Node.StartPos], False));
|
|
Node:=Node.NextBrother;
|
|
end;
|
|
end;
|
|
|
|
else
|
|
debugln(['TIdentCompletionTool.GetValuesOfCaseVariable not an enum: ',Node.DescAsString]);
|
|
exit;
|
|
end;
|
|
end;
|
|
else
|
|
exit;
|
|
end;
|
|
|
|
Result:=true;
|
|
finally
|
|
Params.Free;
|
|
DeactivateGlobalWriteLock;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentCompletionTool.CalcMemSize(Stats: TCTMemStats);
|
|
var
|
|
Node: TAVLTreeNode;
|
|
Ext: TCodeTreeNodeExtension;
|
|
m: PtrUint;
|
|
begin
|
|
inherited CalcMemSize(Stats);
|
|
if FICTClassAndAncestorsAndExtClassOfHelper<>nil then
|
|
Stats.Add('TIdentCompletionTool.ClassAndAncestorsAndExtClassOfHelper',
|
|
FICTClassAndAncestorsAndExtClassOfHelper.Count*(SizeOf(TAVLTreeNode)+SizeOf(TCodeXYPosition)));
|
|
if FIDCTFoundPublicProperties<>nil then
|
|
Stats.Add('TIdentCompletionTool.FoundPublicProperties',
|
|
FIDCTFoundPublicProperties.Count*SizeOf(TAVLTreeNode));
|
|
if FIDTFoundMethods<>nil then begin
|
|
m:=PtrUint(FIDTFoundMethods.Count)*SizeOf(TAVLTreeNode);
|
|
Node:=FIDTFoundMethods.FindLowest;
|
|
while Node<>nil do begin
|
|
Ext:=TCodeTreeNodeExtension(Node.Data);
|
|
inc(m,Ext.CalcMemSize);
|
|
Node:=FIDTFoundMethods.FindSuccessor(Node);
|
|
end;
|
|
STats.Add('TIdentCompletionTool.FoundMethods',m);
|
|
end;
|
|
if CurrentIdentifierList<>nil then
|
|
Stats.Add('TIdentCompletionTool.CurrentIdentifierList',
|
|
CurrentIdentifierList.CalcMemSize);
|
|
if CurrentIdentifierContexts<>nil then
|
|
Stats.Add('TIdentCompletionTool.CurrentContexts',
|
|
CurrentIdentifierContexts.CalcMemSize);
|
|
end;
|
|
|
|
{ TIdentifierListItem }
|
|
|
|
function TIdentifierListItem.GetParamTypeList: string;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if not (iliParamTypeListValid in Flags) then begin
|
|
// Note: if you implement param lists for other than ctnProcedure, check
|
|
// CompareParamList
|
|
ANode:=Node;
|
|
FParamTypeList:='';
|
|
if (ANode<>nil) and (ANode.Desc=ctnProcedure) then begin
|
|
try
|
|
FParamTypeList:=Tool.ExtractProcHead(ANode,
|
|
[phpWithoutClassKeyword,phpWithoutClassName,
|
|
phpWithoutName,phpInUpperCase]);
|
|
//debugln('TIdentifierListItem.GetParamTypeList A ',GetIdentifier(Identifier),' ',Tool.MainFilename,' ',dbgs(CurNode.StartPos));
|
|
except
|
|
on E: ECodeToolError do ; // ignore syntax errors
|
|
end;
|
|
end;
|
|
Include(Flags,iliParamTypeListValid);
|
|
end;
|
|
Result:=FParamTypeList;
|
|
end;
|
|
|
|
function TIdentifierListItem.GetParamNameList: string;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if not (iliParamNameListValid in Flags) then begin
|
|
// Note: if you implement param lists for other than ctnProcedure, check
|
|
// CompareParamList
|
|
ANode:=Node;
|
|
FParamNameList:='';
|
|
if (ANode<>nil) and (ANode.Desc=ctnProcedure) then begin
|
|
try
|
|
FParamNameList:=Tool.ExtractProcHead(ANode,
|
|
[phpWithoutClassKeyword,phpWithoutClassName,
|
|
phpWithoutName,phpInUpperCase,phpWithParameterNames]);
|
|
//debugln('TIdentifierListItem.GetParamNameList A ',GetIdentifier(Identifier),' ',Tool.MainFilename,' ',dbgs(CurNode.StartPos));
|
|
except
|
|
on E: ECodeToolError do ; // ignore syntax errors
|
|
end;
|
|
end;
|
|
Include(Flags,iliParamNameListValid);
|
|
end;
|
|
Result:=FParamNameList;
|
|
end;
|
|
|
|
function TIdentifierListItem.GetNode: TCodeTreeNode;
|
|
begin
|
|
Result:=nil;
|
|
if Tool=nil then
|
|
exit;
|
|
|
|
if (iliNodeValid in Flags)
|
|
and (FToolNodesDeletedStep<>Tool.NodesDeletedChangeStep) then
|
|
Exclude(Flags,iliNodeValid);
|
|
|
|
if (not (iliNodeValid in Flags)) then begin
|
|
if iliNodeHashValid in Flags then begin
|
|
RestoreNode;
|
|
if (iliNodeValid in Flags) then begin
|
|
Result:=FNode;
|
|
end;
|
|
end;
|
|
end else begin
|
|
if FToolNodesDeletedStep=Tool.NodesDeletedChangeStep then begin
|
|
Result:=FNode;
|
|
end else begin
|
|
if not (iliNodeGoneWarned in Flags) then begin
|
|
DebugLn(['TIdentifierListItem.GetNode node ',Identifier,' is gone from ',Tool.MainFilename]);
|
|
Include(Flags,iliNodeGoneWarned);
|
|
end;
|
|
FNode:=nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentifierListItem.SetNode(const AValue: TCodeTreeNode);
|
|
|
|
procedure RaiseToolMissing;
|
|
begin
|
|
raise Exception.Create('TIdentifierListItem.SetNode Node without Tool');
|
|
end;
|
|
|
|
begin
|
|
FNode:=AValue;
|
|
Include(Flags,iliNodeValid);
|
|
Exclude(Flags,iliNodeHashValid);
|
|
if (FNode<>nil) and (Tool=nil) then
|
|
RaiseToolMissing;
|
|
if (Tool<>nil) then
|
|
FToolNodesDeletedStep:=Tool.NodesDeletedChangeStep;
|
|
if (FNode<>nil) then
|
|
StoreNodeHash;
|
|
end;
|
|
|
|
procedure TIdentifierListItem.SetParamTypeList(const AValue: string);
|
|
begin
|
|
FParamTypeList:=AValue;
|
|
Include(Flags,iliParamTypeListValid);
|
|
end;
|
|
|
|
procedure TIdentifierListItem.SetParamNameList(const AValue: string);
|
|
begin
|
|
FParamNameList:=AValue;
|
|
Include(Flags,iliParamNameListValid);
|
|
end;
|
|
|
|
procedure TIdentifierListItem.SetResultType(const AValue: string);
|
|
begin
|
|
FResultType := AValue;
|
|
Include(Flags, iliResultTypeValid);
|
|
end;
|
|
|
|
function TIdentifierListItem.AsString: string;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
WriteStr(Result, Compatibility);
|
|
if HasChilds then
|
|
Result:=Result+' HasChilds'
|
|
else
|
|
Result:=Result+' HasNoChilds';
|
|
Result:=Result+' History='+IntToStr(HistoryIndex);
|
|
Result:=Result+' Ident='+Identifier;
|
|
Result:=Result+' Lvl='+IntToStr(Level);
|
|
if Tool<>nil then
|
|
Result:=Result+' File='+Tool.MainFilename;
|
|
ANode:=Node;
|
|
if ANode<>nil then
|
|
Result:=Result+' Node='+ANode.DescAsString
|
|
+' "'+StringToPascalConst(copy(Tool.Src,ANode.StartPos,50))+'"';
|
|
end;
|
|
|
|
procedure TIdentifierListItem.BeautifyIdentifier(IdentList: TIdentifierList);
|
|
begin
|
|
// can be overridden
|
|
end;
|
|
|
|
function TIdentifierListItem.GetDesc: TCodeTreeNodeDesc;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
ANode:=Node;
|
|
if ANode<>nil then
|
|
Result:=ANode.Desc
|
|
else
|
|
Result:=DefaultDesc;
|
|
end;
|
|
|
|
constructor TIdentifierListItem.Create(
|
|
NewCompatibility: TIdentifierCompatibility; NewHasChilds: boolean;
|
|
NewHistoryIndex: integer; NewIdentifier: PChar; NewLevel: integer;
|
|
NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool;
|
|
NewDefaultDesc: TCodeTreeNodeDesc);
|
|
begin
|
|
Compatibility:=NewCompatibility;
|
|
if NewHasChilds then Include(FLags,iliHasChilds);
|
|
HistoryIndex:=NewHistoryIndex;
|
|
Identifier:=GetIdentifier(NewIdentifier);
|
|
Level:=NewLevel;
|
|
Tool:=NewTool;
|
|
Node:=NewNode;
|
|
DefaultDesc:=NewDefaultDesc;
|
|
BaseExprType:=CleanExpressionType;
|
|
end;
|
|
|
|
function TIdentifierListItem.IsProcNodeWithParams: boolean;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
StartPos: Integer;
|
|
begin
|
|
Result:=(GetDesc=ctnProcedure);
|
|
if not Result then exit;
|
|
if (iliParamNameListValid in Flags) then begin
|
|
StartPos:=1;
|
|
while (StartPos<=length(FParamTypeList))
|
|
and (FParamTypeList[StartPos] in [' ',#9,'(','[']) do
|
|
inc(StartPos);
|
|
if (StartPos<=length(FParamTypeList))
|
|
and (FParamTypeList[StartPos] in [')',']',';']) then
|
|
exit(false)
|
|
else
|
|
exit(true);
|
|
end else if (iliParamTypeListValid in Flags) then begin
|
|
// the type list does not contain names
|
|
// so a () could be empty or (var buf)
|
|
StartPos:=1;
|
|
while (StartPos<=length(FParamTypeList))
|
|
and (FParamTypeList[StartPos] in [' ',#9,'(','[']) do
|
|
inc(StartPos);
|
|
if (StartPos<=length(FParamTypeList))
|
|
and (not (FParamTypeList[StartPos] in [')',']',';'])) then
|
|
exit(true);
|
|
end;
|
|
ANode:=Node;
|
|
Result:=(ANode<>nil) and Tool.ProcNodeHasParamList(ANode);
|
|
end;
|
|
|
|
function TIdentifierListItem.IsPropertyWithParams: boolean;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if not (iliHasParamListValid in Flags) then begin
|
|
Include(Flags,iliHasParamListValid);
|
|
ANode:=Node;
|
|
if (ANode<>nil) and Tool.PropertyNodeHasParamList(ANode) then
|
|
Include(Flags,iliHasParamList)
|
|
else
|
|
Exclude(Flags,iliHasParamList);
|
|
end;
|
|
Result:=iliHasParamList in Flags;
|
|
end;
|
|
|
|
function TIdentifierListItem.IsPropertyReadOnly: boolean;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if not (iliIsReadOnlyValid in Flags) then begin
|
|
Include(Flags,iliIsReadOnlyValid);
|
|
ANode:=Node;
|
|
if (ANode<>nil) and Tool.PropertyHasSpecifier(ANode,'READ',false)
|
|
and not Tool.PropertyHasSpecifier(ANode,'WRITE',false) then
|
|
Include(Flags,iliIsReadOnly)
|
|
else
|
|
Exclude(Flags,iliIsReadOnly);
|
|
end;
|
|
Result:=iliIsReadOnly in Flags;
|
|
end;
|
|
|
|
function TIdentifierListItem.GetHintModifiers: TPascalHintModifiers;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
Result:=[];
|
|
if not (iliHintModifiersValid in Flags) then begin
|
|
Include(Flags,iliHintModifiersValid);
|
|
ANode:=Node;
|
|
if ANode<>nil then begin
|
|
Result:=Tool.GetHintModifiers(ANode);
|
|
if phmDeprecated in Result then Include(Flags,iliIsDeprecated);
|
|
if phmPlatform in Result then Include(Flags,iliIsPlatform);
|
|
if phmLibrary in Result then Include(Flags,iliIsLibrary);
|
|
if phmUnimplemented in Result then Include(Flags,iliIsUnimplemented);
|
|
if phmExperimental in Result then Include(Flags,iliIsExperimental);
|
|
end;
|
|
end else begin
|
|
if iliIsDeprecated in Flags then Include(Result,phmDeprecated);
|
|
if iliIsPlatform in Flags then Include(Result,phmPlatform);
|
|
if iliIsLibrary in Flags then Include(Result,phmLibrary);
|
|
if iliIsUnimplemented in Flags then Include(Result,phmUnimplemented);
|
|
if iliIsExperimental in Flags then Include(Result,phmExperimental);
|
|
end;
|
|
end;
|
|
|
|
function TIdentifierListItem.CheckHasChilds: boolean;
|
|
// returns true if test was successful
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
Result:=false;
|
|
if GetDesc in AllClasses then begin
|
|
Result:=true;
|
|
exit;
|
|
end;
|
|
ANode:=Node;
|
|
if ANode=nil then exit;
|
|
UpdateBaseContext;
|
|
if (BaseExprType.Desc=xtContext)
|
|
and (BaseExprType.Context.Node<>nil)
|
|
and (BaseExprType.Context.Node.Desc in AllClasses)
|
|
then
|
|
Include(Flags,iliHasChilds);
|
|
Result:=true;
|
|
end;
|
|
|
|
function TIdentifierListItem.CanBeAssigned: boolean;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
Result:=false;
|
|
ANode:=Node;
|
|
if (ANode=nil) then exit;
|
|
if (GetDesc=ctnVarDefinition) then
|
|
Result:=true;
|
|
if (ANode.Desc in [ctnProperty,ctnGlobalProperty]) then begin
|
|
if Tool.PropertyHasSpecifier(ANode,'WRITE') then exit(true);
|
|
if Tool.PropNodeIsTypeLess(ANode) then begin
|
|
exit(true);// ToDo: search the real property definition
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentifierListItem.UpdateBaseContext;
|
|
var
|
|
Params: TFindDeclarationParams;
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if (iliBaseExprTypeValid in Flags) then exit;
|
|
Include(Flags,iliBaseExprTypeValid);
|
|
BaseExprType:=CleanExpressionType;
|
|
BaseExprType.Desc:=xtNone;
|
|
ANode:=Node;
|
|
if (ANode<>nil) and (Tool<>nil) then begin
|
|
Tool.ActivateGlobalWriteLock;
|
|
Params:=TFindDeclarationParams.Create(Tool, ANode);
|
|
try
|
|
if ANode.HasParentOfType(ctnGenericType) then exit;
|
|
BaseExprType.Context:=Tool.FindBaseTypeOfNode(Params,ANode);
|
|
if (BaseExprType.Context.Node<>nil) then
|
|
BaseExprType.Desc:=xtContext;
|
|
finally
|
|
Params.Free;
|
|
Tool.DeactivateGlobalWriteLock;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TIdentifierListItem.HasChilds: boolean;
|
|
begin
|
|
Result:=iliHasChilds in Flags;
|
|
end;
|
|
|
|
function TIdentifierListItem.HasIndex: boolean;
|
|
// check if edged bracket can be used []
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if not (iliHasIndexValid in Flags) then begin
|
|
UpdateBaseContext;
|
|
if BaseExprType.Desc in (xtAllStringConvertibles+xtAllWideStringConvertibles)
|
|
then begin
|
|
// strings, widestrings and PChar
|
|
Include(Flags,iliHasIndex);
|
|
end else if (BaseExprType.Desc=xtContext) and (BaseExprType.Context.Node<>nil)
|
|
then begin
|
|
//debugln(['TIdentifierListItem.HasIndex ',BaseExprType.Context.Node.DescAsString]);
|
|
ANode:=BaseExprType.Context.Node;
|
|
case ANode.Desc of
|
|
ctnRangedArrayType,ctnOpenArrayType: Include(Flags,iliHasIndex);
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=iliHasIndex in Flags;
|
|
end;
|
|
|
|
function TIdentifierListItem.IsFunction: boolean;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if not (iliIsFunctionValid in Flags) then
|
|
begin
|
|
ANode := Node;
|
|
if (ANode <> nil) and Tool.NodeIsFunction(ANode) then
|
|
Include(Flags, iliIsFunction);
|
|
Include(Flags, iliIsFunctionValid);
|
|
end;
|
|
Result := iliIsFunction in Flags;
|
|
end;
|
|
|
|
function TIdentifierListItem.IsConstructor: boolean;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if not (iliIsConstructorValid in Flags) then
|
|
begin
|
|
ANode := Node;
|
|
if (ANode <> nil) and Tool.NodeIsConstructor(ANode) then
|
|
Include(Flags, iliIsConstructor);
|
|
Include(Flags, iliIsConstructorValid);
|
|
end;
|
|
Result := iliIsConstructor in Flags;
|
|
end;
|
|
|
|
function TIdentifierListItem.IsDestructor: boolean;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if not (iliIsDestructorValid in Flags) then
|
|
begin
|
|
ANode := Node;
|
|
if (ANode <> nil) and Tool.NodeIsDestructor(ANode) then
|
|
Include(Flags, iliIsDestructor);
|
|
Include(Flags, iliIsDestructorValid);
|
|
end;
|
|
Result := iliIsDestructor in Flags;
|
|
end;
|
|
|
|
function TIdentifierListItem.IsAbstractMethod: boolean;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
begin
|
|
if not (iliIsAbstractMethodValid in Flags) then begin
|
|
ANode:=Node;
|
|
if (ANode<>nil)
|
|
and Tool.ProcNodeHasSpecifier(ANode,psABSTRACT) then
|
|
Include(Flags,iliIsAbstractMethod);
|
|
Include(Flags,iliIsAbstractMethodValid);
|
|
end;
|
|
Result:=iliIsAbstractMethod in Flags;
|
|
end;
|
|
|
|
function TIdentifierListItem.TryIsAbstractMethod: boolean;
|
|
begin
|
|
try
|
|
Result:=IsAbstractMethod;
|
|
except
|
|
Result:=false;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdentifierListItem.Clear;
|
|
begin
|
|
FParamTypeList:='';
|
|
FResultType:='';
|
|
Compatibility:=icompUnknown;
|
|
HistoryIndex:=0;
|
|
Identifier:='';
|
|
Level:=0;
|
|
FNode:=nil;
|
|
Tool:=nil;
|
|
DefaultDesc:=ctnNone;
|
|
Flags:=[];
|
|
BaseExprType:=CleanExpressionType;
|
|
end;
|
|
|
|
procedure TIdentifierListItem.UnbindNode;
|
|
begin
|
|
if FNode=nil then exit;
|
|
StoreNodeHash;
|
|
Exclude(Flags,iliNodeValid);
|
|
FNode:=nil;
|
|
end;
|
|
|
|
procedure TIdentifierListItem.StoreNodeHash;
|
|
begin
|
|
Include(Flags,iliNodeHashValid);
|
|
FNodeStartPos:=FNode.StartPos;
|
|
FNodeDesc:=FNode.Desc;
|
|
FNodeHash:=GetNodeHash(FNode);
|
|
//DebugLn(['TIdentifierListItem.StoreNodeHash ',Identifier,' Pos=',FNodeStartPos,' Hash=',FNodeHash]);
|
|
end;
|
|
|
|
function TIdentifierListItem.RestoreNode: boolean;
|
|
var
|
|
NewNode: TCodeTreeNode;
|
|
NewHash: Cardinal;
|
|
begin
|
|
if not (iliNodeHashValid in Flags) then exit(true);
|
|
//DebugLn(['TIdentifierListItem.RestoreNode ',Identifier]);
|
|
NewNode:=Tool.BuildSubTreeAndFindDeepestNodeAtPos(FNodeStartPos,false);
|
|
Result:=false;
|
|
if (NewNode=nil) or (NewNode.StartPos<>FNodeStartPos)
|
|
or (NewNode.Desc<>FNodeDesc) then begin
|
|
DebugLn(['TIdentifierListItem.RestoreNode not found: ',Identifier]);
|
|
Exclude(Flags,iliNodeHashValid);
|
|
exit;
|
|
end;
|
|
NewHash:=GetNodeHash(NewNode);
|
|
if NewHash<>FNodeHash then begin
|
|
DebugLn(['TIdentifierListItem.RestoreNode hash changed: ',Identifier]);
|
|
Exclude(Flags,iliNodeHashValid);
|
|
exit;
|
|
end;
|
|
//DebugLn(['TIdentifierListItem.RestoreNode Success ',Identifier]);
|
|
Node:=NewNode;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TIdentifierListItem.GetNodeHash(ANode: TCodeTreeNode): Cardinal;
|
|
var
|
|
StartPos: LongInt;
|
|
EndPos: LongInt;
|
|
begin
|
|
case ANode.Desc of
|
|
ctnVarDefinition,ctnConstDefinition,ctnTypeDefinition,ctnGenericType:
|
|
ANode:=Tool.FindDefinitionNameNode(ANode);
|
|
end;
|
|
if ANode<>nil then
|
|
begin
|
|
StartPos:=ANode.StartPos;
|
|
EndPos:=StartPos+20;
|
|
if EndPos>ANode.EndPos then EndPos:=ANode.EndPos;
|
|
Result:=crc32(0, PByte(@Tool.Src[StartPos]), EndPos-StartPos);
|
|
end else
|
|
Result:=0;
|
|
end;
|
|
|
|
function TIdentifierListItem.CompareParamList(CompareItem: TIdentifierListItem
|
|
): integer;
|
|
var
|
|
ANode: TCodeTreeNode;
|
|
CmpNode: TCodeTreeNode;
|
|
begin
|
|
Result:=0;
|
|
if Self=CompareItem then exit;
|
|
ANode:=Node;
|
|
CmpNode:=CompareItem.Node;
|
|
if (ANode=CmpNode) then exit;
|
|
if (ANode=nil) or (CmpNode=nil) then exit;
|
|
if (ANode.Desc<>ctnProcedure) or (CmpNode.Desc<>ctnProcedure) then
|
|
exit;
|
|
{DbgOut('TIdentifierListItem.CompareParamList ',GetIdentifier(Identifier),'=',GetIdentifier(CompareItem.Identifier));
|
|
if Node<>nil then
|
|
DbgOut(' Self=',Tool.MainFilename,' ',dbgs(Node.StartPos));
|
|
if CompareItem.Node<>nil then
|
|
DbgOut(' Other=',CompareItem.Tool.MainFilename,' ',dbgs(CompareItem.Node.StartPos));
|
|
debugln('');}
|
|
Result:=CompareTextIgnoringSpace(ParamTypeList,CompareItem.ParamTypeList,false);
|
|
end;
|
|
|
|
function TIdentifierListItem.CompareParamList(
|
|
CompareItem: TIdentifierListSearchItem): integer;
|
|
begin
|
|
if (ParamTypeList='') and (CompareItem.ParamList='') then
|
|
exit(0);
|
|
Result:=CompareTextIgnoringSpace(ParamTypeList,CompareItem.ParamList,false);
|
|
end;
|
|
|
|
function TIdentifierListItem.CalcMemSize: PtrUInt;
|
|
begin
|
|
Result:=PtrUInt(InstanceSize)
|
|
+MemSizeString(FParamTypeList)
|
|
+SizeOf(FNodeHash)
|
|
+MemSizeString(Identifier);
|
|
end;
|
|
|
|
{ TIdentifierHistoryList }
|
|
|
|
procedure TIdentifierHistoryList.SetCapacity(const AValue: integer);
|
|
begin
|
|
if FCapacity=AValue then exit;
|
|
FCapacity:=AValue;
|
|
if FCapacity<1 then FCapacity:=1;
|
|
while (FItems.Count>0) and (FItems.Count>=FCapacity) do
|
|
FItems.FreeAndDelete(FItems.FindHighest);
|
|
end;
|
|
|
|
function TIdentifierHistoryList.FindItem(NewItem: TIdentifierListItem
|
|
): TAVLTreeNode;
|
|
begin
|
|
if NewItem<>nil then
|
|
Result:=FItems.FindKey(NewItem,@CompareIdentItemWithHistListItem)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
constructor TIdentifierHistoryList.Create;
|
|
begin
|
|
FItems:=TAVLTree.Create(@CompareIdentHistListItem);
|
|
FCapacity:=30;
|
|
end;
|
|
|
|
destructor TIdentifierHistoryList.Destroy;
|
|
begin
|
|
Clear;
|
|
FItems.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIdentifierHistoryList.Clear;
|
|
begin
|
|
FItems.FreeAndClear;
|
|
end;
|
|
|
|
procedure TIdentifierHistoryList.Add(NewItem: TIdentifierListItem);
|
|
var
|
|
OldAVLNode: TAVLTreeNode;
|
|
NewHistItem: TIdentHistListItem;
|
|
AnAVLNode: TAVLTreeNode;
|
|
AdjustIndex: Integer;
|
|
AnHistItem: TIdentHistListItem;
|
|
begin
|
|
if NewItem=nil then exit;
|
|
OldAVLNode:=FindItem(NewItem);
|
|
{$IFDEF ShowHistory}
|
|
DebugLn(['TIdentifierHistoryList.Add Count=',Count,' Found=',OldAVLNode<>nil,
|
|
' ITEM: ',NewItem.AsString]);
|
|
{$ENDIF}
|
|
if OldAVLNode<>nil then begin
|
|
// already in tree
|
|
NewHistItem:=TIdentHistListItem(OldAVLNode.Data);
|
|
if NewHistItem.HistoryIndex=0 then exit;
|
|
// must be moved -> remove it from the tree
|
|
AdjustIndex:=NewHistItem.HistoryIndex;
|
|
FItems.Delete(OldAVLNode);
|
|
end else begin
|
|
// create a new history item
|
|
NewHistItem:=TIdentHistListItem.Create;
|
|
NewHistItem.Identifier:=NewItem.Identifier;
|
|
NewHistItem.NodeDesc:=NewItem.GetDesc;
|
|
NewHistItem.ParamList:=NewItem.ParamTypeList;
|
|
AdjustIndex:=high(AdjustIndex);
|
|
end;
|
|
NewHistItem.HistoryIndex:=0;
|
|
// adjust all other HistoryIndex
|
|
AnAVLNode:=Fitems.FindLowest;
|
|
while AnAVLNode<>nil do begin
|
|
AnHistItem:=TIdentHistListItem(AnAVLNode.Data);
|
|
if AnHistItem.HistoryIndex<AdjustIndex then
|
|
inc(AnHistItem.HistoryIndex);
|
|
AnAVLNode:=FItems.FindSuccessor(AnAVLNode);
|
|
end;
|
|
if (FItems.Count>0) and (FItems.Count>=FCapacity) then
|
|
FItems.FreeAndDelete(FItems.FindHighest);
|
|
FItems.Add(NewHistItem);
|
|
{$IFDEF ShowHistory}
|
|
DebugLn(['TIdentifierHistoryList.Added Count=',Count]);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TIdentifierHistoryList.GetHistoryIndex(AnItem: TIdentifierListItem
|
|
): integer;
|
|
var
|
|
AnAVLNode: TAVLTreeNode;
|
|
begin
|
|
AnAVLNode:=FindItem(AnItem);
|
|
if AnAVLNode=nil then
|
|
Result:=33333333 // a very high value
|
|
else
|
|
Result:=TIdentHistListItem(AnAVLNode.Data).HistoryIndex;
|
|
end;
|
|
|
|
function TIdentifierHistoryList.Count: integer;
|
|
begin
|
|
Result:=FItems.Count;
|
|
end;
|
|
|
|
function TIdentifierHistoryList.CalcMemSize: PtrUInt;
|
|
var
|
|
Node: TAVLTreeNode;
|
|
Item: TIdentHistListItem;
|
|
begin
|
|
Result:=PtrUInt(InstanceSize);
|
|
if FItems<>nil then begin
|
|
{%H-}inc(Result,FItems.Count*SizeOf(TAVLTreeNode));
|
|
Node:=FItems.FindLowest;
|
|
while Node<>nil do begin
|
|
Item:=TIdentHistListItem(Node.Data);
|
|
inc(Result,Item.CalcMemSize);
|
|
Node:=FItems.FindSuccessor(Node);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TCodeContextInfo }
|
|
|
|
function TCodeContextInfo.GetItems(Index: integer): TCodeContextInfoItem;
|
|
begin
|
|
Result:=TCodeContextInfoItem(FItems[Index]);
|
|
end;
|
|
|
|
constructor TCodeContextInfo.Create;
|
|
begin
|
|
FItems:=TFPList.Create;
|
|
end;
|
|
|
|
destructor TCodeContextInfo.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeAndNil(FItems);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TCodeContextInfo.Count: integer;
|
|
begin
|
|
Result:=FItems.Count;
|
|
end;
|
|
|
|
function TCodeContextInfo.Add(const Context: TExpressionType): integer;
|
|
var
|
|
Item: TCodeContextInfoItem;
|
|
begin
|
|
Item:=TCodeContextInfoItem.Create;
|
|
Item.Expr:=Context;
|
|
Result:=FItems.Add(Item);
|
|
end;
|
|
|
|
function TCodeContextInfo.AddCompilerProc: integer;
|
|
var
|
|
Item: TCodeContextInfoItem;
|
|
begin
|
|
Item:=TCodeContextInfoItem.Create;
|
|
Result:=FItems.Add(Item);
|
|
end;
|
|
|
|
procedure TCodeContextInfo.Clear;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to FItems.Count-1 do
|
|
TObject(FItems[i]).Free;
|
|
FItems.Clear;
|
|
end;
|
|
|
|
function TCodeContextInfo.CalcMemSize: PtrUInt;
|
|
begin
|
|
Result:=PtrUInt(InstanceSize)
|
|
+{%H-}PtrUInt(TCodeContextInfoItem)*SizeOf(FItems.Count)
|
|
+MemSizeString(FProcName);
|
|
end;
|
|
|
|
{ TIdentifierListSearchItem }
|
|
|
|
function TIdentifierListSearchItem.CalcMemSize: PtrUInt;
|
|
begin
|
|
Result:=PtrUInt(InstanceSize)
|
|
+MemSizeString(ParamList);
|
|
end;
|
|
|
|
{ TIdentHistListItem }
|
|
|
|
function TIdentHistListItem.CalcMemSize: PtrUInt;
|
|
begin
|
|
Result:=PtrUInt(InstanceSize)
|
|
+MemSizeString(Identifier)
|
|
+MemSizeString(ParamList);
|
|
end;
|
|
|
|
{ TCodeContextInfoItem }
|
|
|
|
destructor TCodeContextInfoItem.Destroy;
|
|
begin
|
|
FreeAndNil(Params);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TCodeContextInfoItem.AsDebugString(WithExpr: boolean): string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=ProcName+'(';
|
|
if Params<>nil then
|
|
for i:=0 to Params.Count-1 do begin
|
|
if i>0 then Result+=',';
|
|
Result+=Params[i];
|
|
end;
|
|
Result+=')';
|
|
if ResultType<>'' then Result+=':'+ResultType;
|
|
if WithExpr then
|
|
Result+=' '+ExprTypeToString(Expr);
|
|
end;
|
|
|
|
end.
|
|
|