{ *************************************************************************** * * * 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 . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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} // new features { $DEFINE DisableIgnoreErrorAfter} uses {$IFDEF MEM_CHECK} MemCheck, {$ENDIF} Classes, SysUtils, FileProcs, CodeTree, CodeAtom, CustomCodeTool, KeywordFuncLists, BasicCodeTools, LinkScanner, AVL_Tree, CodeToolMemManager, DefineTemplates, SourceChanger, FindDeclarationTool, PascalParserTool; type TIdentCompletionTool = class; TIdentifierHistoryList = class; //---------------------------------------------------------------------------- // gathered identifier list TIdentifierCompatibility = ( icompExact, icompCompatible, icompUnknown, icompIncompatible ); TIdentifierCompatibilities = set of TIdentifierCompatibility; TIdentListItemFlag = ( iliHasChilds, iliBaseExprTypeValid, iliIsFunction, iliIsFunctionValid, iliParamListValid ); TIdentListItemFlags = set of TIdentListItemFlag; { TIdentifierListItem } TIdentifierListItem = class private FNext: TIdentifierListItem; FParamList: string; function GetParamList: string; procedure SetParamList(const AValue: string); public Compatibility: TIdentifierCompatibility; HistoryIndex: integer; Identifier: PChar; Level: integer; Node: TCodeTreeNode; Tool: TFindDeclarationTool; DefaultDesc: TCodeTreeNodeDesc; Flags: TIdentListItemFlags; BaseExprType: TExpressionType; function AsString: string; 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 CheckHasChilds: boolean; function CanBeAssigned: boolean; procedure UpdateBaseContext; function HasChilds: boolean; function IsFunction: boolean; procedure Clear; function CompareParamList(CompareItem: TIdentifierListItem): integer; public property ParamList: string read GetParamList write SetParamList; end; TIdentifierListFlag = (ilfFilteredListNeedsUpdate); TIdentifierListFlags = set of TIdentifierListFlag; TIdentifierListContextFlag = ( ilcfStartInStatement, // context starts in statements. e.g. between begin..end ilcfStartIsLValue, // position is start of one statement. e.g. 'A:=' ilcfNeedsEndSemicolon,// after context a semicolon is needed. e.g. 'A end' ilcfIsExpression // is expression part of statement. e.g. 'if expr' ); TIdentifierListContextFlags = set of TIdentifierListContextFlag; TIdentifierList = class private FContext: TFindContext; FContextFlags: TIdentifierListContextFlags; 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 TIdentHistListItem sorted for identifiers FIdentSearchItem: TIdentifierListItem; FPrefix: string; FStartContext: TFindContext; procedure SetContextFlags(const AValue: TIdentifierListContextFlags); procedure SetHistory(const AValue: TIdentifierHistoryList); 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 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; public property Context: TFindContext read FContext write FContext; property ContextFlags: TIdentifierListContextFlags read FContextFlags write SetContextFlags; property FilteredItems[Index: integer]: TIdentifierListItem read GetFilteredItems; property History: TIdentifierHistoryList read FHistory write SetHistory; property Prefix: string read FPrefix write SetPrefix; property StartAtomInFront: TAtomPosition read FStartAtomInFront write FStartAtomInFront; property StartAtomBehind: TAtomPosition read FStartAtomBehind write FStartAtomBehind; property StartBracketLvl: integer read FStartBracketLvl write FStartBracketLvl; property StartContext: TFindContext read FStartContext write FStartContext; property StartContextPos: TCodeXYPosition read FStartContextPos write FStartContextPos; end; //---------------------------------------------------------------------------- // history list TIdentHistListItem = class public Identifier: string; NodeDesc: TCodeTreeNodeDesc; ParamList: string; HistoryIndex: integer; end; 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; public property Capacity: integer read FCapacity write SetCapacity; end; //---------------------------------------------------------------------------- { TCodeContextInfo } TCodeContextInfo = class private FEndPos: integer; FItems: PExpressionType; FCount: integer; FParameterIndex: integer; FProcName: string; FProcNameAtom: TAtomPosition; FStartPos: integer; FTool: TFindDeclarationTool; function GetItems(Index: integer): TExpressionType; public constructor Create; destructor Destroy; override; function Count: integer; property Items[Index: integer]: TExpressionType read GetItems; default; function Add(const Context: TExpressionType): 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; end; //---------------------------------------------------------------------------- // TIdentCompletionTool TIdentCompletionTool = class(TFindDeclarationTool) private LastGatheredIdentParent: TCodeTreeNode; LastGatheredIdentLevel: integer; ClassAndAncestors: TFPList;// list of PCodeXYPosition FoundPublicProperties: TAVLTree;// tree of PChar (pointing to the // property names in source) protected CurrentIdentifierList: TIdentifierList; CurrentContexts: TCodeContextInfo; function CollectAllIdentifiers(Params: TFindDeclarationParams; const FoundContext: TFindContext): TIdentifierFoundResult; procedure GatherPredefinedIdentifiers(CleanPos: integer; const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions); procedure GatherUsefulIdentifiers(CleanPos: integer; const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions); procedure GatherUnitnames(CleanPos: integer; const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions); procedure GatherSourceNames(const Context: TFindContext); procedure InitCollectIdentifiers(const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList); procedure ParseSourceTillCollectionStart(const CursorPos: TCodeXYPosition; out CleanCursorPos: integer; out CursorNode: TCodeTreeNode; out IdentStartPos, IdentEndPos: integer); procedure FindCollectionContext(Params: TFindDeclarationParams; IdentStartPos: integer; CursorNode: TCodeTreeNode; out GatherContext: TFindContext; out ContextExprStartPos: LongInt; out StartInSubContext: Boolean); function CollectAllContexts(Params: TFindDeclarationParams; const FoundContext: TFindContext): TIdentifierFoundResult; procedure AddCollectionContext(Tool: TFindDeclarationTool; Node: TCodeTreeNode); public function GatherIdentifiers(const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList; BeautifyCodeOptions: TBeautifyCodeOptions): boolean; function FindCodeContext(const CursorPos: TCodeXYPosition; out CodeContexts: TCodeContextInfo): boolean; end; const IdentifierCompatibilityNames: array[TIdentifierCompatibility] of string = ( 'icompExact', 'icompCompatible', 'icompUnknown', 'icompIncompatible' ); implementation function CompareIdentListItems(Data1, Data2: Pointer): integer; var Item1: TIdentifierListItem; Item2: TIdentifierListItem; begin Item1:=TIdentifierListItem(Data1); Item2:=TIdentifierListItem(Data2); // first sort for Compatibility (lower is better) if ord(Item1.Compatibility)ord(Item2.Compatibility) then begin Result:=1; exit; end; // then sort for History (lower is better) if Item1.HistoryIndexItem2.HistoryIndex then begin Result:=1; exit; end; // then sort for Level (lower is better) if Item1.LevelItem2.Level then begin Result:=1; exit; end; // then sort alpabetically (lower is better) Result:=CompareIdentifiers(Item2.Identifier,Item1.Identifier); if Result<>0 then exit; // then sort for ParamList (lower is better) Result:=Item2.CompareParamList(Item1); end; function CompareIdentListItemsForIdents(Data1, Data2: Pointer): integer; var Item1: TIdentifierListItem; Item2: TIdentifierListItem; begin Item1:=TIdentifierListItem(Data1); Item2:=TIdentifierListItem(Data2); // sort alpabetically (lower is better) Result:=CompareIdentifiers(Item2.Identifier,Item1.Identifier); if Result<>0 then exit; // then sort for ParamList (lower is better) Result:=Item2.CompareParamList(Item1); end; function CompareIdentHistListItem(Data1, Data2: Pointer): integer; var Item1: TIdentHistListItem; Item2: TIdentHistListItem; begin Item1:=TIdentHistListItem(Data1); Item2:=TIdentHistListItem(Data2); 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; HistItem: TIdentHistListItem; begin IdentItem:=TIdentifierListItem(Data1); HistItem:=TIdentHistListItem(Data2); Result:=CompareIdentifiers(PChar(Pointer(HistItem.Identifier)), IdentItem.Identifier); if Result<>0 then exit; //debugln('CompareIdentItemWithHistListItem ',HistItem.Identifier,'=',GetIdentifier(IdentItem.Identifier)); Result:=SysUtils.CompareText(HistItem.ParamList,IdentItem.ParamList); end; type TIdentifierListItemMemManager = class(TCodeToolMemManager) protected procedure FreeFirstItem; override; public procedure DisposeIdentListItem(IdentListItem: TIdentifierListItem); function NewIdentListItem(NewCompatibility: TIdentifierCompatibility; NewHasChilds: boolean; NewHistoryIndex: integer; NewIdentifier: PChar; NewLevel: integer; NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool; NewDefaultDesc: TCodeTreeNodeDesc): TIdentifierListItem; end; var IdentifierListItemMemManager: TIdentifierListItemMemManager; { TIdentifierListItemMemManager } procedure TIdentifierListItemMemManager.FreeFirstItem; var Item: TIdentifierListItem; begin Item:=TIdentifierListItem(FFirstFree); TIdentifierListItem(FFirstFree):=Item.FNext; Item.Free; end; procedure TIdentifierListItemMemManager.DisposeIdentListItem( IdentListItem: TIdentifierListItem); begin if (FFreeCount free IdentListItem IdentListItem.Free; {$IFDEF DebugCTMemManager} inc(FFreedCount); {$ENDIF} end; dec(FCount); end; function TIdentifierListItemMemManager.NewIdentListItem( NewCompatibility: TIdentifierCompatibility; NewHasChilds: boolean; NewHistoryIndex: integer; NewIdentifier: PChar; NewLevel: integer; NewNode: TCodeTreeNode; NewTool: TFindDeclarationTool; NewDefaultDesc: TCodeTreeNodeDesc): TIdentifierListItem; begin if FFirstFree<>nil then begin // take from free list Result:=TIdentifierListItem(FFirstFree); // ToDo: set values TIdentifierListItem(FFirstFree):=Result.FNext; Result.FNext:=nil; dec(FFreeCount); end else begin // free list empty -> create new node Result:=TIdentifierListItem.Create(NewCompatibility, NewHasChilds,NewHistoryIndex,NewIdentifier,NewLevel, NewNode,NewTool, NewDefaultDesc); {$IFDEF DebugCTMemManager} inc(FAllocatedCount); {$ENDIF} end; inc(FCount); end; { TIdentifierList } 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; 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; while AnAVLNode<>nil do begin CurItem:=TIdentifierListItem(AnAVLNode.Data); if (CurItem.Identifier<>nil) and ComparePrefixIdent(PChar(Pointer(Prefix)),CurItem.Identifier) then begin {$IFDEF ShowFilteredIdents} DebugLn('::: FILTERED ITEM ',FFilteredList.Count,' ',GetIdentifier(CurItem.Identifier)); {$ENDIF} FFilteredList.Add(CurItem); end; AnAVLNode:=FItems.FindSuccessor(AnAVLNode); end; {$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.SetContextFlags( const AValue: TIdentifierListContextFlags); begin if FContextFlags=AValue then exit; FContextFlags:=AValue; 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.Create(@CompareIdentListItems); FIdentView:=TAVLTree.Create(@CompareIdentListItemsForIdents); FIdentSearchItem:=TIdentifierListItem.Create(icompUnknown, false,0,nil,0,nil,nil,ctnNone); FCreatedIdentifiers:=TFPList.Create; end; destructor TIdentifierList.Destroy; begin Clear; 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; 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; Include(FFlags,ilfFilteredListNeedsUpdate); end; procedure TIdentifierList.Add(NewItem: TIdentifierListItem); var AnAVLNode: TAVLTreeNode; begin 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); Include(FFlags,ilfFilteredListNeedsUpdate); 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:=''; Result:=FIdentView.FindKey(FIdentSearchItem, @CompareIdentListItemsForIdents)<>nil; 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.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 prefix of all of them var AnAVLNode: TAVLTreeNode; CurItem: TIdentifierListItem; FoundFirst: Boolean; SamePos: Integer; begin Result:=Prefix; FoundFirst:=false; AnAVLNode:=FItems.FindLowest; while AnAVLNode<>nil do begin CurItem:=TIdentifierListItem(AnAVLNode.Data); if (CurItem.Identifier<>nil) and ComparePrefixIdent(PChar(Pointer(Prefix)),CurItem.Identifier) then begin if not FoundFirst then begin Result:=GetIdentifier(CurItem.Identifier); FoundFirst:=true; end else begin SamePos:=length(Prefix); while (SamePosnil 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 [ctnClass,ctnClassInterface])) do CurClassNode:=CurClassNode.Parent; if CurClassNode=nil then exit; p:=CreateFindContext(Params.NewCodeTool,CurClassNode); if IndexOfFindContext(ClassAndAncestors,@p)>=0 then begin // this class node is the class or one of the ancestors of the class // of the start context of the identifier completion exit(true); end; end; Result:=false; end; function PropertyIsOverridenPublicPublish: boolean; begin // protected properties can be made public in child classes. //debugln('PropertyIsOverridenPublicPublish Identifier=',GetIdentifier(Ident),' Find=',dbgs((FoundPublicProperties<>nil) and (FoundPublicProperties.Find(Ident)<>nil))); if FoundPublicProperties<>nil then begin if FoundPublicProperties.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 FoundPublicProperties=nil then begin // create tree FoundPublicProperties:= TAVLTree.Create(TListSortCompare(@CompareIdentifiers)) end else if FoundPublicProperties.Find(Ident)<>nil then begin // identifier is already public end; FoundPublicProperties.Add(Ident); //debugln('SavePublicPublishedProperty Identifier=',GetIdentifier(Ident),' Find=',dbgs(FoundPublicProperties.Find(Ident)<>nil)); end; var NewItem: TIdentifierListItem; begin // proceed searching ... Result:=ifrProceedSearch; {$IFDEF ShowFoundIdents} 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 LastGatheredIdentParent<>CurContextParent then begin // new context level LastGatheredIdentParent:=CurContextParent; inc(LastGatheredIdentLevel); end; if FoundContext.Tool=Self then begin // identifier is in the same unit //DebugLn('::: COLLECT IDENT in SELF ',FoundContext.Node.DescAsString, // ' "',StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50)),'"' // ,' '+dbgs(fdfIgnoreUsedUnits in Params.Flags)); end else begin // identifier is in another unit if (FoundContext.Node.Parent<>nil) then begin if (FoundContext.Node.Parent.Desc=ctnClassPrivate) then begin // skip private definitions in other units exit; end; if (FoundContext.Node.Parent.Desc=ctnClassProtected) then begin // protected defnitions are only accessible from descendants if ProtectedNodeIsInAllowedClass then begin //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 //debugln('TIdentCompletionTool.CollectAllIdentifiers MAYBE Protected made Public '+StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50))); end else begin //debugln('TIdentCompletionTool.CollectAllIdentifiers FORBIDDEN Protected '+StringToPascalConst(copy(FoundContext.Tool.Src,FoundContext.Node.StartPos,50))); exit; end; end; end; end; Ident:=nil; case FoundContext.Node.Desc of ctnTypeDefinition: begin if (FoundContext.Node.FirstChild<>nil) and (FoundContext.Node.FirstChild.Desc in [ctnClass,ctnClassInterface]) and ((ctnsForwardDeclaration and FoundContext.Node.FirstChild.SubDesc)>0) then // skip forward definition exit; Ident:=@FoundContext.Tool.Src[FoundContext.Node.StartPos]; end; ctnVarDefinition,ctnConstDefinition,ctnEnumIdentifier: Ident:=@FoundContext.Tool.Src[FoundContext.Node.StartPos]; ctnProcedure,ctnProcedureHead: Ident:=FoundContext.Tool.GetProcNameIdentifier(FoundContext.Node); 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; exit; end; if (FoundContext.Node.Parent.Desc in [ctnClassPrivate,ctnClassProtected]) and (not PropertyIsOverridenPublicPublish) then begin exit; end; end; end; if Ident=nil then exit; NewItem:=TIdentifierListItem.Create( icompUnknown, false, 0, Ident, LastGatheredIdentLevel, FoundContext.Node, FoundContext.Tool, ctnNone); {$IFDEF ShowFoundIdents} DebugLn(' IDENT COLLECTED: ',NewItem.AsString); {$ENDIF} CurrentIdentifierList.Add(NewItem); end; procedure TIdentCompletionTool.GatherPredefinedIdentifiers(CleanPos: integer; const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions); // Add predefined identifiers const CompilerFuncHistoryIndex = 10; CompilerFuncLevel = 10; 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 AddCompilerProcedure(const AProcName, AParameterList: PChar); var NewItem: TIdentifierListItem; begin NewItem:=TIdentifierListItem.Create( icompUnknown, false, CompilerFuncHistoryIndex, AProcName, CompilerFuncLevel, nil, nil, ctnProcedure); NewItem.ParamList:=AParameterList; CurrentIdentifierList.Add(NewItem); end; procedure AddCompilerFunction(const AProcName, AParameterList, AResultType: PChar); var NewItem: TIdentifierListItem; begin NewItem:=TIdentifierListItem.Create( icompUnknown, false, CompilerFuncHistoryIndex, AProcName, CompilerFuncLevel, nil, nil, ctnProcedure); NewItem.ParamList:=AParameterList; NewItem.Flags:=NewItem.Flags+[iliIsFunction,iliIsFunctionValid]; CurrentIdentifierList.Add(NewItem); end; var NewItem: TIdentifierListItem; ProcNode: TCodeTreeNode; begin if Context.Node.Desc in AllPascalStatements then begin AddCompilerProcedure('SetLength','array of type; NewLength: integer'); AddCompilerProcedure('Copy','const s: string; FromPosition, ToPosition: integer'); AddCompilerProcedure('Write','Args : Arguments'); AddCompilerProcedure('WriteLn','Args : Arguments'); AddCompilerProcedure('Read',''); AddCompilerProcedure('ReadLn',''); AddCompilerFunction('Length','array of type','ordinal'); AddCompilerFunction('High','Argument','ordinal'); AddCompilerFunction('Low','Argument','ordinal'); AddCompilerProcedure('Include','set of enum; enum'); AddCompilerProcedure('Exclude','set of enum; enum'); if Context.Tool.NodeIsInAMethod(Context.Node) and (not CurrentIdentifierList.HasIdentifier('Self','')) then begin // method body -> add 'Self' NewItem:=TIdentifierListItem.Create( icompUnknown, true, 1, 'Self', StatementLevel, nil, nil, ctnVarDefinition); CurrentIdentifierList.Add(NewItem); end; ProcNode:=Context.Node.GetNodeOfType(ctnProcedure); if Context.Tool.NodeIsFunction(ProcNode) and (not CurrentIdentifierList.HasIdentifier('Result','')) then begin // function body -> add 'Result' NewItem:=TIdentifierListItem.Create( icompUnknown, true, 1, 'Result', StatementLevel, nil, nil, ctnVarDefinition); CurrentIdentifierList.Add(NewItem); end; end; end; procedure TIdentCompletionTool.GatherUsefulIdentifiers(CleanPos: integer; const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions); var NewItem: TIdentifierListItem; PropertyName: String; begin while (CleanPos>1) and (IsIdentChar[Src[CleanPos-1]]) do dec(CleanPos); GatherPredefinedIdentifiers(CleanPos,Context,BeautifyCodeOptions); if Context.Node.Desc=ctnProperty then begin PropertyName:=ExtractPropName(Context.Node,false); //debugln('TIdentCompletionTool.GatherUsefulIdentifiers Property ',PropertyName); MoveCursorToCleanPos(CleanPos); ReadPriorAtom; if UpAtomIs('READ') then begin // add the default class completion 'read' specifier function NewItem:=TIdentifierListItem.Create( icompUnknown,true,0, CurrentIdentifierList.CreateIdentifier( BeautifyCodeOptions.PropertyReadIdentPrefix+PropertyName), 0,nil,nil,ctnProcedure); CurrentIdentifierList.Add(NewItem); end; if UpAtomIs('WRITE') then begin // add the default class completion 'write' specifier function NewItem:=TIdentifierListItem.Create( icompUnknown,true,0, CurrentIdentifierList.CreateIdentifier( BeautifyCodeOptions.PropertyWriteIdentPrefix+PropertyName), 0,nil,nil,ctnProcedure); CurrentIdentifierList.Add(NewItem); end; if UpAtomIs('READ') or UpAtomIs('WRITE') then begin // add the default class completion 'read'/'write' specifier variable NewItem:=TIdentifierListItem.Create( icompUnknown,true,0, CurrentIdentifierList.CreateIdentifier( BeautifyCodeOptions.PrivateVariablePrefix+PropertyName), 0,nil,nil,ctnVarDefinition); CurrentIdentifierList.Add(NewItem); end; if UpAtomIs('STORED') then begin // add the default class completion 'stored' specifier function NewItem:=TIdentifierListItem.Create( icompUnknown,true,0, CurrentIdentifierList.CreateIdentifier( PropertyName+BeautifyCodeOptions.PropertyStoredIdentPostfix), 0,nil,nil,ctnProcedure); CurrentIdentifierList.Add(NewItem); end; end; end; procedure TIdentCompletionTool.GatherUnitnames(CleanPos: integer; const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions); var TreeOfUnitFiles: TAVLTree; procedure GatherUnitsFromUnitLinks; var UnitLinks: string; UnitLinkStart: Integer; UnitLinkEnd: LongInt; UnitLinkLen: Integer; Filename: String; begin UnitLinks:=Scanner.Values[ExternalMacroStart+'UnitLinks']; UnitLinkStart:=1; while UnitLinkStart<=length(UnitLinks) do begin while (UnitLinkStart<=length(UnitLinks)) and (UnitLinks[UnitLinkStart] in [#10,#13]) do inc(UnitLinkStart); UnitLinkEnd:=UnitLinkStart; while (UnitLinkEnd<=length(UnitLinks)) and (UnitLinks[UnitLinkEnd]<>' ') do inc(UnitLinkEnd); UnitLinkLen:=UnitLinkEnd-UnitLinkStart; if UnitLinkLen>0 then begin Filename:=copy(UnitLinks,UnitLinkStart,UnitLinkEnd-UnitLinkStart); AddToTreeOfUnitFiles(TreeOfUnitFiles,Filename,false); end; UnitLinkStart:=UnitLinkEnd+1; while (UnitLinkStart<=length(UnitLinks)) and (not (UnitLinks[UnitLinkStart] in [#10,#13])) do inc(UnitLinkStart); end; end; var UnitPath, SrcPath: string; BaseDir: String; ANode: TAVLTreeNode; UnitFileInfo: TUnitFileInfo; NewItem: TIdentifierListItem; UnitExt: String; SrcExt: String; CurSourceName: String; begin UnitPath:=''; SrcPath:=''; GatherUnitAndSrcPath(UnitPath,SrcPath); //DebugLn('TIdentCompletionTool.GatherUnitnames UnitPath="',UnitPath,'" SrcPath="',SrcPath,'"'); BaseDir:=ExtractFilePath(MainFilename); TreeOfUnitFiles:=nil; try // search in unitpath UnitExt:='pp;pas;ppu'; GatherUnitFiles(BaseDir,UnitPath,UnitExt,false,true,TreeOfUnitFiles); // search in srcpath SrcExt:='pp;pas'; GatherUnitFiles(BaseDir,SrcPath,SrcExt,false,true,TreeOfUnitFiles); // add unitlinks GatherUnitsFromUnitLinks; // create list CurSourceName:=GetSourceName; ANode:=TreeOfUnitFiles.FindLowest; while ANode<>nil do begin UnitFileInfo:=TUnitFileInfo(ANode.Data); if CompareIdentifiers(PChar(Pointer(UnitFileInfo.UnitName)), PChar(Pointer(CurSourceName)))<>0 then begin NewItem:=TIdentifierListItem.Create( icompCompatible,true,0, CurrentIdentifierList.CreateIdentifier(UnitFileInfo.UnitName), 0,nil,nil,ctnUnit); CurrentIdentifierList.Add(NewItem); end; ANode:=TreeOfUnitFiles.FindSuccessor(ANode); end; finally FreeTreeOfUnitFiles(TreeOfUnitFiles); end; end; procedure TIdentCompletionTool.GatherSourceNames(const Context: TFindContext); procedure Add(const SrcName: string); var NewItem: TIdentifierListItem; begin NewItem:=TIdentifierListItem.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.InitCollectIdentifiers( const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList); begin if IdentifierList=nil then IdentifierList:=TIdentifierList.Create; CurrentIdentifierList:=IdentifierList; CurrentIdentifierList.Clear; LastGatheredIdentParent:=nil; LastGatheredIdentLevel:=0; CurrentIdentifierList.StartContextPos:=CursorPos; CurrentIdentifierList.StartContext.Tool:=Self; end; procedure TIdentCompletionTool.ParseSourceTillCollectionStart( const CursorPos: TCodeXYPosition; out CleanCursorPos: integer; out CursorNode: TCodeTreeNode; out IdentStartPos, IdentEndPos: integer); begin CleanCursorPos:=0; CursorNode:=nil; IdentStartPos:=0; IdentEndPos:=0; // build code tree {$IFDEF CTDEBUG} DebugLn('TIdentCompletionTool.GatherIdentifiers A CursorPos=',dbgs(CursorPos.X),',',dbgs(CursorPos.Y)); {$ENDIF} BuildTreeAndGetCleanPos(trTillCursor,CursorPos,CleanCursorPos, [{$IFNDEF DisableIgnoreErrorAfter}btSetIgnoreErrorPos{$ENDIF}]); // find node at position CursorNode:=FindDeepestExpandedNodeAtPos(CleanCursorPos,true); if CurrentIdentifierList<>nil then CurrentIdentifierList.StartContext.Node:=CursorNode; // get identifier position GetIdentStartEndAtPosition(Src,CleanCursorPos,IdentStartPos,IdentEndPos); end; procedure TIdentCompletionTool.FindCollectionContext( Params: TFindDeclarationParams; IdentStartPos: integer; CursorNode: TCodeTreeNode; out GatherContext: TFindContext; out ContextExprStartPos: LongInt; out StartInSubContext: Boolean); function GetContextExprStartPos(IdentStartPos: integer; ContextNode: TCodeTreeNode): integer; begin Result:=FindStartOfVariable(IdentStartPos); if Result0 then begin MoveCursorToCleanPos(StartPosOfVariable); ReadPriorAtom; CurrentIdentifierList.StartAtomInFront:=CurPos; // check if LValue if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags) then begin if (CurPos.Flag in [cafSemicolon,cafBegin,cafEnd]) or WordIsBlockKeyWord.DoItUpperCase(UpperSrc, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then begin CurrentIdentifierList.ContextFlags:= CurrentIdentifierList.ContextFlags+[ilcfStartIsLValue]; end; if UpAtomIs('IF') or UpAtomIs('FOR') or UpAtomIs('DO') or UpAtomIs('CASE') or UpAtomIs('OF') then begin CurrentIdentifierList.ContextFlags:= CurrentIdentifierList.ContextFlags+[ilcfIsExpression]; end; end; end; // context behind if IdentEndPosctnBeginBlock) and (not CursorNode.HasParentOfType(ctnBeginBlock)) then exit; // check is cursor is in a parameter list behind an identifier if not CheckParameterSyntax(CursorNode, CleanCursorPos, VarNameAtom, ProcNameAtom, ParameterIndex) then exit; if VarNameAtom.StartPos<1 then exit; //DebugLn('CheckContextIsParameter Variable=',GetAtom(VarNameAtom),' Proc=',GetAtom(ProcNameAtom),' ParameterIndex=',dbgs(ParameterIndex)); // it is a parameter -> create context Result:=true; if CurrentContexts=nil then CurrentContexts:=TCodeContextInfo.Create; CurrentContexts.Tool:=Self; CurrentContexts.ParameterIndex:=ParameterIndex+1; CurrentContexts.ProcNameAtom:=ProcNameAtom; CurrentContexts.ProcName:=GetAtom(ProcNameAtom); MoveCursorToAtomPos(ProcNameAtom); ReadNextAtom; // read opening bracket CurrentContexts.StartPos:=CurPos.EndPos; // read closing bracket if ReadTilBracketClose(false) then CurrentContexts.EndPos:=CurPos.StartPos else CurrentContexts.EndPos:=SrcLen+1; FindCollectionContext(Params,ProcNameAtom.StartPos,CursorNode, GatherContext,ContextExprStartPos,StartInSubContext); // gather declarations of all parameter lists Params.ContextNode:=GatherContext.Node; Params.SetIdentifier(Self,@Src[ProcNameAtom.StartPos],@CollectAllContexts); Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable]; if not StartInSubContext then Include(Params.Flags,fdfSearchInParentNodes); if Params.ContextNode.Desc in [ctnClass,ctnClassInterface] then Exclude(Params.Flags,fdfSearchInParentNodes); CurrentIdentifierList.Context:=GatherContext; //DebugLn('CheckContextIsParameter searching procedure ...'); GatherContext.Tool.FindIdentifierInContext(Params); //DebugLn('CheckContextIsParameter END'); Ok:=true; end; var IdentifierList: TIdentifierList; IdentStartPos, IdentEndPos: integer; begin CodeContexts:=nil; Result:=false; IdentifierList:=nil; CurrentContexts:=CodeContexts; ActivateGlobalWriteLock; Params:=TFindDeclarationParams.Create; try InitCollectIdentifiers(CursorPos,IdentifierList); ParseSourceTillCollectionStart(CursorPos,CleanCursorPos,CursorNode, IdentStartPos,IdentEndPos); // find class and ancestors if existing (needed for protected identifiers) FindContextClassAndAncestors(CursorPos,ClassAndAncestors); if CursorNode<>nil then begin if CheckContextIsParameter(Result) then exit; end; if CodeContexts=nil then begin // create default AddCollectionContext(Self,CursorNode); end; Result:=true; finally if Result then begin CodeContexts:=CurrentContexts; CurrentContexts:=nil; end else begin FreeAndNil(CurrentContexts); end; FreeListOfPFindContext(ClassAndAncestors); FreeAndNil(FoundPublicProperties); Params.Free; ClearIgnoreErrorAfter; DeactivateGlobalWriteLock; FreeAndNil(CurrentIdentifierList); end; Result:=false; end; { TIdentifierListItem } function TIdentifierListItem.GetParamList: string; begin if not (iliParamListValid in Flags) then begin // Note: if you implement param lists for other than ctnProcedure, check // CompareParamList if (Node<>nil) and (Node.Desc=ctnProcedure) then begin FParamList:=Tool.ExtractProcHead(Node, [phpWithoutClassKeyword,phpWithoutClassName, phpWithoutName,phpInUpperCase]); //debugln('TIdentifierListItem.GetParamList A ',GetIdentifier(Identifier),' ',Tool.MainFilename,' ',dbgs(Node.StartPos)); end else FParamList:=''; Include(Flags,iliParamListValid); end; Result:=FParamList; end; procedure TIdentifierListItem.SetParamList(const AValue: string); begin FParamList:=AValue; Include(Flags,iliParamListValid); end; function TIdentifierListItem.AsString: string; begin Result:=IdentifierCompatibilityNames[Compatibility]; if HasChilds then Result:=Result+' HasChilds' else Result:=Result+' HasNoChilds'; Result:=Result+' History='+IntToStr(HistoryIndex); Result:=Result+' Ident='+GetIdentifier(Identifier); Result:=Result+' Lvl='+IntToStr(Level); if Tool<>nil then Result:=Result+' File='+Tool.MainFilename; if Node<>nil then Result:=Result+' Node='+Node.DescAsString +' "'+StringToPascalConst(copy(Tool.Src,Node.StartPos,50))+'"'; end; function TIdentifierListItem.GetDesc: TCodeTreeNodeDesc; begin if Node<>nil then Result:=Node.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:=NewIdentifier; Level:=NewLevel; Node:=NewNode; Tool:=NewTool; DefaultDesc:=NewDefaultDesc; BaseExprType:=CleanExpressionType; end; function TIdentifierListItem.IsProcNodeWithParams: boolean; begin Result:=(Node<>nil) and Tool.ProcNodeHasParamList(Node); end; function TIdentifierListItem.IsPropertyWithParams: boolean; begin Result:=(Node<>nil) and Tool.PropertyNodeHasParamList(Node); end; function TIdentifierListItem.CheckHasChilds: boolean; // returns true if test was successful begin Result:=false; if GetDesc in [ctnClass,ctnRecordType,ctnClassInterface] then begin Result:=true; exit; end; if Node=nil then exit; UpdateBaseContext; if (BaseExprType.Desc=xtContext) and (BaseExprType.Context.Node<>nil) and (BaseExprType.Context.Node.Desc in [ctnClass,ctnRecordType,ctnClassInterface]) then Include(Flags,iliHasChilds); end; function TIdentifierListItem.CanBeAssigned: boolean; begin Result:=false; if (Node=nil) then exit; if (GetDesc=ctnVarDefinition) then Result:=true; end; procedure TIdentifierListItem.UpdateBaseContext; var Params: TFindDeclarationParams; begin if (iliBaseExprTypeValid in Flags) then exit; BaseExprType:=CleanExpressionType; BaseExprType.Desc:=xtNone; if (Node<>nil) and (Tool<>nil) then begin Tool.ActivateGlobalWriteLock; Params:=TFindDeclarationParams.Create; try BaseExprType.Context:=Tool.FindBaseTypeOfNode(Params,Node); if (BaseExprType.Context.Node<>nil) then BaseExprType.Desc:=xtContext; finally Params.Free; Tool.DeactivateGlobalWriteLock; end; end; Include(Flags,iliBaseExprTypeValid); end; function TIdentifierListItem.HasChilds: boolean; begin Result:=iliHasChilds in Flags; end; function TIdentifierListItem.IsFunction: boolean; begin if not (iliIsFunctionValid in Flags) then begin if (Node<>nil) and Tool.NodeIsFunction(Node) then Include(Flags,iliIsFunction); Include(Flags,iliIsFunctionValid); end; Result:=iliIsFunction in Flags; end; procedure TIdentifierListItem.Clear; begin FParamList:=''; Compatibility:=icompUnknown; HistoryIndex:=0; Identifier:=nil; Level:=0; Node:=nil; Tool:=nil; DefaultDesc:=ctnNone; Flags:=[]; BaseExprType:=CleanExpressionType; end; function TIdentifierListItem.CompareParamList(CompareItem: TIdentifierListItem ): integer; begin Result:=0; if Self=CompareItem then exit; if (Node=CompareItem.Node) then exit; if (Node=nil) or (CompareItem.Node=nil) then exit; if (Node.Desc<>ctnProcedure) or (CompareItem.Node.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:=SysUtils.CompareText(ParamList,CompareItem.ParamList); 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:=GetIdentifier(NewItem.Identifier); NewHistItem.NodeDesc:=NewItem.GetDesc; NewHistItem.ParamList:=NewItem.ParamList; AdjustIndex:=0; 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; { TCodeContextInfo } function TCodeContextInfo.GetItems(Index: integer): TExpressionType; begin Result:=FItems[Index]; end; constructor TCodeContextInfo.Create; begin end; destructor TCodeContextInfo.Destroy; begin Clear; inherited Destroy; end; function TCodeContextInfo.Count: integer; begin Result:=FCount; end; function TCodeContextInfo.Add(const Context: TExpressionType): integer; begin inc(FCount); Result:=Count; ReAllocMem(FItems,SizeOf(TExpressionType)*FCount); FItems[FCount-1]:=Context; end; procedure TCodeContextInfo.Clear; begin FCount:=0; ReAllocMem(FItems,0); end; initialization IdentifierListItemMemManager:=TIdentifierListItemMemManager.Create; finalization IdentifierListItemMemManager.Free; IdentifierListItemMemManager:=nil; end.