lazarus/components/codetools/identcompletiontool.pas
2006-02-04 18:19:30 +00:00

1516 lines
48 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., 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
);
TIdentListItemFlags = set of TIdentListItemFlag;
{ TIdentifierListItem }
TIdentifierListItem = class
private
FNext: TIdentifierListItem;
FParamList: string;
FParamListValid: boolean;
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;
procedure Clear;
function CompareParamList(CompareItem: TIdentifierListItem): integer;
public
property ParamList: string read GetParamList write SetParamList;
end;
TIdentifierListFlag = (ilfFilteredListNeedsUpdate);
TIdentifierListFlags = set of TIdentifierListFlag;
TIdentifierListContextFlag = (
ilcfStartInStatement,
ilcfStartIsLValue,
ilcfContextNeedsEndSemicolon
);
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;
//----------------------------------------------------------------------------
// 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;
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);
public
function GatherIdentifiers(const CursorPos: TCodeXYPosition;
var IdentifierList: TIdentifierList;
BeautifyCodeOptions: TBeautifyCodeOptions): 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 else if ord(Item1.Compatibility)>ord(Item2.Compatibility) then begin
Result:=1;
exit;
end;
// 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;
// then sort for Level (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;
// 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(Item2.Identifier),PChar(Item1.Identifier));
if Result<>0 then exit;
//debugln('CompareIdentHistListItem ',Item2.Identifier,'=',Item1.Identifier);
Result:=CompareIdentifiers(PChar(Item2.ParamList),PChar(Item1.ParamList));
end;
function CompareIdentItemWithHistListItem(Data1, Data2: Pointer): integer;
var
IdentItem: TIdentifierListItem;
HistItem: TIdentHistListItem;
begin
IdentItem:=TIdentifierListItem(Data1);
HistItem:=TIdentHistListItem(Data2);
Result:=CompareIdentifiers(PChar(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<FMinFree) or (FFreeCount<((FCount shr 3)*FMaxFreeRatio)) then
begin
// add IdentListItem to Free list
IdentListItem.FNext:=TIdentifierListItem(FFirstFree);
TIdentifierListItem(FFirstFree):=IdentListItem;
inc(FFreeCount);
end else begin
// free list full -> 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(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(Ident),PChar(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(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(Prefix),CurItem.Identifier) then begin
if not FoundFirst then begin
Result:=GetIdentifier(CurItem.Identifier);
FoundFirst:=true;
end else begin
SamePos:=length(Prefix);
while (SamePos<length(Result))
and (UpChars[CurItem.Identifier[SamePos]]=UpChars[Result[SamePos+1]])
do
inc(SamePos);
if SamePos<length(Result) then begin
Result:=copy(Result,1,SamePos);
if length(Result)=length(Prefix) then exit;
end;
end;
end;
AnAVLNode:=FItems.FindSuccessor(AnAVLNode);
end;
end;
{ TIdentCompletionTool }
function TIdentCompletionTool.CollectAllIdentifiers(
Params: TFindDeclarationParams; const FoundContext: TFindContext
): TIdentifierFoundResult;
var
Ident: PChar;
CurContextParent: TCodeTreeNode;
function ProtectedNodeIsInAllowedClass: boolean;
var
CurClassNode: TCodeTreeNode;
p: TFindContext;
begin
if ClassAndAncestors<>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 [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
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;
var
NewItem: TIdentifierListItem;
ProcNode: TCodeTreeNode;
begin
if Context.Node.Desc in AllPascalStatements then begin
// begin..end -> add 'SetLength'
NewItem:=TIdentifierListItem.Create(
icompUnknown,
false,
3,
'SetLength',
StatementLevel,
nil,
nil,
ctnProcedure);
NewItem.ParamList:='array of type; NewLength: integer';
CurrentIdentifierList.Add(NewItem);
// begin..end -> add 'copy'
NewItem:=TIdentifierListItem.Create(
icompUnknown,
false,
3,
'Copy',
StatementLevel,
nil,
nil,
ctnProcedure);
NewItem.ParamList:='const s: string; FromPosition, ToPosition: integer';
CurrentIdentifierList.Add(NewItem);
// begin..end -> add 'write'
NewItem:=TIdentifierListItem.Create(
icompUnknown,
false,
3,
'Write',
StatementLevel,
nil,
nil,
ctnProcedure);
NewItem.ParamList:='various';
CurrentIdentifierList.Add(NewItem);
// begin..end -> add 'writeln'
NewItem:=TIdentifierListItem.Create(
icompUnknown,
false,
3,
'WriteLn',
StatementLevel,
nil,
nil,
ctnProcedure);
NewItem.ParamList:='various';
CurrentIdentifierList.Add(NewItem);
if Context.Tool.NodeIsInAMethod(Context.Node)
and (not CurrentIdentifierList.HasIdentifier('Self','')) then begin
// method body -> add 'Self'
NewItem:=TIdentifierListItem.Create(
icompUnknown,
true,
0,
'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,
0,
'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(UnitFileInfo.UnitName),PChar(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;
function TIdentCompletionTool.GatherIdentifiers(
const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList;
BeautifyCodeOptions: TBeautifyCodeOptions): boolean;
function GetContextExprStartPos(IdentStartPos: integer;
ContextNode: TCodeTreeNode): integer;
begin
Result:=FindStartOfVariable(IdentStartPos);
if Result<ContextNode.StartPos then
Result:=ContextNode.StartPos;
MoveCursorToCleanPos(Result);
ReadNextAtom;
case ContextNode.Desc of
ctnProperty:
// check for special property keywords
if WordIsPropertySpecifier.DoItUpperCase(UpperSrc,
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
then
// don't resolve property specifiers
Result:=IdentStartPos;
end;
end;
var
CleanCursorPos, IdentStartPos, IdentEndPos: integer;
CursorNode: TCodeTreeNode;
Params: TFindDeclarationParams;
GatherContext: TFindContext;
ExprType: TExpressionType;
ContextExprStartPos: Integer;
StartInSubContext: Boolean;
begin
Result:=false;
if IdentifierList=nil then IdentifierList:=TIdentifierList.Create;
CurrentIdentifierList:=IdentifierList;
CurrentIdentifierList.Clear;
LastGatheredIdentParent:=nil;
LastGatheredIdentLevel:=0;
CurrentIdentifierList.StartContextPos:=CursorPos;
CurrentIdentifierList.StartContext.Tool:=Self;
ActivateGlobalWriteLock;
Params:=TFindDeclarationParams.Create;
try
// 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);
CurrentIdentifierList.StartContext.Node:=CursorNode;
// find class and ancestors if existing (needed for protected identifiers)
FindContextClassAndAncestors(CursorPos,ClassAndAncestors);
// get identifier position
GetIdentStartEndAtPosition(Src,CleanCursorPos,IdentStartPos,IdentEndPos);
// find context
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers B',
' CleanCursorPos=',dbgs(CleanCursorPos),
' IdentStartPos=',dbgs(IdentStartPos),' IdentEndPos=',dbgs(IdentEndPos),
' Ident=',copy(Src,IdentStartPos,IdentEndPos-IdentStartPos));
{$ENDIF}
GatherContext:=CreateFindContext(Self,CursorNode);
if CursorNode.Desc=ctnUsesSection then begin
GatherUnitNames(IdentStartPos,GatherContext,BeautifyCodeOptions);
end else if CursorNode.Desc in AllSourceTypes then begin
GatherSourceNames(GatherContext);
end else begin
ContextExprStartPos:=GetContextExprStartPos(IdentStartPos,CursorNode);
if GatherContext.Node.Desc=ctnWithVariable then
GatherContext.Node:=GatherContext.Node.Parent;
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers C',
' ContextExprStartPos=',dbgs(ContextExprStartPos),
' Expr=',StringToPascalConst(copy(Src,ContextExprStartPos,
IdentStartPos-ContextExprStartPos)));
{$ENDIF}
StartInSubContext:=false;
if ContextExprStartPos<IdentStartPos then begin
MoveCursorToCleanPos(IdentStartPos);
Params.ContextNode:=CursorNode;
Params.SetIdentifier(Self,nil,nil);
Params.Flags:=[fdfExceptionOnNotFound,
fdfSearchInParentNodes,fdfSearchInAncestors];
ExprType:=FindExpressionTypeOfVariable(ContextExprStartPos,IdentStartPos,
Params);
if (ExprType.Desc=xtContext) then begin
GatherContext:=ExprType.Context;
StartInSubContext:=true;
end;
end;
// 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];
if not StartInSubContext then
Include(Params.Flags,fdfSearchInParentNodes);
if Params.ContextNode.Desc in [ctnClass,ctnClassInterface] then
Exclude(Params.Flags,fdfSearchInParentNodes);
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers F');
{$ENDIF}
CurrentIdentifierList.Context:=GatherContext;
GatherContext.Tool.FindIdentifierInContext(Params);
end;
// add useful identifiers without context
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers G');
{$ENDIF}
GatherUsefulIdentifiers(IdentStartPos,GatherContext,BeautifyCodeOptions);
// check for incomplete context
// context bracket level
CurrentIdentifierList.StartBracketLvl:=
GetBracketLvl(Src,CursorNode.StartPos,IdentStartPos,
Scanner.NestedComments);
// context behind
if IdentEndPos<SrcLen then begin
MoveCursorToCleanPos(IdentEndPos);
ReadNextAtom;
CurrentIdentifierList.StartAtomBehind:=CurPos;
// check if in statement
if CursorNode.Desc in AllPascalStatements then begin
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartInStatement];
// check if at end of statement
if (CurPos.Flag in [cafEnd,cafBegin])
or ((not UpAtomIs('ELSE'))
and (CurPos.Flag=cafWord)
and (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
then
if CurrentIdentifierList.StartBracketLvl=0 then
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfContextNeedsEndSemicolon];
end;
end;
// context in front of
MoveCursorToCleanPos(IdentStartPos);
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
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartIsLValue];
end;
end;
Result:=true;
finally
FreeListOfPFindContext(ClassAndAncestors);
FreeAndNil(FoundPublicProperties);
Params.Free;
ClearIgnoreErrorAfter;
DeactivateGlobalWriteLock;
end;
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers END');
{$ENDIF}
end;
{ TIdentifierListItem }
function TIdentifierListItem.GetParamList: string;
begin
if not FParamListValid 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:='';
FParamListValid:=true;
end;
Result:=FParamList;
end;
procedure TIdentifierListItem.SetParamList(const AValue: string);
begin
FParamList:=AValue;
FParamListValid:=true;
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;
procedure TIdentifierListItem.Clear;
begin
FParamList:='';
FParamListValid:=false;
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.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;
initialization
IdentifierListItemMemManager:=TIdentifierListItemMemManager.Create;
finalization
IdentifierListItemMemManager.Free;
IdentifierListItemMemManager:=nil;
end.