added codetools cache for unit search

git-svn-id: trunk@8951 -
This commit is contained in:
mattias 2006-03-17 23:44:24 +00:00
parent 0dd09a9638
commit 663d9d28c4
8 changed files with 678 additions and 490 deletions

2
.gitattributes vendored
View File

@ -58,7 +58,7 @@ components/codetools/codetoolsstructs.pas svneol=native#text/pascal
components/codetools/codetree.pas svneol=native#text/pascal components/codetools/codetree.pas svneol=native#text/pascal
components/codetools/customcodetool.pas svneol=native#text/pascal components/codetools/customcodetool.pas svneol=native#text/pascal
components/codetools/definetemplates.pas svneol=native#text/pascal components/codetools/definetemplates.pas svneol=native#text/pascal
components/codetools/directorycache.pas svneol=native#text/plain components/codetools/directorycacher.pas svneol=native#text/plain
components/codetools/eventcodetool.pas svneol=native#text/pascal components/codetools/eventcodetool.pas svneol=native#text/pascal
components/codetools/examples/finddeclaration.lpi svneol=native#text/plain components/codetools/examples/finddeclaration.lpi svneol=native#text/plain
components/codetools/examples/finddeclaration.lpr svneol=native#text/plain components/codetools/examples/finddeclaration.lpr svneol=native#text/plain

View File

@ -19,7 +19,8 @@ uses
FindDeclarationTool, StdCodeTools, MethodJumpTool, EventCodeTool, FindDeclarationTool, StdCodeTools, MethodJumpTool, EventCodeTool,
CodeCompletionTool, LinkScanner, FindDeclarationCache, BasicCodeTools, CodeCompletionTool, LinkScanner, FindDeclarationCache, BasicCodeTools,
CodeTree, CodeAtom, SourceChanger, CodeToolMemManager, CodeCache, CodeTree, CodeAtom, SourceChanger, CodeToolMemManager, CodeCache,
KeywordFuncLists, SourceLog, ExprEval, DefineTemplates, FileProcs, CodeToolsStrConsts, KeywordFuncLists, SourceLog, ExprEval, DefineTemplates, FileProcs,
CodeToolsStrConsts, DirectoryCacher,
MultiKeyWordListTool, ResourceCodeTool, CodeToolsStructs, MultiKeyWordListTool, ResourceCodeTool, CodeToolsStructs,
// fast xml units, changes not merged in current fpc // fast xml units, changes not merged in current fpc
Laz_DOM, Laz_XMLCfg, Laz_XMLRead, Laz_XMLWrite, Laz_XMLStreaming; Laz_DOM, Laz_XMLCfg, Laz_XMLRead, Laz_XMLWrite, Laz_XMLStreaming;

View File

@ -43,7 +43,7 @@ uses
Classes, SysUtils, FileProcs, BasicCodeTools, CodeToolsStrConsts, Classes, SysUtils, FileProcs, BasicCodeTools, CodeToolsStrConsts,
EventCodeTool, CodeTree, CodeAtom, SourceChanger, DefineTemplates, CodeCache, EventCodeTool, CodeTree, CodeAtom, SourceChanger, DefineTemplates, CodeCache,
ExprEval, LinkScanner, KeywordFuncLists, TypInfo, ExprEval, LinkScanner, KeywordFuncLists, TypInfo,
DirectoryCache, AVL_Tree, LFMTrees, PascalParserTool, CodeToolsConfig, DirectoryCacher, AVL_Tree, LFMTrees, PascalParserTool, CodeToolsConfig,
CustomCodeTool, FindDeclarationTool, IdentCompletionTool, StdCodeTools, CustomCodeTool, FindDeclarationTool, IdentCompletionTool, StdCodeTools,
ResourceCodeTool, CodeToolsStructs, CodeTemplatesTool, ExtractProcTool; ResourceCodeTool, CodeToolsStructs, CodeTemplatesTool, ExtractProcTool;
@ -64,6 +64,8 @@ type
TOnFindDefineProperty = procedure(Sender: TObject; TOnFindDefineProperty = procedure(Sender: TObject;
const PersistentClassName, AncestorClassName, Identifier: string; const PersistentClassName, AncestorClassName, Identifier: string;
var IsDefined: boolean) of object; var IsDefined: boolean) of object;
ECodeToolManagerError = class(Exception);
{ TCodeToolManager } { TCodeToolManager }
@ -136,6 +138,7 @@ type
function GetOwnerForCodeTreeNode(ANode: TCodeTreeNode): TObject; function GetOwnerForCodeTreeNode(ANode: TCodeTreeNode): TObject;
function DirectoryCachePoolGetString(const ADirectory: string; function DirectoryCachePoolGetString(const ADirectory: string;
const AStringType: TCTDirCacheString): string; const AStringType: TCTDirCacheString): string;
function DirectoryCachePoolFindVirtualFile(const Filename: string): string;
public public
DefinePool: TDefinePool; // definition templates (rules) DefinePool: TDefinePool; // definition templates (rules)
DefineTree: TDefineTree; // cache for defines (e.g. initial compiler values) DefineTree: TDefineTree; // cache for defines (e.g. initial compiler values)
@ -616,6 +619,7 @@ begin
GlobalValues:=TExpressionEvaluator.Create; GlobalValues:=TExpressionEvaluator.Create;
DirectoryCachePool:=TCTDirectoryCachePool.Create; DirectoryCachePool:=TCTDirectoryCachePool.Create;
DirectoryCachePool.OnGetString:=@DirectoryCachePoolGetString; DirectoryCachePool.OnGetString:=@DirectoryCachePoolGetString;
DirectoryCachePool.OnFindVirtualFile:=@DirectoryCachePoolFindVirtualFile;
FAddInheritedCodeToOverrideMethod:=true; FAddInheritedCodeToOverrideMethod:=true;
FAdjustTopLineDueToComment:=true; FAdjustTopLineDueToComment:=true;
FCatchExceptions:=true; FCatchExceptions:=true;
@ -1017,8 +1021,12 @@ begin
// make it absolute, so the user need less string concatenations // make it absolute, so the user need less string concatenations
if FilenameIsAbsolute(Directory) then if FilenameIsAbsolute(Directory) then
Result:=CreateAbsoluteSearchPath(Result,Directory); Result:=CreateAbsoluteSearchPath(Result,Directory);
if (System.Pos('debugger',Result)>0) and (System.Pos('ide',Directory)>0) then
DebugLn('TCodeToolManager.GetCompleteSrcPathForDirectory ABSOLUTE Directory="',Directory,'" Result="',Result,'"');
// trim the paths, remove doubles and empty paths // trim the paths, remove doubles and empty paths
Result:=MinimizeSearchPath(Result); Result:=MinimizeSearchPath(Result);
if (System.Pos('debugger',Result)>0) and (System.Pos('ide',Directory)>0) then
DebugLn('TCodeToolManager.GetCompleteSrcPathForDirectory END Directory="',Directory,'" Result="',Result,'"');
end; end;
end; end;
@ -1330,6 +1338,9 @@ begin
end else if (AnException is ESourceChangeCacheError) then begin end else if (AnException is ESourceChangeCacheError) then begin
// SourceChangeCache error // SourceChangeCache error
fErrorCode:=nil; fErrorCode:=nil;
end else if (AnException is ECodeToolManagerError) then begin
// CodeToolManager error
fErrorCode:=nil;
end else begin end else begin
// unknown exception // unknown exception
DumpExceptionBackTrace; DumpExceptionBackTrace;
@ -3591,7 +3602,7 @@ begin
CreateScanner(Code); CreateScanner(Code);
if Code.Scanner=nil then begin if Code.Scanner=nil then begin
if ExceptionOnError then if ExceptionOnError then
raise Exception.CreateFmt(ctsNoScannerFound,[Code.Filename]); raise ECodeToolManagerError.CreateFmt(ctsNoScannerFound,[Code.Filename]);
exit; exit;
end; end;
Result:=TCodeTool.Create; Result:=TCodeTool.Create;
@ -3728,6 +3739,19 @@ begin
end; end;
end; end;
function TCodeToolManager.DirectoryCachePoolFindVirtualFile(
const Filename: string): string;
var
Code: TCodeBuffer;
begin
Result:='';
if (Filename='') or (System.Pos(PathDelim,Filename)>0) then
exit;
Code:=FindFile(Filename);
if Code<>nil then
Result:=Code.Filename;
end;
procedure TCodeToolManager.OnToolSetWriteLock(Lock: boolean); procedure TCodeToolManager.OnToolSetWriteLock(Lock: boolean);
begin begin
if Lock then ActivateWriteLock else DeactivateWriteLock; if Lock then ActivateWriteLock else DeactivateWriteLock;

View File

@ -54,7 +54,7 @@ unit DefineTemplates;
interface interface
uses uses
Classes, SysUtils, CodeToolsStrConsts, ExprEval, DirectoryCache, Classes, SysUtils, CodeToolsStrConsts, ExprEval, DirectoryCacher,
Laz_XMLCfg, AVL_Tree, Laz_XMLCfg, AVL_Tree,
Process, KeywordFuncLists, FileProcs; Process, KeywordFuncLists, FileProcs;

View File

@ -28,7 +28,7 @@
the same files. the same files.
} }
unit DirectoryCache; unit DirectoryCacher;
{$mode objfpc}{$H+} {$mode objfpc}{$H+}
@ -56,12 +56,23 @@ type
end; end;
TCTDirectoryUnitSources = ( TCTDirectoryUnitSources = (
ctdusUnitNormal, ctdusUnitNormal, // e.g. unitname -> filename
ctdusUnitCaseInsensitive, ctdusUnitCaseInsensitive, // unitname case insensitive -> filename
ctdusInFilenameNormal, ctdusInFilenameNormal, // unit 'in' filename -> filename
ctdusInFilenameCaseInsenstive ctdusInFilenameCaseInsenstive, // unit 'in' filename case insensitive -> filename
ctdusUnitFileNormal, // unitname.ext -> filename
ctdusUnitFileCaseInsensitive // unitname.ext case insensitive -> filename
); );
const
ctdusCaseSensitive = [ctdusUnitNormal,
ctdusInFilenameNormal,
ctdusUnitFileNormal];
ctdusCaseInsensitive = [ctdusUnitCaseInsensitive,
ctdusInFilenameCaseInsenstive,
ctdusUnitFileCaseInsensitive];
type
TCTDirCacheUnitSrcRecord = record TCTDirCacheUnitSrcRecord = record
Files: TStringToStringTree; Files: TStringToStringTree;
TimeStamp: cardinal; TimeStamp: cardinal;
@ -73,7 +84,8 @@ type
public public
TimeStamp: cardinal; TimeStamp: cardinal;
Names: PChar; // all filenames separated with #0 Names: PChar; // all filenames separated with #0
NameCount: integer; NameCount: integer; // number of filenames
NamesLength: PtrInt; // length of Names in bytes
NameStarts: PInteger; // offsets in 'Names' NameStarts: PInteger; // offsets in 'Names'
destructor Destroy; override; destructor Destroy; override;
procedure Clear; procedure Clear;
@ -99,6 +111,10 @@ type
const AValue: string); const AValue: string);
procedure ClearUnitLinks; procedure ClearUnitLinks;
procedure UpdateListing; procedure UpdateListing;
function GetUnitSourceCacheValue(const UnitSrc: TCTDirectoryUnitSources;
const Search: string; var Filename: string): boolean;
procedure AddToCache(const UnitSrc: TCTDirectoryUnitSources;
const Search, Filename: string);
public public
constructor Create(const TheDirectory: string; constructor Create(const TheDirectory: string;
ThePool: TCTDirectoryCachePool); ThePool: TCTDirectoryCachePool);
@ -108,8 +124,14 @@ type
function FindUnitLink(const UnitName: string): string; function FindUnitLink(const UnitName: string): string;
function FindFile(const ShortFilename: string; function FindFile(const ShortFilename: string;
const FileCase: TCTSearchFileCase): string; const FileCase: TCTSearchFileCase): string;
function FindUnitSource(var UnitName, InFilename: string; function FindUnitSource(const UnitName: string; AnyCase: boolean): string;
AnyCase: boolean): string; function FindUnitSourceInCleanSearchPath(const Unitname,
SearchPath: string; AnyCase: boolean): string;
function FindUnitSourceInCompletePath(var UnitName, InFilename: string;
AnyCase: boolean): string;
function FindCompiledUnitInCompletePath(var ShortFilename: string;
AnyCase: boolean): string;
procedure WriteListing;
public public
property Directory: string read FDirectory; property Directory: string read FDirectory;
property RefCount: integer read FRefCount; property RefCount: integer read FRefCount;
@ -122,9 +144,11 @@ type
TCTDirCacheGetString = function(const ADirectory: string; TCTDirCacheGetString = function(const ADirectory: string;
const AStringType: TCTDirCacheString const AStringType: TCTDirCacheString
): string of object; ): string of object;
TCTDirCacheFindVirtualFile = function(const Filename: string): string of object;
TCTDirectoryCachePool = class TCTDirectoryCachePool = class
private private
FOnFindVirtualFile: TCTDirCacheFindVirtualFile;
FOnGetString: TCTDirCacheGetString; FOnGetString: TCTDirCacheGetString;
FTimeStamp: cardinal; FTimeStamp: cardinal;
FDirectories: TAVLTree; FDirectories: TAVLTree;
@ -140,8 +164,15 @@ type
UseCache: boolean = true): string; UseCache: boolean = true): string;
procedure IncreaseTimeStamp; procedure IncreaseTimeStamp;
function FindUnitInUnitLinks(const Directory, UnitName: string): string; function FindUnitInUnitLinks(const Directory, UnitName: string): string;
function FindDiskFilename(const Filename: string): string;
function FindUnitInDirectory(const Directory, UnitName: string;
AnyCase: boolean = false): string;
function FindVirtualFile(const Filename: string): string;
function FindVirtualUnit(const UnitName: string): string;
property TimeStamp: cardinal read FTimeStamp; property TimeStamp: cardinal read FTimeStamp;
property OnGetString: TCTDirCacheGetString read FOnGetString write FOnGetString; property OnGetString: TCTDirCacheGetString read FOnGetString write FOnGetString;
property OnFindVirtualFile: TCTDirCacheFindVirtualFile read FOnFindVirtualFile
write FOnFindVirtualFile;
end; end;
function CompareCTDirectoryCaches(Data1, Data2: Pointer): integer; function CompareCTDirectoryCaches(Data1, Data2: Pointer): integer;
@ -377,63 +408,111 @@ begin
FListing:=TCTDirectoryListing.Create; FListing:=TCTDirectoryListing.Create;
FListing.Clear; FListing.Clear;
FListing.TimeStamp:=Pool.TimeStamp; FListing.TimeStamp:=Pool.TimeStamp;
if Directory='' then exit;// virtual directory
// read the directory // read the directory
WorkingListing:=nil; WorkingListing:=nil;
WorkingListingCapacity:=0; WorkingListingCapacity:=0;
WorkingListingCount:=0; WorkingListingCount:=0;
if SysUtils.FindFirst(Directory+FileMask,faAnyFile,FileInfo)=0 then begin try
repeat if SysUtils.FindFirst(Directory+FileMask,faAnyFile,FileInfo)=0 then begin
// check if special file repeat
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='') // check if special file
then if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
continue; then
// add file continue;
if WorkingListingCount=WorkingListingCapacity then begin // add file
// grow WorkingListing if WorkingListingCount=WorkingListingCapacity then begin
if WorkingListingCapacity>0 then // grow WorkingListing
NewCapacity:=WorkingListingCapacity*2 if WorkingListingCapacity>0 then
else NewCapacity:=WorkingListingCapacity*2
NewCapacity:=8; else
ReAllocMem(WorkingListing,SizeOf(Pointer)*NewCapacity); NewCapacity:=8;
FillChar(WorkingListing[WorkingListingCount], ReAllocMem(WorkingListing,SizeOf(Pointer)*NewCapacity);
SizeOf(Pointer)*(NewCapacity-WorkingListingCapacity),0); FillChar(WorkingListing[WorkingListingCount],
WorkingListingCapacity:=NewCapacity; SizeOf(Pointer)*(NewCapacity-WorkingListingCapacity),0);
end; WorkingListingCapacity:=NewCapacity;
WorkingListing[WorkingListingCount]:=FileInfo.Name; end;
inc(WorkingListingCount); WorkingListing[WorkingListingCount]:=FileInfo.Name;
until SysUtils.FindNext(FileInfo)<>0; inc(WorkingListingCount);
end; until SysUtils.FindNext(FileInfo)<>0;
SysUtils.FindClose(FileInfo);
if WorkingListingCount=0 then exit;
// sort the files
MergeSort(PPointer(WorkingListing),WorkingListingCount,
@ComparePCharFirstCaseInsThenCase);
// create listing
TotalLen:=0;
for i:=0 to WorkingListingCount-1 do
inc(TotalLen,length(WorkingListing[i])+1);
GetMem(FListing.Names,TotalLen);
FListing.NameCount:=WorkingListingCount;
p:=0;
for i:=0 to WorkingListingCount-1 do begin
CurFilenameLen:=length(WorkingListing[i]);
if CurFilenameLen>0 then begin
System.Move(WorkingListing[i][1],FListing.Names[p],CurFilenameLen);
inc(p,CurFilenameLen);
end; end;
FListing.Names[p]:=#0; SysUtils.FindClose(FileInfo);
inc(p);
if WorkingListingCount=0 then exit;
// sort the files
MergeSort(PPointer(WorkingListing),WorkingListingCount,
@ComparePCharFirstCaseInsThenCase);
// create listing
TotalLen:=0;
for i:=0 to WorkingListingCount-1 do
inc(TotalLen,length(WorkingListing[i])+1);
GetMem(FListing.Names,TotalLen);
FListing.NamesLength:=TotalLen;
FListing.NameCount:=WorkingListingCount;
GetMem(FListing.NameStarts,SizeOf(PChar)*WorkingListingCount);
p:=0;
for i:=0 to WorkingListingCount-1 do begin
CurFilenameLen:=length(WorkingListing[i]);
if CurFilenameLen>0 then begin
FListing.NameStarts[i]:=p;
System.Move(WorkingListing[i][1],FListing.Names[p],CurFilenameLen);
inc(p,CurFilenameLen);
end;
FListing.Names[p]:=#0;
inc(p);
end;
finally
for i:=0 to WorkingListingCount-1 do
WorkingListing[i]:='';
ReAllocMem(WorkingListing,0);
end; end;
end; end;
function TCTDirectoryCache.GetUnitSourceCacheValue(
const UnitSrc: TCTDirectoryUnitSources; const Search: string;
var Filename: string): boolean;
var
Files: TStringToStringTree;
begin
Files:=FUnitSources[UnitSrc].Files;
if (FUnitSources[UnitSrc].TimeStamp<>Pool.TimeStamp) then begin
// cache is invalid -> clear to make it valid
if Files<>nil then
Files.Clear;
FUnitSources[UnitSrc].TimeStamp:=Pool.TimeStamp;
Result:=false;
end else begin
// cache is valid
if Files<>nil then begin
Result:=Files.GetString(Search,Filename);
end else begin
Result:=false;
end;
end;
end;
procedure TCTDirectoryCache.AddToCache(const UnitSrc: TCTDirectoryUnitSources;
const Search, Filename: string);
var
Files: TStringToStringTree;
begin
Files:=FUnitSources[UnitSrc].Files;
if Files=nil then begin
Files:=TStringToStringTree.Create(UnitSrc in ctdusCaseSensitive);
FUnitSources[UnitSrc].Files:=Files;
end;
Files[Search]:=Filename;
end;
constructor TCTDirectoryCache.Create(const TheDirectory: string; constructor TCTDirectoryCache.Create(const TheDirectory: string;
ThePool: TCTDirectoryCachePool); ThePool: TCTDirectoryCachePool);
begin begin
FDirectory:=AppendPathDelim(TrimFilename(TheDirectory)); FDirectory:=AppendPathDelim(TrimFilename(TheDirectory));
if (FDirectory<>'') and not FilenameIsAbsolute(FDirectory) then
raise Exception.Create('directory not absolute');
FPool:=ThePool; FPool:=ThePool;
FRefCount:=1; FRefCount:=1;
end; end;
@ -515,82 +594,158 @@ var
cmp: LongInt; cmp: LongInt;
CurFilename: PChar; CurFilename: PChar;
begin begin
if ShortFilename='' then exit('');
UpdateListing;
Result:=''; Result:='';
if (FListing.Names=nil) then exit; if ShortFilename='' then exit;
l:=0; if Directory<>'' then begin
r:=FListing.NameCount-1; UpdateListing;
while r>=l do begin if (FListing.Names=nil) then exit;
m:=(l+r) shr 1; l:=0;
CurFilename:=@FListing.Names[FListing.NameStarts[m]]; r:=FListing.NameCount-1;
case FileCase of while r>=l do begin
ctsfcDefault: m:=(l+r) shr 1;
{$IFDEF CaseInsensitiveFilenames} CurFilename:=@FListing.Names[FListing.NameStarts[m]];
cmp:=stricomp(PChar(ShortFilename),CurFilename); case FileCase of
{$ELSE} ctsfcDefault:
cmp:=strcomp(PChar(ShortFilename),CurFilename); {$IFDEF CaseInsensitiveFilenames}
{$ENDIF} cmp:=stricomp(PChar(ShortFilename),CurFilename);
ctsfcAllCase,ctsfcLoUpCase: {$ELSE}
cmp:=stricomp(PChar(ShortFilename),CurFilename); cmp:=strcomp(PChar(ShortFilename),CurFilename);
else RaiseDontKnow; {$ENDIF}
end; ctsfcAllCase,ctsfcLoUpCase:
if cmp>0 then cmp:=stricomp(PChar(ShortFilename),CurFilename);
l:=m else RaiseDontKnow;
else if cmp<0 then end;
r:=m if cmp>0 then
else begin l:=m+1
Result:=CurFilename; else if cmp<0 then
exit; r:=m-1
else begin
Result:=CurFilename;
exit;
end;
end; end;
end else begin
// this is a virtual directory
Result:=Pool.FindVirtualFile(ShortFilename);
end; end;
end; end;
function TCTDirectoryCache.FindUnitSource(var UnitName, InFilename: string; function TCTDirectoryCache.FindUnitSource(const UnitName: string;
AnyCase: boolean): string; AnyCase: boolean): string;
var var
UnitSrc: TCTDirectoryUnitSources; l: Integer;
r: Integer;
m: Integer;
cmp: LongInt;
CurFilename: PChar;
CurFilenameLen: LongInt;
begin
Result:='';
//DebugLn('TCTDirectoryCache.FindUnitSource UnitName="',Unitname,'" AnyCase=',dbgs(AnyCase),' Directory=',Directory);
if UnitName='' then exit;
if Directory<>'' then begin
UpdateListing;
if (FListing.Names=nil) then exit;
l:=0;
r:=FListing.NameCount-1;
while r>=l do begin
m:=(l+r) shr 1;
CurFilename:=@FListing.Names[FListing.NameStarts[m]];
cmp:=stricomp(PChar(UnitName),CurFilename);
if cmp>0 then
l:=m+1
else if cmp<0 then
r:=m-1
else
break;
end;
// now all files above m are higher than the Unitname
// -> check that m is equal or above
if (m>0) and (Cmp>0) then
inc(m);
// now all files below m are lower than the Unitname
// -> now find a filename with correct case and extension
while m<FListing.NameCount do begin
CurFilename:=@FListing.Names[FListing.NameStarts[m]];
CurFilenameLen:=strlen(CurFilename);
function GetUnitSourceCacheValue(const Search: string; // check if the filename prefix is the unitname
var Filename: string): boolean; // if not, then all filenames are not compatible as well
var if CurFilenameLen<length(UnitName) then break;
Files: TStringToStringTree; if strlicomp(CurFilename,PChar(Unitname),length(UnitName))<>0 then break;
begin
Files:=FUnitSources[UnitSrc].Files; // check if the filename fits
if (FUnitSources[UnitSrc].TimeStamp<>Pool.TimeStamp) then begin if (CompareFilenameOnly(CurFilename,CurFilenameLen,
// cache is invalid -> clear to make it valid PChar(UnitName),length(UnitName),false)=0)
if Files<>nil then and FilenameIsPascalUnit(CurFilename,CurFilenameLen,false)
Files.Clear; then begin
FUnitSources[UnitSrc].TimeStamp:=Pool.TimeStamp; // the unitname is ok and the extension is ok
Result:=false; Result:=CurFilename;
end else begin if AnyCase then begin
// cache is valid exit;
if Files<>nil then begin end else begin
Result:=Files.GetString(Search,Filename); // check case
end else begin if (Result=lowercase(Result))
Result:=false; or (Result=uppercase(Result))
or (ExtractFileNameOnly(Result)=UnitName) then
exit;
end;
end; end;
inc(m);
end; end;
end else begin
// this is a virtual directory
Result:=Pool.FindVirtualUnit(UnitName);
if Result<>'' then exit;
end; end;
Result:='';
procedure AddToCache(const Search, Filename: string); end;
var
Files: TStringToStringTree; function TCTDirectoryCache.FindUnitSourceInCleanSearchPath(const Unitname,
begin SearchPath: string; AnyCase: boolean): string;
Files:=FUnitSources[UnitSrc].Files;
if Files=nil then begin
Files:=TStringToStringTree.Create(not AnyCase);
FUnitSources[UnitSrc].Files:=Files;
end;
Files[Search]:=Filename;
end;
var var
p, StartPos, l: integer;
CurPath: string;
IsAbsolute: Boolean;
begin
//if (CompareText(Unitname,'UnitDependencies')=0) then
// DebugLn('TCTDirectoryCache.FindUnitSourceInCleanSearchPath UnitName="',Unitname,'" SearchPath="',SearchPath,'"');
StartPos:=1;
l:=length(SearchPath);
while StartPos<=l do begin
p:=StartPos;
while (p<=l) and (SearchPath[p]<>';') do inc(p);
CurPath:=Trim(copy(SearchPath,StartPos,p-StartPos));
if CurPath<>'' then begin
IsAbsolute:=FilenameIsAbsolute(CurPath);
if (not IsAbsolute) and (Directory<>'') then begin
CurPath:=Directory+CurPath;
IsAbsolute:=true;
end;
//DebugLn('TCTDirectoryCache.FindUnitSourceInCleanSearchPath CurPath="',CurPath,'"');
if IsAbsolute then begin
CurPath:=AppendPathDelim(CurPath);
Result:=Pool.FindUnitInDirectory(CurPath,UnitName,AnyCase);
end else if (CurPath='.') and (Directory='') then begin
Result:=Pool.FindVirtualUnit(Unitname);
end;
if Result<>'' then exit;
end;
StartPos:=p+1;
end;
Result:='';
end;
function TCTDirectoryCache.FindUnitSourceInCompletePath(
var UnitName, InFilename: string; AnyCase: boolean): string;
var
UnitSrc: TCTDirectoryUnitSources;
CurDir: String; CurDir: String;
SrcPath: string; SrcPath: string;
NewUnitName: String; NewUnitName: String;
SearchCase: TCTSearchFileCase;
begin begin
Result:='';
//DebugLn('TCTDirectoryCache.FindUnitSourceInCompletePath UnitName="',Unitname,'" InFilename="',InFilename,'"');
if InFilename<>'' then begin if InFilename<>'' then begin
// uses IN parameter // uses IN parameter
InFilename:=TrimFilename(SetDirSeparators(InFilename)); InFilename:=TrimFilename(SetDirSeparators(InFilename));
@ -598,7 +753,7 @@ begin
UnitSrc:=ctdusInFilenameCaseInsenstive UnitSrc:=ctdusInFilenameCaseInsenstive
else else
UnitSrc:=ctdusInFilenameNormal; UnitSrc:=ctdusInFilenameNormal;
if GetUnitSourceCacheValue(InFilename,Result) then begin if GetUnitSourceCacheValue(UnitSrc,InFilename,Result) then begin
// found in cache // found in cache
if Result<>'' then begin if Result<>'' then begin
// unit found // unit found
@ -610,21 +765,22 @@ begin
end else begin end else begin
// not found in cache -> search // not found in cache -> search
if FilenameIsAbsolute(InFilename) then begin if FilenameIsAbsolute(InFilename) then begin
// absolute filename
if AnyCase then if AnyCase then
Result:=FindDiskFilename(InFilename) Result:=Pool.FindDiskFilename(InFilename)
else else
Result:=InFilename; Result:=InFilename;
if FileExistsCached(Result) then if FileExistsCached(Result) then
InFilename:=Result InFilename:=CreateRelativePath(Result,Directory)
else else
Result:=''; Result:='';
end else begin end else begin
// file is relative to current directory // 'in'-filename has no complete path
// -> search file in current directory // -> search file relative to current directory
CurDir:=Directory; CurDir:=Directory;
if CurDir<>'' then begin if CurDir<>'' then begin
if AnyCase then if AnyCase then
Result:=SearchFileInDir(InFilename,CurDir,ctsfcAllCase) Result:=Pool.FindDiskFilename(CurDir+InFilename)
else else
Result:=TrimFilename(CurDir+InFilename); Result:=TrimFilename(CurDir+InFilename);
if FileExistsCached(Result) then begin if FileExistsCached(Result) then begin
@ -633,11 +789,12 @@ begin
Result:=''; Result:='';
end; end;
end else begin end else begin
// virtual directory -> TODO // this is a virtual directory -> search virtual unit
Result:=''; InFilename:=Pool.FindVirtualFile(InFilename);
Result:=InFilename;
end; end;
end; end;
AddToCache(InFilename,Result); AddToCache(UnitSrc,InFilename,Result);
end; end;
end else begin end else begin
// normal unit name // normal unit name
@ -646,7 +803,7 @@ begin
UnitSrc:=ctdusUnitCaseInsensitive UnitSrc:=ctdusUnitCaseInsensitive
else else
UnitSrc:=ctdusUnitNormal; UnitSrc:=ctdusUnitNormal;
if GetUnitSourceCacheValue(UnitName,Result) then begin if GetUnitSourceCacheValue(UnitSrc,UnitName,Result) then begin
// found in cache // found in cache
if Result<>'' then begin if Result<>'' then begin
// unit found // unit found
@ -654,42 +811,89 @@ begin
// unit not found // unit not found
end; end;
end else begin end else begin
// not found in cache -> search // not found in cache -> search in complete source path
// search in unit, src and compiled src path
SrcPath:=Strings[ctdcsCompleteSrcPath]; SrcPath:=Strings[ctdcsCompleteSrcPath];
if SysUtils.CompareText(UnitName,'Forms')=0 then begin
DebugLn('============================================================== '); // search in search path
DebugLn('TFindDeclarationTool.FindUnitCaseInsensitive ',SrcPath); Result:=FindUnitSourceInCleanSearchPath(UnitName,SrcPath,AnyCase);
if Result='' then begin
// search in unit links
Result:=FindUnitLink(UnitName);
end;
if Result<>'' then begin
NewUnitName:=ExtractFileNameOnly(Result);
if (NewUnitName<>lowercase(NewUnitName))
and (UnitName<>NewUnitName) then
UnitName:=NewUnitName;
end; end;
CurDir:=Directory; AddToCache(UnitSrc,UnitName,Result);
if CurDir<>'' then begin
// search in search path
if AnyCase then
SearchCase:=ctsfcAllCase
else
SearchCase:=ctsfcLoUpCase;
Result:=SearchPascalUnitInPath(UnitName,CurDir,SrcPath,';',SearchCase);
if Result='' then begin
// search in unit links
Result:=FindUnitLink(UnitName);
end;
if Result<>'' then begin
NewUnitName:=ExtractFileNameOnly(Result);
if (NewUnitName<>lowercase(NewUnitName))
and (UnitName<>NewUnitName) then
UnitName:=NewUnitName;
end;
end else begin
// virtual directory -> TODO
Result:='';
end;
AddToCache(UnitName,Result);
end; end;
end; end;
//DebugLn('TFindDeclarationTool.FindUnitCaseInsensitive RESULT AnUnitName=',AnUnitName,' InFilename=',InFilename,' Result=',Result); //DebugLn('TCTDirectoryCache.FindUnitSourceInCompletePath RESULT UnitName="',UnitName,'" InFilename="',InFilename,'" Result=',Result);
end;
function TCTDirectoryCache.FindCompiledUnitInCompletePath(
var ShortFilename: string; AnyCase: boolean): string;
var
UnitPath: string;
NewShortFilename: String;
UnitSrc: TCTDirectoryUnitSources;
CurDir: String;
SearchCase: TCTSearchFileCase;
begin
Result:='';
if AnyCase then
UnitSrc:=ctdusUnitFileCaseInsensitive
else
UnitSrc:=ctdusUnitFileNormal;
if GetUnitSourceCacheValue(UnitSrc,ShortFilename,Result) then begin
// found in cache
if Result<>'' then begin
// unit found
end else begin
// unit not found
end;
end else begin
// not found in cache -> search
// search in unit, src and compiled src path
UnitPath:=Strings[ctdcsUnitPath];
CurDir:=Directory;
if CurDir<>'' then begin
// search in search path
if AnyCase then
SearchCase:=ctsfcAllCase
else
SearchCase:=ctsfcLoUpCase;
Result:=SearchPascalFileInPath(ShortFilename,CurDir,UnitPath,';',SearchCase);
if Result<>'' then begin
NewShortFilename:=ExtractFileName(Result);
if (NewShortFilename<>lowercase(NewShortFilename))
and (ShortFilename<>NewShortFilename) then
ShortFilename:=NewShortFilename;
end;
end else begin
// virtual directory -> TODO
Result:='';
end;
AddToCache(UnitSrc,ShortFilename,Result);
end;
end;
procedure TCTDirectoryCache.WriteListing;
var
i: Integer;
Filename: PChar;
begin
writeln('TCTDirectoryCache.WriteListing Count=',FListing.NameCount,' TextLen=',FListing.NamesLength);
for i:=0 to FListing.NameCount-1 do begin
Filename:=@FListing.Names[FListing.NameStarts[i]];
writeln(i,' "',Filename,'"');
end;
end; end;
{ TCTDirectoryCachePool } { TCTDirectoryCachePool }
@ -783,6 +987,53 @@ begin
Result:=Cache.FindUnitLink(UnitName); Result:=Cache.FindUnitLink(UnitName);
end; end;
function TCTDirectoryCachePool.FindDiskFilename(const Filename: string
): string;
var
ADirectory: String;
Cache: TCTDirectoryCache;
ShortFilename: String;
begin
Result:=TrimFilename(Filename);
ADirectory:=ExtractFilePath(Result);
Cache:=GetCache(ADirectory,true,false);
ShortFilename:=ExtractFileName(Result);
Result:=Cache.FindFile(ShortFilename,ctsfcAllCase);
if Result='' then exit;
Result:=Cache.Directory+Result;
end;
function TCTDirectoryCachePool.FindUnitInDirectory(const Directory,
UnitName: string; AnyCase: boolean): string;
var
Cache: TCTDirectoryCache;
begin
Cache:=GetCache(Directory,true,false);
Result:=Cache.FindUnitSource(UnitName,AnyCase);
if Result='' then exit;
Result:=Cache.Directory+Result;
end;
function TCTDirectoryCachePool.FindVirtualFile(const Filename: string): string;
begin
if Assigned(OnFindVirtualFile) then
Result:=OnFindVirtualFile(Filename)
else
Result:='';
end;
function TCTDirectoryCachePool.FindVirtualUnit(const UnitName: string): string;
var
e: TCTPascalExtType;
begin
for e:=Low(CTPascalExtension) to High(CTPascalExtension) do begin
if CTPascalExtension[e]='' then continue;
Result:=FindVirtualFile(UnitName+CTPascalExtension[e]);
if Result<>'' then exit;
end;
Result:='';
end;
{ TCTDirectoryListing } { TCTDirectoryListing }
destructor TCTDirectoryListing.Destroy; destructor TCTDirectoryListing.Destroy;
@ -796,6 +1047,7 @@ begin
if NameStarts<>nil then begin if NameStarts<>nil then begin
FreeMem(NameStarts); FreeMem(NameStarts);
NameStarts:=nil; NameStarts:=nil;
NamesLength:=0;
FreeMem(Names); FreeMem(Names);
Names:=nil; Names:=nil;
NameCount:=0; NameCount:=0;

View File

@ -63,7 +63,7 @@ type
function CompareFilenames(const Filename1, Filename2: string): integer; function CompareFilenames(const Filename1, Filename2: string): integer;
function CompareFileExt(const Filename, Ext: string; function CompareFileExt(const Filename, Ext: string;
CaseSensitive: boolean): integer; CaseSensitive: boolean): integer;
function DirPathExists(DirectoryName: string): boolean; function DirPathExists(DirectoryName: string): boolean;
function DirectoryIsWritable(const DirectoryName: string): boolean; function DirectoryIsWritable(const DirectoryName: string): boolean;
function ExtractFileNameOnly(const AFilename: string): string; function ExtractFileNameOnly(const AFilename: string): string;
@ -97,14 +97,25 @@ function GetFilenameOnDisk(const AFilename: string): string;
function FindDiskFilename(const Filename: string): string; function FindDiskFilename(const Filename: string): string;
function CompareAnsiStringFilenames(Data1, data2: Pointer): integer; function CompareAnsiStringFilenames(Data1, data2: Pointer): integer;
function CompareFilenameOnly(Filename: PChar; FilenameLen: integer;
NameOnly: PChar; NameOnlyLen: integer; CaseSensitive: boolean): integer;
// searching .pas, .pp, .p
function FilenameIsPascalUnit(const Filename: string; function FilenameIsPascalUnit(const Filename: string;
CaseSensitive: boolean = false): boolean; CaseSensitive: boolean = false): boolean;
function FilenameIsPascalUnit(Filename: PChar; FilenameLen: integer;
CaseSensitive: boolean = false): boolean;
function SearchPascalUnitInDir(const AnUnitName, BaseDirectory: string; function SearchPascalUnitInDir(const AnUnitName, BaseDirectory: string;
SearchCase: TCTSearchFileCase): string; SearchCase: TCTSearchFileCase): string;
function SearchPascalUnitInPath(const AnUnitName, BasePath, SearchPath, function SearchPascalUnitInPath(const AnUnitName, BasePath, SearchPath,
Delimiter: string; SearchCase: TCTSearchFileCase): string; Delimiter: string; SearchCase: TCTSearchFileCase): string;
// searching .ppu
function SearchPascalFileInDir(const ShortFilename, BaseDirectory: string;
SearchCase: TCTSearchFileCase): string;
function SearchPascalFileInPath(const ShortFilename, BasePath, SearchPath,
Delimiter: string; SearchCase: TCTSearchFileCase): string;
function CreateAbsoluteSearchPath(const SearchPath, BaseDirectory: string): string; function CreateAbsoluteSearchPath(const SearchPath, BaseDirectory: string): string;
function CreateRelativeSearchPath(const SearchPath, BaseDirectory: string): string; function CreateRelativeSearchPath(const SearchPath, BaseDirectory: string): string;
function MinimizeSearchPath(const SearchPath: string): string; function MinimizeSearchPath(const SearchPath: string): string;
@ -439,6 +450,44 @@ begin
Pointer(s2):=nil; Pointer(s2):=nil;
end; end;
function CompareFilenameOnly(Filename: PChar; FilenameLen: integer;
NameOnly: PChar; NameOnlyLen: integer; CaseSensitive: boolean): integer;
// compare only the filename (without extension and path)
var
EndPos: integer;
StartPos: LongInt;
p: Integer;
l: LongInt;
FilenameOnlyLen: Integer;
begin
StartPos:=FilenameLen;
while (StartPos>0) and (Filename[StartPos-1]<>PathDelim) do dec(StartPos);
EndPos:=FilenameLen;
while (EndPos>StartPos) and (Filename[EndPos]<>'.') do dec(EndPos);
if (EndPos=StartPos) and (EndPos<FilenameLen) and (Filename[EndPos]<>'.') then
EndPos:=FilenameLen;
FilenameOnlyLen:=EndPos-StartPos;
l:=FilenameOnlyLen;
if l>NameOnlyLen then
l:=NameOnlyLen;
//DebugLn('CompareFilenameOnly NameOnly="',copy(NameOnly,1,NameOnlyLen),'" FilenameOnly="',copy(Filename,StartPos,EndPos-StartPos),'"');
p:=0;
if CaseSensitive then begin
while p<l do begin
Result:=ord(Filename[StartPos+p])-ord(NameOnly[p]);
if Result<>0 then exit;
inc(p);
end;
end else begin
while p<l do begin
Result:=ord(FPUpChars[Filename[StartPos+p]])-ord(FPUpChars[NameOnly[p]]);
if Result<>0 then exit;
inc(p);
end;
end;
Result:=FilenameOnlyLen-NameOnlyLen;
end;
function CompareFilenames(const Filename1, Filename2: string): integer; function CompareFilenames(const Filename1, Filename2: string): integer;
begin begin
{$IFDEF CaseInsensitiveFilenames} {$IFDEF CaseInsensitiveFilenames}
@ -922,6 +971,41 @@ begin
Result:=false; Result:=false;
end; end;
function FilenameIsPascalUnit(Filename: PChar; FilenameLen: integer;
CaseSensitive: boolean): boolean;
var
StartPos: LongInt;
ExtLen: Integer;
e: TCTPascalExtType;
i: Integer;
p: PChar;
begin
StartPos:=FilenameLen-1;
while (StartPos>=0) and (Filename[StartPos]<>'.') do dec(StartPos);
if StartPos<0 then exit(false);
ExtLen:=FilenameLen-StartPos;
for e:=Low(CTPascalExtension) to High(CTPascalExtension) do begin
if (CTPascalExtension[e]='') or (length(CTPascalExtension[e])<>ExtLen) then
continue;
i:=0;
p:=PChar(CTPascalExtension[e]);
if CaseSensitive then begin
while (i<ExtLen) and (p^=Filename[StartPos+i]) do begin
inc(i);
inc(p);
end;
end else begin
while (i<ExtLen) and (FPUpChars[p^]=FPUpChars[Filename[StartPos+i]]) do
begin
inc(i);
inc(p);
end;
end;
if i=ExtLen then exit(true);
end;
Result:=false;
end;
function SearchPascalUnitInDir(const AnUnitName, BaseDirectory: string; function SearchPascalUnitInDir(const AnUnitName, BaseDirectory: string;
SearchCase: TCTSearchFileCase): string; SearchCase: TCTSearchFileCase): string;
@ -933,7 +1017,9 @@ function SearchPascalUnitInDir(const AnUnitName, BaseDirectory: string;
var var
Base: String; Base: String;
FileInfo: TSearchRec; FileInfo: TSearchRec;
CurExt: String; LowerCaseUnitname: String;
UpperCaseUnitname: String;
CurUnitName: String;
begin begin
Base:=AppendPathDelim(BaseDirectory); Base:=AppendPathDelim(BaseDirectory);
Base:=TrimFilename(Base); Base:=TrimFilename(Base);
@ -941,6 +1027,15 @@ begin
Result:=''; Result:='';
if SearchCase=ctsfcAllCase then if SearchCase=ctsfcAllCase then
Base:=FindDiskFilename(Base); Base:=FindDiskFilename(Base);
if SearchCase in [ctsfcDefault,ctsfcLoUpCase] then begin
LowerCaseUnitname:=lowercase(AnUnitName);
UpperCaseUnitname:=uppercase(AnUnitName);
end else begin
LowerCaseUnitname:='';
UpperCaseUnitname:='';
end;
if SysUtils.FindFirst(Base+FileMask,faAnyFile,FileInfo)=0 then if SysUtils.FindFirst(Base+FileMask,faAnyFile,FileInfo)=0 then
begin begin
repeat repeat
@ -949,25 +1044,31 @@ begin
then then
continue; continue;
if not FilenameIsPascalUnit(FileInfo.Name,false) then continue; if not FilenameIsPascalUnit(FileInfo.Name,false) then continue;
CurExt:=ExtractFileExt(FileInfo.Name);
case SearchCase of case SearchCase of
ctsfcDefault,ctsfcLoUpCase: ctsfcDefault,ctsfcLoUpCase:
begin if (CompareFilenameOnly(PChar(FileInfo.Name),length(FileInfo.Name),
if (AnUnitName+lowercase(CurExt)=FileInfo.Name) PChar(AnUnitName),length(AnUnitName),false)=0)
or (lowercase(AnUnitName+CurExt)=FileInfo.Name) then begin
or (uppercase(AnUnitName+CurExt)=FileInfo.Name) CurUnitName:=ExtractFilePath(FileInfo.Name);
then begin if CurUnitName=AnUnitName then begin
Result:=FileInfo.Name;
break;
end else if ((LowerCaseUnitname=CurUnitName)
or (UpperCaseUnitname=CurUnitName)) then begin
Result:=FileInfo.Name; Result:=FileInfo.Name;
if AnUnitName+CurExt=FileInfo.Name then break;
end; end;
end; end;
ctsfcAllCase: ctsfcAllCase:
begin if (CompareFilenameOnly(PChar(FileInfo.Name),length(FileInfo.Name),
if CompareText(AnUnitName+CurExt,FileInfo.Name)=0 then begin PChar(AnUnitName),length(AnUnitName),true)=0)
Result:=FileInfo.Name; then begin
if AnUnitName+CurExt=FileInfo.Name then break; Result:=FileInfo.Name;
end; CurUnitName:=ExtractFilePath(FileInfo.Name);
if CurUnitName=AnUnitName then
break;
end; end;
else else
RaiseNotImplemented; RaiseNotImplemented;
end; end;
@ -1006,6 +1107,94 @@ begin
Result:=''; Result:='';
end; end;
function SearchPascalFileInDir(const ShortFilename, BaseDirectory: string;
SearchCase: TCTSearchFileCase): string;
procedure RaiseNotImplemented;
begin
raise Exception.Create('not implemented');
end;
var
Base: String;
FileInfo: TSearchRec;
LowerCaseFilename: string;
UpperCaseFilename: string;
begin
Base:=AppendPathDelim(BaseDirectory);
Base:=TrimFilename(Base);
// search file
Result:='';
if SearchCase=ctsfcAllCase then
Base:=FindDiskFilename(Base);
if SearchCase in [ctsfcDefault,ctsfcLoUpCase] then begin
LowerCaseFilename:=lowercase(ShortFilename);
UpperCaseFilename:=uppercase(ShortFilename);
end else begin
LowerCaseFilename:='';
UpperCaseFilename:='';
end;
if SysUtils.FindFirst(Base+FileMask,faAnyFile,FileInfo)=0 then
begin
repeat
// check if special file
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
then
continue;
case SearchCase of
ctsfcDefault,ctsfcLoUpCase:
if (ShortFilename=FileInfo.Name) then begin
Result:=FileInfo.Name;
break;
end else if (LowerCaseFilename=FileInfo.Name)
or (UpperCaseFilename=FileInfo.Name)
then
Result:=FileInfo.Name;
ctsfcAllCase:
if CompareText(ShortFilename,FileInfo.Name)=0 then begin
Result:=FileInfo.Name;
if ShortFilename=FileInfo.Name then break;
end;
else
RaiseNotImplemented;
end;
until SysUtils.FindNext(FileInfo)<>0;
end;
SysUtils.FindClose(FileInfo);
if Result<>'' then Result:=Base+Result;
end;
function SearchPascalFileInPath(const ShortFilename, BasePath, SearchPath,
Delimiter: string; SearchCase: TCTSearchFileCase): string;
var
p, StartPos, l: integer;
CurPath, Base: string;
begin
Base:=ExpandFilename(AppendPathDelim(BasePath));
// search in current directory
Result:=SearchPascalUnitInDir(ShortFilename,Base,SearchCase);
if Result<>'' then exit;
// search in search path
StartPos:=1;
l:=length(SearchPath);
while StartPos<=l do begin
p:=StartPos;
while (p<=l) and (pos(SearchPath[p],Delimiter)<1) do inc(p);
CurPath:=Trim(copy(SearchPath,StartPos,p-StartPos));
if CurPath<>'' then begin
if not FilenameIsAbsolute(CurPath) then
CurPath:=Base+CurPath;
CurPath:=ExpandFilename(AppendPathDelim(CurPath));
Result:=SearchPascalUnitInDir(ShortFilename,CurPath,SearchCase);
if Result<>'' then exit;
end;
StartPos:=p+1;
end;
Result:='';
end;
function CreateAbsoluteSearchPath(const SearchPath, BaseDirectory: string function CreateAbsoluteSearchPath(const SearchPath, BaseDirectory: string
): string; ): string;
var var
@ -1156,7 +1345,7 @@ begin
break; break;
inc(CmpPos); inc(CmpPos);
end; end;
if CmpPos<EndPos then begin if CmpPos=APathLen then begin
Result:=@SearchPath[StartPos]; Result:=@SearchPath[StartPos];
exit; exit;
end; end;

View File

@ -82,7 +82,7 @@ uses
MemCheck, MemCheck,
{$ENDIF} {$ENDIF}
Classes, SysUtils, CodeToolsStrConsts, CodeTree, CodeAtom, CustomCodeTool, Classes, SysUtils, CodeToolsStrConsts, CodeTree, CodeAtom, CustomCodeTool,
KeywordFuncLists, BasicCodeTools, LinkScanner, CodeCache, DirectoryCache, KeywordFuncLists, BasicCodeTools, LinkScanner, CodeCache, DirectoryCacher,
AVL_Tree, PascalParserTool, AVL_Tree, PascalParserTool,
PascalReaderTool, FileProcs, DefineTemplates, FindDeclarationCache; PascalReaderTool, FileProcs, DefineTemplates, FindDeclarationCache;
@ -517,7 +517,7 @@ type
TFindDeclarationTool = class(TPascalReaderTool) TFindDeclarationTool = class(TPascalReaderTool)
private private
FAdjustTopLineDueToComment: boolean; FAdjustTopLineDueToComment: boolean;
FDirectoryValues: TCTDirectoryCache; FDirectoryCache: TCTDirectoryCache;
FInterfaceIdentifierCache: TInterfaceIdentifierCache; FInterfaceIdentifierCache: TInterfaceIdentifierCache;
FOnFindUsedUnit: TOnFindUsedUnit; FOnFindUsedUnit: TOnFindUsedUnit;
FOnGetCodeToolForBuffer: TOnGetCodeToolForBuffer; FOnGetCodeToolForBuffer: TOnGetCodeToolForBuffer;
@ -747,7 +747,7 @@ type
read FOnGetSrcPathForCompiledUnit write fOnGetSrcPathForCompiledUnit; read FOnGetSrcPathForCompiledUnit write fOnGetSrcPathForCompiledUnit;
property AdjustTopLineDueToComment: boolean property AdjustTopLineDueToComment: boolean
read FAdjustTopLineDueToComment write FAdjustTopLineDueToComment; read FAdjustTopLineDueToComment write FAdjustTopLineDueToComment;
property DirectoryValues: TCTDirectoryCache read FDirectoryValues; property DirectoryCache: TCTDirectoryCache read FDirectoryCache;
end; end;
function ExprTypeToString(const ExprType: TExpressionType): string; function ExprTypeToString(const ExprType: TExpressionType): string;
@ -1589,163 +1589,14 @@ end;
function TFindDeclarationTool.FindUnitSource(const AnUnitName, function TFindDeclarationTool.FindUnitSource(const AnUnitName,
AnUnitInFilename: string; ExceptionOnNotFound: boolean): TCodeBuffer; AnUnitInFilename: string; ExceptionOnNotFound: boolean): TCodeBuffer;
var var
CurDir, CompiledSrcExt: string; CompiledFilename: string;
AFilename: String;
function LoadFile(const AFilename: string; NewUnitName: String;
out NewCode: TCodeBuffer): boolean; NewInFilename: String;
begin NewCompiledUnitname: String;
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool.FindUnitSource.LoadFile ',AFilename);
{$ENDIF}
NewCode:=TCodeBuffer(Scanner.OnLoadSource(
Self,ExpandFilename(TrimFilename(AFilename)),true));
Result:=NewCode<>nil;
end;
function SearchUnitFileInDir(const ADir, AnUnitName: string;
SearchSource: boolean): TCodeBuffer;
var APath: string;
begin
APath:=ADir;
if (APath<>'') and (APath[length(APath)]<>PathDelim) then
APath:=APath+PathDelim;
// search as FPC: first lowercase, then keeping case, then uppercase
if SearchSource then begin
if LoadFile(ADir+AnUnitName+'.pp',Result) then exit;
if LoadFile(ADir+AnUnitName+'.pas',Result) then exit;
if LoadFile(ADir+AnUnitName+'.p',Result) then exit;
{$IFNDEF win32}
if LoadFile(ADir+lowercase(AnUnitName)+'.pp',Result) then exit;
if LoadFile(ADir+lowercase(AnUnitName)+'.pas',Result) then exit;
if LoadFile(ADir+lowercase(AnUnitName)+'.p',Result) then exit;
if LoadFile(ADir+UpperCaseStr(AnUnitName)+'.pp',Result) then exit;
if LoadFile(ADir+UpperCaseStr(AnUnitName)+'.pas',Result) then exit;
if LoadFile(ADir+UpperCaseStr(AnUnitName)+'.p',Result) then exit;
{$ENDIF}
end else begin
if LoadFile(ADir+AnUnitName+CompiledSrcExt,Result) then exit;
{$IFNDEF win32}
if LoadFile(ADir+lowercase(AnUnitName)+CompiledSrcExt,Result) then exit;
if LoadFile(ADir+UpperCaseStr(AnUnitName)+CompiledSrcExt,Result) then exit;
{$ENDIF}
end;
Result:=nil;
end;
function SearchUnitFileInPath(const APath, TheUnitName: string;
SearchSource: boolean): TCodeBuffer;
var PathStart, PathEnd: integer;
ADir: string;
begin
PathStart:=1;
while PathStart<=length(APath) do begin
PathEnd:=PathStart;
while (PathEnd<=length(APath)) and (APath[PathEnd]<>';') do inc(PathEnd);
if PathEnd>PathStart then begin
ADir:=copy(APath,PathStart,PathEnd-PathStart);
if (ADir<>'') and (ADir[length(ADir)]<>PathDelim) then
ADir:=ADir+PathDelim;
if not FilenameIsAbsolute(ADir) then ADir:=CurDir+ADir;
Result:=SearchUnitFileInDir(ADir,TheUnitName,SearchSource);
if Result<>nil then exit;
end;
PathStart:=PathEnd+1;
end;
Result:=nil;
end;
function SearchUnitFileInCompiledSrcPaths(const APath, TheUnitName: string
): TCodeBuffer;
var PathStart, PathEnd: integer;
ADir: string;
CurCompiledSrcPath: string;
begin
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool..SearchUnitFileInCompiledSrcPaths START APath="',APath,'" TheUnitName="',TheUnitName,'"');
{$ENDIF}
if not Assigned(OnGetSrcPathForCompiledUnit) then begin
Result:=nil;
exit;
end;
PathStart:=1;
while PathStart<=length(APath) do begin
PathEnd:=PathStart;
while (PathEnd<=length(APath)) and (APath[PathEnd]<>';') do inc(PathEnd);
if PathEnd>PathStart then begin
// extract and expand current search directory
ADir:=copy(APath,PathStart,PathEnd-PathStart);
if (ADir<>'') and (ADir[length(ADir)]<>PathDelim) then
ADir:=ADir+PathDelim;
if not FilenameIsAbsolute(ADir) then ADir:=CurDir+ADir;
// get CompiledSrcPath for current search directory
CurCompiledSrcPath:=OnGetSrcPathForCompiledUnit(Self,ADir);
if CurCompiledSrcPath<>'' then begin
// this directory is an unit output directory
// -> search the source in the current CompiledSrcPath
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool..SearchUnitFileInCompiledSrcPaths CurCompiledSrcPath="',CurCompiledSrcPath,'"');
{$ENDIF}
Result:=SearchUnitFileInPath(CurCompiledSrcPath,TheUnitName,true);
if Result<>nil then exit;
end;
end;
PathStart:=PathEnd+1;
end;
Result:=nil;
end;
function SearchFileInPath(const APath, RelativeFilename: string): TCodeBuffer;
var PathStart, PathEnd: integer;
ADir: string;
begin
PathStart:=1;
while PathStart<=length(APath) do begin
PathEnd:=PathStart;
while (PathEnd<=length(APath)) and (APath[PathEnd]<>';') do inc(PathEnd);
if PathEnd>PathStart then begin
ADir:=copy(APath,PathStart,PathEnd-PathStart);
if (ADir<>'') and (ADir[length(ADir)]<>PathDelim) then
ADir:=ADir+PathDelim;
if not FilenameIsAbsolute(ADir) then ADir:=CurDir+ADir;
if LoadFile(ADir+RelativeFilename,Result) then exit;
end;
PathStart:=PathEnd+1;
end;
Result:=nil;
end;
var
UnitSrcSearchPath: string;
MainCodeIsVirtual: boolean;
CompiledResult: TCodeBuffer;
UnitSearchPath: string;
SrcPathInitialized: boolean;
WorkingUnitInFilename: String;
CurFilename: String;
procedure InitSrcPath;
begin
if SrcPathInitialized then exit;
SrcPathInitialized:=true;
if Assigned(OnGetUnitSourceSearchPath) then begin
UnitSearchPath:='';
UnitSrcSearchPath:=OnGetUnitSourceSearchPath(Self);
end else begin
UnitSearchPath:=Scanner.Values[ExternalMacroStart+'UnitPath'];
UnitSrcSearchPath:=Scanner.Values[ExternalMacroStart+'SrcPath'];
if UnitSearchPath<>'' then begin
if UnitSrcSearchPath<>'' then
UnitSrcSearchPath:=UnitSrcSearchPath+';'+UnitSearchPath
else
UnitSrcSearchPath:=UnitSearchPath;
end;
end;
end;
begin begin
{$IFDEF ShowTriedFiles} {$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool.FindUnitSource A AnUnitName=',AnUnitName,' AnUnitInFilename=',AnUnitInFilename,' Self="',MainFilename,'"'); DebugLn('TFindDeclarationTool.FindUnitSource A AnUnitName="',AnUnitName,'" AnUnitInFilename="',AnUnitInFilename,'" Self="',MainFilename,'"');
{$ENDIF} {$ENDIF}
Result:=nil; Result:=nil;
if (AnUnitName='') or (Scanner=nil) or (Scanner.MainCode=nil) if (AnUnitName='') or (Scanner=nil) or (Scanner.MainCode=nil)
@ -1756,112 +1607,32 @@ begin
RaiseException('TFindDeclarationTool.FindUnitSource Invalid Data'); RaiseException('TFindDeclarationTool.FindUnitSource Invalid Data');
end; end;
SrcPathInitialized:=false; NewUnitName:=AnUnitName;
UnitSearchPath:=''; NewInFilename:=AnUnitInFilename;
UnitSrcSearchPath:=''; AFilename:=DirectoryCache.FindUnitSourceInCompletePath(
CompiledSrcExt:='.ppu'; NewUnitName,NewInFilename,false);
CompiledResult:=nil; Result:=TCodeBuffer(Scanner.OnLoadSource(Self,AFilename,true));
//DebugLn('>>>>>',Scanner.Values.AsString,'<<<<<');
MainCodeIsVirtual:=TCodeBuffer(Scanner.MainCode).IsVirtual;
if not MainCodeIsVirtual then begin
CurDir:=ExtractFilePath(TCodeBuffer(Scanner.MainCode).Filename);
end else begin
CurDir:='';
end;
// search as the compiler would search
if AnUnitInFilename<>'' then begin
// uses IN parameter
WorkingUnitInFilename:=SetDirSeparators(AnUnitInFilename);
if FilenameIsAbsolute(WorkingUnitInFilename) then begin
Result:=TCodeBuffer(Scanner.OnLoadSource(Self,WorkingUnitInFilename,true));
end else begin
// file is relative to current unit directory
// -> search file in current directory
CurDir:=AppendPathDelim(CurDir);
if not LoadFile(CurDir+WorkingUnitInFilename,Result) then begin
Result:=nil;
end;
end;
end else begin
// normal unit name
// first search in current directory (= where the maincode is)
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool.FindUnitSource Search in current dir=',CurDir);
{$ENDIF}
Result:=SearchUnitFileInDir(CurDir,AnUnitName,true);
if Result=nil then begin
// search source in search path
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool.FindUnitSource Search in search path=',UnitSrcSearchPath);
{$ENDIF}
InitSrcPath;
Result:=SearchUnitFileInPath(UnitSrcSearchPath,AnUnitName,true);
end;
if Result=nil then begin
// search for compiled unit
// search compiled unit in current directory
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool.FindUnitSource Search Compiled unit in current dir=',CurDir);
{$ENDIF}
if Scanner.InitialValues.IsDefined('WIN32')
and Scanner.InitialValues.IsDefined('VER1_0') then
CompiledSrcExt:='.ppw';
CompiledResult:=SearchUnitFileInDir(CurDir,AnUnitName,false);
// search compiled unit in unit path
if CompiledResult=nil then begin
UnitSearchPath:=Scanner.Values[ExternalMacroStart+'UnitPath'];
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool.FindUnitSource Search Compiled unit in unit path=',UnitSearchPath);
{$ENDIF}
CompiledResult:=SearchUnitFileInPath(UnitSearchPath,AnUnitName,false);
end;
if (CompiledResult<>nil) then begin
// there is a compiled unit
if Assigned(OnGetSrcPathForCompiledUnit)
and (not CompiledResult.IsVirtual) then begin
UnitSrcSearchPath:=
OnGetSrcPathForCompiledUnit(Self,CompiledResult.Filename);
CurDir:=ExtractFilePath(CompiledResult.Filename);
// search source in search path of compiled unit
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool.FindUnitSource Search in Compiled unit search path=',UnitSrcSearchPath);
{$ENDIF}
Result:=SearchUnitFileInPath(UnitSrcSearchPath,AnUnitName,true);
end;
end;
end;
if Result=nil then begin
// search in every unit path for a CompiledSrcPath and search there
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool.FindUnitSource Search Compiled unit in current dir=',CurDir);
{$ENDIF}
UnitSearchPath:=Scanner.Values[ExternalMacroStart+'UnitPath'];
Result:=SearchUnitFileInCompiledSrcPaths(UnitSearchPath,AnUnitName);
end;
if Result=nil then begin
// search in FPC source directory
CurFilename:=SearchUnitInUnitLinks(AnUnitName);
if CurFilename<>'' then
LoadFile(CurFilename,Result);
end;
end;
if (Result=nil) and Assigned(OnFindUsedUnit) then begin if (Result=nil) and Assigned(OnFindUsedUnit) then begin
// no unit found // no unit found
Result:=OnFindUsedUnit(Self,AnUnitName,AnUnitInFilename); Result:=OnFindUsedUnit(Self,AnUnitName,AnUnitInFilename);
end; end;
if Result=nil then begin
// search .ppu
NewCompiledUnitname:=AnUnitName+'.ppu';
CompiledFilename:=DirectoryCache.FindCompiledUnitInCompletePath(
NewCompiledUnitname,false);
end else begin
CompiledFilename:='';
end;
if (Result=nil) and ExceptionOnNotFound then begin if (Result=nil) and ExceptionOnNotFound then begin
if CompiledResult<>nil then begin if CompiledFilename<>'' then begin
// there is a compiled unit, only the source was not found // there is a compiled unit, only the source was not found
RaiseExceptionInstance( RaiseExceptionInstance(
ECodeToolUnitNotFound.Create(Self, ECodeToolUnitNotFound.Create(Self,
Format(ctsSourceNotFoundUnit, [CompiledResult.Filename]),AnUnitName)); Format(ctsSourceNotFoundUnit, [CompiledFilename]),AnUnitName));
end else begin end else begin
// nothing found // nothing found
RaiseExceptionInstance( RaiseExceptionInstance(
@ -1873,60 +1644,10 @@ end;
function TFindDeclarationTool.FindUnitCaseInsensitive(var AnUnitName, function TFindDeclarationTool.FindUnitCaseInsensitive(var AnUnitName,
AnUnitInFilename: string): string; AnUnitInFilename: string): string;
var
CurDir: String;
UnitPath, SrcPath: string;
NewUnitName: String;
begin begin
//DebugLn('TFindDeclarationTool.FindUnitCaseInsensitive AnUnitName=',AnUnitName,' AnUnitInFilename=',AnUnitInFilename); if not CheckDirectoryCache then exit;
if AnUnitInFilename<>'' then begin Result:=DirectoryCache.FindUnitSourceInCompletePath(
// uses IN parameter AnUnitName,AnUnitInFilename,true);
AnUnitInFilename:=TrimFilename(SetDirSeparators(AnUnitInFilename));
if FilenameIsAbsolute(AnUnitInFilename) then begin
Result:=FindDiskFilename(AnUnitInFilename);
if FileExists(Result) then
AnUnitInFilename:=Result
else
Result:='';
end else begin
// file is relative to current unit directory
// -> search file in current directory
CurDir:=ExtractFilePath(MainFilename);
if CurDir<>'' then begin
Result:=SearchFileInDir(AnUnitInFilename,CurDir,ctsfcAllCase);
if FileExists(Result) then begin
AnUnitInFilename:=CreateRelativePath(Result,CurDir);
end else begin
Result:='';
end;
end else begin
// virtual unit -> TODO
Result:='';
end;
end;
end else begin
// normal unit name
// search in unit, src and compiled src path
GatherUnitAndSrcPath(UnitPath,SrcPath);
if SysUtils.CompareText(AnUnitName,'Forms')=0 then begin
DebugLn('============================================================== ');
DebugLn('TFindDeclarationTool.FindUnitCaseInsensitive ',UnitPath+';'+SrcPath);
end;
Result:=SearchPascalUnitInPath(AnUnitName,CurDir,SrcPath,';',ctsfcAllCase);
if Result='' then begin
// search in unit links
Result:=SearchUnitInUnitLinks(AnUnitName);
end;
if Result<>'' then begin
NewUnitName:=ExtractFileNameOnly(Result);
if (NewUnitName<>lowercase(NewUnitName))
and (AnUnitName<>NewUnitName) then
AnUnitName:=NewUnitName;
end;
//DebugLn('TFindDeclarationTool.FindUnitCaseInsensitive TODO search unit');
end;
//DebugLn('TFindDeclarationTool.FindUnitCaseInsensitive RESULT AnUnitName=',AnUnitName,' AnUnitInFilename=',AnUnitInFilename,' Result=',Result);
end; end;
procedure TFindDeclarationTool.GatherUnitAndSrcPath(var UnitPath, procedure TFindDeclarationTool.GatherUnitAndSrcPath(var UnitPath,
@ -1935,8 +1656,8 @@ begin
UnitPath:=''; UnitPath:='';
CompleteSrcPath:=''; CompleteSrcPath:='';
if not CheckDirectoryCache then exit; if not CheckDirectoryCache then exit;
UnitPath:=DirectoryValues.Strings[ctdcsUnitPath]; UnitPath:=DirectoryCache.Strings[ctdcsUnitPath];
CompleteSrcPath:=DirectoryValues.Strings[ctdcsCompleteSrcPath]; CompleteSrcPath:=DirectoryCache.Strings[ctdcsCompleteSrcPath];
//DebugLn('TFindDeclarationTool.GatherUnitAndSrcPath UnitPath="',UnitPath,'" CompleteSrcPath="',CompleteSrcPath,'"'); //DebugLn('TFindDeclarationTool.GatherUnitAndSrcPath UnitPath="',UnitPath,'" CompleteSrcPath="',CompleteSrcPath,'"');
end; end;
@ -1945,7 +1666,7 @@ function TFindDeclarationTool.SearchUnitInUnitLinks(const TheUnitName: string
begin begin
Result:=''; Result:='';
if not CheckDirectoryCache then exit; if not CheckDirectoryCache then exit;
Result:=DirectoryValues.FindUnitLink(TheUnitName); Result:=DirectoryCache.FindUnitLink(TheUnitName);
end; end;
function TFindDeclarationTool.FindSmartHint(const CursorPos: TCodeXYPosition function TFindDeclarationTool.FindSmartHint(const CursorPos: TCodeXYPosition
@ -4670,7 +4391,7 @@ begin
RaiseException('[TFindDeclarationTool.FindCodeToolForUsedUnit] ' RaiseException('[TFindDeclarationTool.FindCodeToolForUsedUnit] '
+'internal error: invalid UnitNameAtom'); +'internal error: invalid UnitNameAtom');
AnUnitName:=copy(Src,UnitNameAtom.StartPos, AnUnitName:=copy(Src,UnitNameAtom.StartPos,
UnitNameAtom.EndPos-UnitNameAtom.StartPos); UnitNameAtom.EndPos-UnitNameAtom.StartPos);
if UnitInFileAtom.StartPos>=1 then begin if UnitInFileAtom.StartPos>=1 then begin
if (UnitInFileAtom.StartPos<1) if (UnitInFileAtom.StartPos<1)
or (UnitInFileAtom.EndPos<=UnitInFileAtom.StartPos) or (UnitInFileAtom.EndPos<=UnitInFileAtom.StartPos)
@ -7262,10 +6983,10 @@ end;
function TFindDeclarationTool.CheckDirectoryCache: boolean; function TFindDeclarationTool.CheckDirectoryCache: boolean;
begin begin
if FDirectoryValues<>nil then exit(true); if FDirectoryCache<>nil then exit(true);
if Assigned(OnGetDirectoryCache) then if Assigned(OnGetDirectoryCache) then
FDirectoryValues:=OnGetDirectoryCache(ExtractFilePath(MainFilename)); FDirectoryCache:=OnGetDirectoryCache(ExtractFilePath(MainFilename));
Result:=FDirectoryValues<>nil; Result:=FDirectoryCache<>nil;
end; end;
procedure TFindDeclarationTool.DoDeleteNodes; procedure TFindDeclarationTool.DoDeleteNodes;
@ -7348,9 +7069,9 @@ begin
FDependsOnCodeTools:=nil; FDependsOnCodeTools:=nil;
FDependentCodeTools.Free; FDependentCodeTools.Free;
FDependentCodeTools:=nil; FDependentCodeTools:=nil;
if FDirectoryValues<>nil then begin if FDirectoryCache<>nil then begin
FDirectoryValues.Release; FDirectoryCache.Release;
FDirectoryValues:=nil; FDirectoryCache:=nil;
end; end;
inherited Destroy; inherited Destroy;
end; end;

View File

@ -943,7 +943,8 @@ function TStandardCodeTool.FindMissingUnits(var MissingUnits: TStrings;
// find unit file // find unit file
NewUnitName:=OldUnitName; NewUnitName:=OldUnitName;
NewInFilename:=OldInFilename; NewInFilename:=OldInFilename;
AFilename:=DirectoryValues.FindUnitSource(NewUnitName,NewInFilename,true); AFilename:=DirectoryCache.FindUnitSourceInCompletePath(
NewUnitName,NewInFilename,true);
s:=NewUnitName; s:=NewUnitName;
if NewInFilename<>'' then if NewInFilename<>'' then
s:=s+' in '''+NewInFilename+''''; s:=s+' in '''+NewInFilename+'''';