mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-07 13:38:00 +02:00
codetools: FindUsedUnitReferences
git-svn-id: trunk@42681 -
This commit is contained in:
parent
ae2989ecac
commit
5364c52f95
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -740,6 +740,8 @@ components/codetools/examples/finddeclaration.lpi svneol=native#text/plain
|
||||
components/codetools/examples/finddeclaration.lpr svneol=native#text/plain
|
||||
components/codetools/examples/findunusedunits.lpi svneol=native#text/plain
|
||||
components/codetools/examples/findunusedunits.lpr svneol=native#text/pascal
|
||||
components/codetools/examples/findusedunitreferences.lpi svneol=native#text/plain
|
||||
components/codetools/examples/findusedunitreferences.lpr svneol=native#text/plain
|
||||
components/codetools/examples/fixdefinitionorder.lpi svneol=native#text/plain
|
||||
components/codetools/examples/fixdefinitionorder.lpr svneol=native#text/plain
|
||||
components/codetools/examples/fixfilenames.lpi svneol=native#text/plain
|
||||
@ -812,6 +814,7 @@ components/codetools/examples/scanexamples/test.h svneol=native#text/plain
|
||||
components/codetools/examples/scanexamples/tgeneric2.pas svneol=native#text/plain
|
||||
components/codetools/examples/scanexamples/uglyifdefs.pas svneol=native#text/plain
|
||||
components/codetools/examples/scanexamples/unusedunits1.pas svneol=native#text/pascal
|
||||
components/codetools/examples/scanexamples/usedunitrefs1.pas svneol=native#text/plain
|
||||
components/codetools/examples/scanexamples/wrongforwarddefinitions.pas svneol=native#text/plain
|
||||
components/codetools/examples/setincludepath.lpi svneol=native#text/plain
|
||||
components/codetools/examples/setincludepath.pas svneol=native#text/plain
|
||||
|
@ -474,7 +474,7 @@ type
|
||||
function GatherOverloads(Code: TCodeBuffer; X,Y: integer;
|
||||
out Graph: TDeclarationOverloadsGraph): boolean;
|
||||
|
||||
// rename, remove identifier
|
||||
// find references, rename identifier, remove identifier
|
||||
function FindReferences(IdentifierCode: TCodeBuffer;
|
||||
X, Y: integer; SearchInCode: TCodeBuffer; SkipComments: boolean;
|
||||
var ListOfPCodeXYPosition: TFPList;
|
||||
@ -482,6 +482,8 @@ type
|
||||
): boolean;
|
||||
function FindUnitReferences(UnitCode, TargetCode: TCodeBuffer;
|
||||
SkipComments: boolean; var ListOfPCodeXYPosition: TFPList): boolean;
|
||||
function FindUsedUnitReferences(Code: TCodeBuffer; X, Y: integer;
|
||||
SkipComments: boolean; var ListOfPCodeXYPosition: TFPList): boolean;
|
||||
function RenameIdentifier(TreeOfPCodeXYPosition: TAVLTree;
|
||||
const OldIdentifier, NewIdentifier: string;
|
||||
DeclarationCode: TCodeBuffer = nil; DeclarationCaretXY: PPoint = nil): boolean;
|
||||
@ -2530,6 +2532,7 @@ end;
|
||||
|
||||
function TCodeToolManager.FindUnitReferences(UnitCode, TargetCode: TCodeBuffer;
|
||||
SkipComments: boolean; var ListOfPCodeXYPosition: TFPList): boolean;
|
||||
// finds unit name of UnitCode in unit of TargetCode
|
||||
begin
|
||||
Result:=false;
|
||||
{$IFDEF CTDEBUG}
|
||||
@ -2548,6 +2551,34 @@ begin
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
function TCodeToolManager.FindUsedUnitReferences(Code: TCodeBuffer; X,
|
||||
Y: integer; SkipComments: boolean; var ListOfPCodeXYPosition: TFPList
|
||||
): boolean;
|
||||
// finds in unit of Code all references of the unit at the uses clause at X,Y
|
||||
var
|
||||
CursorPos: TCodeXYPosition;
|
||||
begin
|
||||
Result:=false;
|
||||
{$IFDEF CTDEBUG}
|
||||
DebugLn('TCodeToolManager.FindUsedUnitReferences A ',Code.Filename,' X=',X,' Y=',Y,' SkipComments=',SkipComments);
|
||||
{$ENDIF}
|
||||
ListOfPCodeXYPosition:=nil;
|
||||
if not InitCurCodeTool(Code) then exit;
|
||||
CursorPos.X:=X;
|
||||
CursorPos.Y:=Y;
|
||||
CursorPos.Code:=Code;
|
||||
try
|
||||
FCurCodeTool.FindUsedUnitReferences(CursorPos,SkipComments,
|
||||
ListOfPCodeXYPosition);
|
||||
Result:=true;
|
||||
except
|
||||
on e: Exception do HandleException(e);
|
||||
end;
|
||||
{$IFDEF CTDEBUG}
|
||||
DebugLn('TCodeToolManager.FindUnitReferences END ');
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
function TCodeToolManager.RenameIdentifier(TreeOfPCodeXYPosition: TAVLTree;
|
||||
const OldIdentifier, NewIdentifier: string; DeclarationCode: TCodeBuffer;
|
||||
DeclarationCaretXY: PPoint): boolean;
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<fpdoc-descriptions>
|
||||
<package name="CodeTools">
|
||||
<module name="CodeToolManager">
|
||||
@ -6,6 +6,8 @@
|
||||
<short>Returns the unit search path of the given directory separated by semicolon</short>
|
||||
<descr>The unit path is created from the define templates variable #UnitPath.</descr>
|
||||
</element>
|
||||
<element name="TCodeToolManager.FindUnitReferences"><short>Searches unitname of UnitCode in unit of TargetCode</short>
|
||||
</element>
|
||||
</module>
|
||||
</package>
|
||||
</fpdoc-descriptions>
|
||||
|
64
components/codetools/examples/findusedunitreferences.lpi
Normal file
64
components/codetools/examples/findusedunitreferences.lpi
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CONFIG>
|
||||
<ProjectOptions>
|
||||
<Version Value="9"/>
|
||||
<General>
|
||||
<Flags>
|
||||
<LRSInOutputDirectory Value="False"/>
|
||||
</Flags>
|
||||
<SessionStorage Value="InProjectDir"/>
|
||||
<MainUnit Value="0"/>
|
||||
<Title Value="findusedunitreferences"/>
|
||||
</General>
|
||||
<BuildModes Count="1">
|
||||
<Item1 Name="default" Default="True"/>
|
||||
</BuildModes>
|
||||
<PublishOptions>
|
||||
<Version Value="2"/>
|
||||
<IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
|
||||
<ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
|
||||
</PublishOptions>
|
||||
<RunParams>
|
||||
<local>
|
||||
<FormatVersion Value="1"/>
|
||||
<LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
|
||||
</local>
|
||||
</RunParams>
|
||||
<RequiredPackages Count="1">
|
||||
<Item1>
|
||||
<PackageName Value="CodeTools"/>
|
||||
</Item1>
|
||||
</RequiredPackages>
|
||||
<Units Count="4">
|
||||
<Unit0>
|
||||
<Filename Value="findusedunitreferences.lpr"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="findusedunitreferences"/>
|
||||
</Unit0>
|
||||
<Unit1>
|
||||
<Filename Value="scanexamples/simpleunit1.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="SimpleUnit1"/>
|
||||
</Unit1>
|
||||
<Unit2>
|
||||
<Filename Value="scanexamples/unusedunits1.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="UnusedUnits1"/>
|
||||
</Unit2>
|
||||
<Unit3>
|
||||
<Filename Value="scanexamples/usedunitrefs1.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="UsedUnitRefs1"/>
|
||||
</Unit3>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
<Version Value="11"/>
|
||||
<SearchPaths>
|
||||
<OtherUnitFiles Value="scanexamples"/>
|
||||
</SearchPaths>
|
||||
<Other>
|
||||
<CompilerPath Value="$(CompPath)"/>
|
||||
</Other>
|
||||
</CompilerOptions>
|
||||
</CONFIG>
|
91
components/codetools/examples/findusedunitreferences.lpr
Normal file
91
components/codetools/examples/findusedunitreferences.lpr
Normal file
@ -0,0 +1,91 @@
|
||||
{
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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:
|
||||
Simple demonstrating, how to setup the codetools, FPC and Lazarus Source
|
||||
directory to find what unit A uses of unit B.
|
||||
|
||||
Usage:
|
||||
findusedunitreferences filename line column
|
||||
|
||||
Filename is a unit.
|
||||
Line, column is a unit within a uses section.
|
||||
}
|
||||
program FindUsedUnitReferences;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
uses
|
||||
Classes, SysUtils, CodeCache, CodeToolManager, DefineTemplates,
|
||||
CodeToolsConfig, FileProcs, usedunitrefs1;
|
||||
|
||||
const
|
||||
ConfigFilename = 'codetools.config';
|
||||
var
|
||||
Code: TCodeBuffer;
|
||||
Filename: String;
|
||||
ListOfPCodeXYPosition: TFPList;
|
||||
X: Integer;
|
||||
Y: Integer;
|
||||
begin
|
||||
if (ParamCount>=1) and (Paramcount<3) then begin
|
||||
writeln('Usage:');
|
||||
writeln(' ',ParamStr(0));
|
||||
writeln(' ',ParamStr(0),' <filename> <X> <Y>');
|
||||
Halt(1);
|
||||
end;
|
||||
|
||||
CodeToolBoss.SimpleInit(ConfigFilename);
|
||||
|
||||
// Example: find all references to unit Math
|
||||
Filename:=ExpandFileName('scanexamples/usedunitrefs1.pas');
|
||||
X:=23;
|
||||
Y:=8;
|
||||
|
||||
if (ParamCount>=3) then begin
|
||||
Filename:=CleanAndExpandFilename(ParamStr(1));
|
||||
X:=StrToInt(ParamStr(2));
|
||||
Y:=StrToInt(ParamStr(3));
|
||||
writeln('File: ',Filename,' Line=',Y,' Column=',X);
|
||||
end;
|
||||
|
||||
// Step 1: load the file
|
||||
Code:=CodeToolBoss.LoadFile(Filename,false,false);
|
||||
if Code=nil then
|
||||
raise Exception.Create('loading failed '+Filename);
|
||||
|
||||
// Step 2: find references
|
||||
writeln('Filename: ',Code.Filename);
|
||||
ListOfPCodeXYPosition:=nil;
|
||||
try
|
||||
if CodeToolBoss.FindUsedUnitReferences(Code,X,Y,false,ListOfPCodeXYPosition) then
|
||||
begin
|
||||
writeln('List:');
|
||||
writeln(ListOfPCodeXYPositionToStr(ListOfPCodeXYPosition));
|
||||
end else begin
|
||||
writeln('CodeToolBoss.FindUsedUnitReferences failed: ',CodeToolBoss.ErrorMessage);
|
||||
end;
|
||||
finally
|
||||
FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
|
||||
end;
|
||||
end.
|
||||
|
28
components/codetools/examples/scanexamples/usedunitrefs1.pas
Normal file
28
components/codetools/examples/scanexamples/usedunitrefs1.pas
Normal file
@ -0,0 +1,28 @@
|
||||
unit UsedUnitRefs1;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, math, process;
|
||||
|
||||
implementation
|
||||
|
||||
procedure DoSome;
|
||||
var
|
||||
c: TComponent;
|
||||
d: float;
|
||||
i: Int64;
|
||||
begin
|
||||
c:=TComponent.Create(nil);
|
||||
c.Free;
|
||||
d:=1.3;
|
||||
i:=round(d);
|
||||
writeln(i+abs(d));
|
||||
writeln(sin(d));
|
||||
writeln(MinFloat<d);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -727,6 +727,9 @@ type
|
||||
function CheckParameterSyntax(StartPos, CleanCursorPos: integer;
|
||||
out ParameterAtom, ProcNameAtom: TAtomPosition;
|
||||
out ParameterIndex: integer): boolean;
|
||||
procedure OnFindUsedUnitIdentifier(Sender: TPascalParserTool;
|
||||
IdentifierCleanPos: integer; Range: TEPRIRange;
|
||||
Node: TCodeTreeNode; Data: Pointer; var Abort: boolean);
|
||||
protected
|
||||
public
|
||||
constructor Create;
|
||||
@ -823,7 +826,11 @@ type
|
||||
function FindReferences(const CursorPos: TCodeXYPosition;
|
||||
SkipComments: boolean; out ListOfPCodeXYPosition: TFPList): boolean;
|
||||
function FindUnitReferences(UnitCode: TCodeBuffer;
|
||||
SkipComments: boolean; out ListOfPCodeXYPosition: TFPList): boolean;
|
||||
SkipComments: boolean; out ListOfPCodeXYPosition: TFPList): boolean; // searches unitname of UnitCode
|
||||
procedure FindUsedUnitReferences(const CursorPos: TCodeXYPosition;
|
||||
SkipComments: boolean; out ListOfPCodeXYPosition: TFPList); // searches all references of unit in uses clause
|
||||
procedure FindUsedUnitReferences(TargetTool: TFindDeclarationTool;
|
||||
SkipComments: boolean; out ListOfPCodeXYPosition: TFPList); // searches all references of TargetTool
|
||||
|
||||
function CleanPosIsDeclarationIdentifier(CleanPos: integer;
|
||||
Node: TCodeTreeNode): boolean;
|
||||
@ -899,6 +906,18 @@ function dbgs(const vat: TVariableAtomType): string; overload;
|
||||
|
||||
implementation
|
||||
|
||||
type
|
||||
|
||||
{ TFindUsedUnitReferences }
|
||||
|
||||
TFindUsedUnitReferences = class
|
||||
public
|
||||
TargetTool: TFindDeclarationTool;
|
||||
TargetUnitName: string;
|
||||
ListOfPCodeXYPosition: TFPList;
|
||||
Params: TFindDeclarationParams;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
function dbgs(const Flags: TFindDeclarationFlags): string;
|
||||
var
|
||||
@ -1203,6 +1222,12 @@ begin
|
||||
ListOfPFindContext:=nil;
|
||||
end;
|
||||
|
||||
destructor TFindUsedUnitReferences.Destroy;
|
||||
begin
|
||||
FreeAndNil(Params);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{ TFindDeclarationTool }
|
||||
|
||||
function TFindDeclarationTool.FindDeclaration(const CursorPos: TCodeXYPosition;
|
||||
@ -4613,7 +4638,7 @@ var
|
||||
except
|
||||
on E: ECodeToolError do begin
|
||||
if E.Sender<>Self then begin
|
||||
// there is an error in another unit, which prevetns searching
|
||||
// there is an error in another unit, which prevents searching
|
||||
// stop further searching in this unit
|
||||
raise;
|
||||
end;
|
||||
@ -5097,6 +5122,55 @@ begin
|
||||
Result:=true;
|
||||
end;
|
||||
|
||||
procedure TFindDeclarationTool.FindUsedUnitReferences(
|
||||
const CursorPos: TCodeXYPosition; SkipComments: boolean; out
|
||||
ListOfPCodeXYPosition: TFPList);
|
||||
var
|
||||
CleanPos: integer;
|
||||
Node: TCodeTreeNode;
|
||||
UnitInFilename: string;
|
||||
AnUnitName: String;
|
||||
TargetCode: TCodeBuffer;
|
||||
TargetTool: TFindDeclarationTool;
|
||||
begin
|
||||
//debugln(['TFindDeclarationTool.FindUsedUnitReferences ',dbgs(CursorPos)]);
|
||||
ListOfPCodeXYPosition:=nil;
|
||||
BuildTreeAndGetCleanPos(CursorPos,CleanPos);
|
||||
Node:=FindDeepestNodeAtPos(CleanPos,true);
|
||||
if Node.Desc<>ctnUseUnit then
|
||||
RaiseException('This function needs the cursor at a unit in a uses clause');
|
||||
// cursor is on an used unit -> try to locate it
|
||||
MoveCursorToCleanPos(Node.StartPos);
|
||||
ReadNextAtom;
|
||||
AnUnitName:=ExtractUsedUnitNameAtCursor(@UnitInFilename);
|
||||
//debugln(['TFindDeclarationTool.FindUsedUnitReferences Used Unit=',AnUnitName,' in "',UnitInFilename,'"']);
|
||||
TargetCode:=FindUnitSource(AnUnitName,UnitInFilename,true,Node.StartPos);
|
||||
//debugln(['TFindDeclarationTool.FindUsedUnitReferences TargetCode=',TargetCode.Filename]);
|
||||
TargetTool:=FOnGetCodeToolForBuffer(Self,TargetCode,false);
|
||||
FindUsedUnitReferences(TargetTool,SkipComments,ListOfPCodeXYPosition);
|
||||
end;
|
||||
|
||||
procedure TFindDeclarationTool.FindUsedUnitReferences(
|
||||
TargetTool: TFindDeclarationTool; SkipComments: boolean; out
|
||||
ListOfPCodeXYPosition: TFPList);
|
||||
var
|
||||
refs: TFindUsedUnitReferences;
|
||||
begin
|
||||
ListOfPCodeXYPosition:=TFPList.Create;
|
||||
if TargetTool=nil then
|
||||
RaiseException('TargetTool=nil');
|
||||
TargetTool.BuildInterfaceIdentifierCache(true);
|
||||
refs:=TFindUsedUnitReferences.Create;
|
||||
try
|
||||
refs.TargetTool:=TargetTool;
|
||||
refs.TargetUnitName:=TargetTool.GetSourceName(false);
|
||||
refs.ListOfPCodeXYPosition:=ListOfPCodeXYPosition;
|
||||
ForEachIdentifier(SkipComments,@OnFindUsedUnitIdentifier,refs);
|
||||
finally
|
||||
refs.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
{-------------------------------------------------------------------------------
|
||||
function TFindDeclarationTool.CleanPosIsDeclarationIdentifier(CleanPos: integer;
|
||||
Node: TCodeTreeNode): boolean;
|
||||
@ -9444,6 +9518,64 @@ begin
|
||||
until false;
|
||||
end;
|
||||
|
||||
procedure TFindDeclarationTool.OnFindUsedUnitIdentifier(
|
||||
Sender: TPascalParserTool; IdentifierCleanPos: integer; Range: TEPRIRange;
|
||||
Node: TCodeTreeNode; Data: Pointer; var Abort: boolean);
|
||||
var
|
||||
Identifier: PChar;
|
||||
CacheEntry: PInterfaceIdentCacheEntry;
|
||||
refs: TFindUsedUnitReferences;
|
||||
Found: Boolean;
|
||||
ReferencePos: TCodeXYPosition;
|
||||
begin
|
||||
if Range=epriInDirective then exit;
|
||||
if not (Node.Desc in (AllPascalTypes+AllPascalStatements)) then exit;
|
||||
Identifier:=@Src[IdentifierCleanPos];
|
||||
refs:=TFindUsedUnitReferences(Data);
|
||||
CacheEntry:=refs.TargetTool.FInterfaceIdentifierCache.FindIdentifier(Identifier);
|
||||
//debugln(['TFindUsedUnitReferences.OnIdentifier Identifier=',GetIdentifier(Identifier),' Found=',CacheEntry<>nil]);
|
||||
if (CacheEntry=nil)
|
||||
and (CompareIdentifiers(Identifier,PChar(refs.TargetUnitName))<>0) then
|
||||
exit;
|
||||
Sender.MoveCursorToCleanPos(IdentifierCleanPos);
|
||||
Sender.ReadPriorAtom;
|
||||
if (Sender.CurPos.Flag=cafPoint) or (Sender.UpAtomIs('inherited')) then exit;
|
||||
//debugln(['TFindUsedUnitReferences.OnIdentifier Identifier=',GetIdentifier(Identifier),' at begin of term']);
|
||||
// find declaration
|
||||
if refs.Params=nil then
|
||||
refs.Params:=TFindDeclarationParams.Create
|
||||
else
|
||||
refs.Params.Clear;
|
||||
refs.Params.Flags:=[fdfSearchInParentNodes,fdfSearchInAncestors,
|
||||
fdfIgnoreCurContextNode];
|
||||
refs.Params.ContextNode:=Node;
|
||||
//debugln(copy(Src,Params.ContextNode.StartPos,200));
|
||||
refs.Params.SetIdentifier(Self,Identifier,@CheckSrcIdentifier);
|
||||
|
||||
if Range=epriInCode then begin
|
||||
// search identifier in code
|
||||
Found:=FindDeclarationOfIdentAtParam(refs.Params);
|
||||
end else begin
|
||||
// search identifier in comment -> if not found, this is no problem
|
||||
// => silently ignore
|
||||
try
|
||||
Found:=FindDeclarationOfIdentAtParam(refs.Params);
|
||||
except
|
||||
on E: ECodeToolError do begin
|
||||
// continue
|
||||
end;
|
||||
on E: Exception do
|
||||
raise;
|
||||
end;
|
||||
end;
|
||||
//debugln(['TFindUsedUnitReferences.OnIdentifier Identifier=',GetIdentifier(Identifier),' found=',Found]);
|
||||
|
||||
if not Found then exit;
|
||||
|
||||
if CleanPosToCaret(IdentifierCleanPos,ReferencePos) then
|
||||
AddCodePosition(refs.ListOfPCodeXYPosition,ReferencePos);
|
||||
end;
|
||||
|
||||
function TFindDeclarationTool.FindNthParameterNode(Node: TCodeTreeNode;
|
||||
ParameterIndex: integer): TCodeTreeNode;
|
||||
var
|
||||
|
@ -51,6 +51,16 @@ type
|
||||
);
|
||||
TPascalHintModifiers = set of TPascalHintModifier;
|
||||
|
||||
TEPRIRange = (
|
||||
epriInCode,
|
||||
epriInComment,
|
||||
epriInDirective
|
||||
);
|
||||
|
||||
TOnEachPRIdentifier = procedure(Sender: TPascalParserTool;
|
||||
IdentifierCleanPos: integer; Range: TEPRIRange;
|
||||
Node: TCodeTreeNode; Data: Pointer; var Abort: boolean) of object;
|
||||
|
||||
{ TPascalReaderTool }
|
||||
|
||||
TPascalReaderTool = class(TPascalParserTool)
|
||||
@ -75,6 +85,14 @@ type
|
||||
function ReadStringConstantValue(StartPos: integer): string;
|
||||
function GetNodeIdentifier(Node: TCodeTreeNode): PChar;
|
||||
function GetHintModifiers(Node: TCodeTreeNode): TPascalHintModifiers;
|
||||
procedure ForEachIdentifierInCleanSrc(StartPos, EndPos: integer;
|
||||
SkipComments: boolean; Node: TCodeTreeNode;
|
||||
const OnIdentifier: TOnEachPRIdentifier; Data: pointer;
|
||||
var Abort: boolean); // range in clean source
|
||||
procedure ForEachIdentifierInNode(Node: TCodeTreeNode; SkipComments: boolean;
|
||||
const OnIdentifier: TOnEachPRIdentifier; Data: Pointer; var Abort: boolean); // node and child nodes
|
||||
procedure ForEachIdentifier(SkipComments: boolean;
|
||||
const OnIdentifier: TOnEachPRIdentifier; Data: Pointer); // whole unit/program
|
||||
|
||||
// properties
|
||||
function ExtractPropType(PropNode: TCodeTreeNode;
|
||||
@ -1689,6 +1707,230 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPascalReaderTool.ForEachIdentifierInCleanSrc(StartPos,
|
||||
EndPos: integer; SkipComments: boolean; Node: TCodeTreeNode;
|
||||
const OnIdentifier: TOnEachPRIdentifier; Data: pointer; var Abort: boolean);
|
||||
var
|
||||
CommentLvl: Integer;
|
||||
InStrConst: Boolean;
|
||||
p: PChar;
|
||||
EndP: Pointer;
|
||||
Range: TEPRIRange;
|
||||
|
||||
procedure SkipIdentifier; inline;
|
||||
begin
|
||||
while (p<EndP) and IsIdentChar[p^] do inc(p);
|
||||
end;
|
||||
|
||||
begin
|
||||
//debugln(['TPascalReaderTool.ForEachIdentifierInCleanSrc Node=',Node.DescAsString,' "',dbgstr(Src,StartPos,EndPos-StartPos),'"']);
|
||||
if StartPos>SrcLen then exit;
|
||||
if EndPos>SrcLen then EndPos:=SrcLen+1;
|
||||
if StartPos>=EndPos then exit;
|
||||
p:=@Src[StartPos];
|
||||
EndP:=p+EndPos-StartPos;
|
||||
while p<EndP do begin
|
||||
case p^ of
|
||||
|
||||
'{':
|
||||
begin
|
||||
inc(p);
|
||||
if p^=#3 then begin
|
||||
// codetools skip comment {#3 #3}
|
||||
inc(p);
|
||||
repeat
|
||||
if p>=EndP then exit;
|
||||
if (p^=#3) and (p[1]='}')
|
||||
then begin
|
||||
inc(p,2);
|
||||
break;
|
||||
end;
|
||||
inc(p);
|
||||
until false;
|
||||
end else begin
|
||||
// pascal comment {}
|
||||
CommentLvl:=1;
|
||||
InStrConst:=false;
|
||||
if p^='$' then
|
||||
Range:=epriInDirective
|
||||
else
|
||||
Range:=epriInComment;
|
||||
repeat
|
||||
if p>=EndP then exit;
|
||||
case p^ of
|
||||
'{': if Scanner.NestedComments then inc(CommentLvl);
|
||||
'}':
|
||||
begin
|
||||
dec(CommentLvl);
|
||||
if CommentLvl=0 then break;
|
||||
end;
|
||||
'a'..'z','A'..'Z','_':
|
||||
if not InStrConst then begin
|
||||
if not SkipComments then begin
|
||||
OnIdentifier(Self,p-PChar(Src)+1,Range,Node,Data,Abort);
|
||||
SkipIdentifier;
|
||||
if Abort then exit;
|
||||
end;
|
||||
while (p<EndP) and IsIdentChar[p^] do inc(p);
|
||||
end;
|
||||
'''':
|
||||
InStrConst:=not InStrConst;
|
||||
#10,#13:
|
||||
InStrConst:=false;
|
||||
end;
|
||||
inc(p);
|
||||
until false;
|
||||
inc(p);
|
||||
//debugln(StartPos,' ',copy(Src,CommentStart,StartPos-CommentStart));
|
||||
end;
|
||||
end;
|
||||
|
||||
'/': // Delphi comment
|
||||
if p[1]<>'/' then begin
|
||||
inc(p);
|
||||
end else begin
|
||||
inc(p,2);
|
||||
InStrConst:=false;
|
||||
repeat
|
||||
if p>=EndP then exit;
|
||||
case p^ of
|
||||
#10,#13:
|
||||
break;
|
||||
'a'..'z','A'..'Z','_':
|
||||
if not InStrConst then begin
|
||||
if not SkipComments then begin
|
||||
OnIdentifier(Self,p-PChar(Src)+1,Range,Node,Data,Abort);
|
||||
SkipIdentifier;
|
||||
if Abort then exit;
|
||||
end;
|
||||
while (p<EndP) and IsIdentChar[p^] do inc(p);
|
||||
end;
|
||||
'''':
|
||||
InStrConst:=not InStrConst;
|
||||
end;
|
||||
inc(p);
|
||||
until false;
|
||||
inc(p);
|
||||
if (p<EndP) and (p^ in [#10,#13])
|
||||
and (p[-1]<>p^) then
|
||||
inc(p);
|
||||
end;
|
||||
|
||||
'(': // turbo pascal comment
|
||||
if (p[1]<>'*') then begin
|
||||
inc(p);
|
||||
end else begin
|
||||
inc(p,3);
|
||||
InStrConst:=false;
|
||||
repeat
|
||||
if p>=EndP then exit;
|
||||
case p^ of
|
||||
')':
|
||||
if p[-1]='*' then break;
|
||||
'a'..'z','A'..'Z','_':
|
||||
if not InStrConst then begin
|
||||
if not SkipComments then begin
|
||||
OnIdentifier(Self,p-PChar(Src)+1,Range,Node,Data,Abort);
|
||||
SkipIdentifier;
|
||||
if Abort then exit;
|
||||
end;
|
||||
SkipIdentifier;
|
||||
end;
|
||||
'''':
|
||||
InStrConst:=not InStrConst;
|
||||
#10,#13:
|
||||
InStrConst:=false;
|
||||
end;
|
||||
inc(p);
|
||||
until false;
|
||||
inc(p);
|
||||
end;
|
||||
|
||||
'a'..'z','A'..'Z','_':
|
||||
begin
|
||||
OnIdentifier(Self,p-PChar(Src)+1,epriInCode,Node,Data,Abort);
|
||||
SkipIdentifier;
|
||||
if Abort then exit;
|
||||
end;
|
||||
|
||||
'''':
|
||||
begin
|
||||
// skip string constant
|
||||
inc(p);
|
||||
while p<EndP do begin
|
||||
if (not (p^ in ['''',#10,#13])) then
|
||||
inc(p)
|
||||
else begin
|
||||
inc(p);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
else
|
||||
inc(p);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPascalReaderTool.ForEachIdentifierInNode(Node: TCodeTreeNode;
|
||||
SkipComments: boolean; const OnIdentifier: TOnEachPRIdentifier;
|
||||
Data: Pointer; var Abort: boolean);
|
||||
var
|
||||
StartPos: Integer;
|
||||
EndPos: Integer;
|
||||
Child: TCodeTreeNode;
|
||||
begin
|
||||
//debugln(['TPascalReaderTool.ForEachIdentifierInNode START ',Node.DescAsString]);
|
||||
if NodeNeedsBuildSubTree(Node) then
|
||||
BuildSubTree(Node);
|
||||
if Node.FirstChild<>nil then begin
|
||||
EndPos:=Node.StartPos;
|
||||
Child:=Node.FirstChild;
|
||||
while Child<>nil do begin
|
||||
// scan in front of child
|
||||
ForEachIdentifierInCleanSrc(EndPos,Child.StartPos,SkipComments,
|
||||
Node,OnIdentifier,Data,Abort);
|
||||
if Abort then exit;
|
||||
// scan child
|
||||
ForEachIdentifierInNode(Child,SkipComments,OnIdentifier,Data,Abort);
|
||||
if Abort then exit;
|
||||
EndPos:=Child.EndPos;
|
||||
Child:=Child.NextBrother;
|
||||
end;
|
||||
// scan behind children
|
||||
ForEachIdentifierInCleanSrc(EndPos,Node.EndPos,SkipComments,
|
||||
Node,OnIdentifier,Data,Abort);
|
||||
end else begin
|
||||
// leaf node
|
||||
StartPos:=Node.StartPos;
|
||||
EndPos:=Node.EndPos;
|
||||
// nodes without children can overlap with their NextBrother
|
||||
if (Node.NextBrother<>nil)
|
||||
and (Node.NextBrother.StartPos<EndPos) then
|
||||
EndPos:=Node.NextBrother.StartPos;
|
||||
// scan node range
|
||||
ForEachIdentifierInCleanSrc(StartPos,EndPos,SkipComments,
|
||||
Node,OnIdentifier,Data,Abort);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPascalReaderTool.ForEachIdentifier(SkipComments: boolean;
|
||||
const OnIdentifier: TOnEachPRIdentifier; Data: Pointer);
|
||||
var
|
||||
Node: TCodeTreeNode;
|
||||
Abort: boolean;
|
||||
begin
|
||||
//debugln(['TPascalReaderTool.ForEachIdentifier START']);
|
||||
Node:=Tree.Root;
|
||||
Abort:=false;
|
||||
while Node<>nil do begin
|
||||
ForEachIdentifierInNode(Node,SkipComments,OnIdentifier,Data,Abort);
|
||||
if Abort then exit;
|
||||
Node:=Node.NextBrother;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPascalReaderTool.FindVarNode(StartNode: TCodeTreeNode;
|
||||
const UpperVarName: string): TCodeTreeNode;
|
||||
var
|
||||
|
Loading…
Reference in New Issue
Block a user