codetools: tests for star directory

This commit is contained in:
mattias 2023-07-09 14:03:17 +02:00
parent a6696145cd
commit 427ba37baf
17 changed files with 552 additions and 227 deletions

View File

@ -384,6 +384,12 @@ type
function GetNamespacesForDirectory(const Directory: string;
UseCache: boolean = true): string;// value of macro #Namespaces
// simple global defines for tests and simple projects
function GetGlobalDefines(CreateIfNotExists: boolean = true): TDefineTemplate;
function SetGlobalPath(const DefName, Description, MacroName, SearchPath: string): TDefineTemplate;
function SetGlobalUnitPath(const UnitPath: string): TDefineTemplate;
function SetGlobalIncludePath(const IncludePath: string): TDefineTemplate;
// miscellaneous
property OnGetMethodName: TOnGetMethodname read FOnGetMethodName
write FOnGetMethodName;
@ -1772,6 +1778,47 @@ begin
end;
end;
function TCodeToolManager.GetGlobalDefines(CreateIfNotExists: boolean
): TDefineTemplate;
begin
Result:=DefineTree.FindDefineTemplateByName(StdDefTemplGlobal,true);
if (Result=nil) and CreateIfNotExists then begin
Result:=TDefineTemplate.Create(StdDefTemplGlobal,'Global definitions','','',da_Block);
DefineTree.AddFirst(Result);
end;
end;
function TCodeToolManager.SetGlobalPath(const DefName, Description, MacroName,
SearchPath: string): TDefineTemplate;
var
GlobalDef: TDefineTemplate;
NewValue: String;
begin
GlobalDef:=GetGlobalDefines;
Result:=GlobalDef.FindChildByName(DefName);
NewValue:='$('+MacroName+');'+SearchPath;
if Result=nil then begin
Result:=TDefineTemplate.Create(DefName,Description,MacroName,
NewValue,da_DefineRecurse);
end else if Result.Value=NewValue then
exit
else
Result.Value:=NewValue;
DefineTree.ClearCache;
end;
function TCodeToolManager.SetGlobalUnitPath(const UnitPath: string
): TDefineTemplate;
begin
Result:=SetGlobalPath('UnitPath','Unit search path',UnitPathMacroName,UnitPath);
end;
function TCodeToolManager.SetGlobalIncludePath(const IncludePath: string
): TDefineTemplate;
begin
Result:=SetGlobalPath('IncPath','Include search path',IncludePathMacroName,IncludePath);
end;
procedure TCodeToolManager.FreeListOfPCodeXYPosition(var List: TFPList);
begin
CodeCache.FreeListOfPCodeXYPosition(List);

View File

@ -66,6 +66,7 @@ const
ExternalMacroStart = ExprEval.ExternalMacroStart;
// Standard Template Names (do not translate them)
StdDefTemplGlobal = 'Global';
StdDefTemplFPC = 'Free Pascal Compiler';
StdDefTemplFPCSrc = 'Free Pascal sources';
StdDefTemplLazarusSources = 'Lazarus sources';
@ -644,6 +645,7 @@ type
procedure Move(SrcIndex, DestIndex: integer);
property EnglishErrorMsgFilename: string
read FEnglishErrorMsgFilename write SetEnglishErrorMsgFilename;
// FPC templates
function CreateFPCTemplate(const CompilerPath, CompilerOptions,
TestPascalFile: string;
@ -6224,9 +6226,12 @@ begin
end;
procedure TDefinePool.Delete(Index: integer);
var
Def: TDefineTemplate;
begin
Items[Index].Clear(true);
Items[Index].Free;
Def:=Items[Index];
Def.Clear(true);
Def.Free;
FItems.Delete(Index);
end;

View File

@ -178,7 +178,7 @@ type
const FileCase: TCTSearchFileCase): string; virtual; abstract;
function FindIncludeFile(const IncFilename: string; AnyCase: boolean): string; virtual; abstract;
function FindUnitSource(const AUnitName: string; AnyCase: boolean): string; virtual; abstract;
property Directory: string read FDirectory;
property Directory: string read FDirectory; // with trailing pathdelim
property Pool: TCTDirectoryCachePool read FPool;
end;
@ -201,8 +201,7 @@ type
procedure AddToCache(const UnitSrc: TCTDirectoryUnitSources;
const Search, Filename: string);
public
constructor Create(const TheDirectory: string;
ThePool: TCTDirectoryCachePool);
constructor Create(const TheDirectory: string; ThePool: TCTDirectoryCachePool);
destructor Destroy; override;
procedure CalcMemSize(Stats: TCTMemStats); override;
procedure Reference;
@ -211,23 +210,27 @@ type
function IndexOfFileCaseSensitive(ShortFilename: PChar): integer; override;
function FindFile(const ShortFilename: string;
const FileCase: TCTSearchFileCase): string; override;
function FindIncludeFile(const IncFilename: string; AnyCase: boolean
): string; override;
function FileAge(const ShortFilename: string): TCTFileAgeTime;
function FileAttr(const ShortFilename: string): TCTDirectoryListingAttr;
function FileSize(const ShortFilename: string): TCTDirectoryListingSize;
// unit link (for fpc ppu files to fpc src file)
function FindUnitLink(const AUnitName: string): string;
function FindUnitInUnitSet(const AUnitName: string;
SrcSearchRequiresPPU: boolean = true): string;
function FindCompiledUnitInUnitSet(const AUnitName: string): string;
function FindUnitInUnitSet(const AUnitName: string; SrcSearchRequiresPPU: boolean = true): string;
// find unit source
function FindUnitSource(const AUnitName: string; AnyCase: boolean): string; override;
function FindUnitSourceInCleanSearchPath(const AUnitName,
SearchPath: string; AnyCase: boolean): string; // search in unitpath
function FindUnitSourceInCompletePath(var AUnitName, InFilename: string; // search in unitpath and unitpaths of output dirs
AnyCase: boolean; FPCSrcSearchRequiresPPU: boolean = false;
const AddNameSpaces: string = ''): string;
function FindCompiledUnitInCompletePath(const AnUnitname: string;
AnyCase: boolean): string;
// find ppu/dcu file
function FindCompiledUnitInUnitSet(const AUnitName: string): string;
function FindCompiledUnitInCompletePath(const AnUnitname: string; AnyCase: boolean): string;
// include files
function FindIncludeFile(const IncFilename: string; AnyCase: boolean): string; override;
function FindIncludeFileInPath(IncFilename: string; AnyCase: boolean): string;
function FindIncludeFileInCleanPath(IncFilename, SearchPath: string; AnyCase: boolean): string;
procedure IterateFPCUnitsInSet(const Iterate: TCTOnIterateFile);
procedure UpdateListing;
procedure WriteListing;
@ -239,7 +242,7 @@ type
property Listing: TCTDirectoryListing read FListing;
end;
{ TCTStarDirectoryCache }
{ TCTStarDirectoryCache - a cache for a directory and its sub directories, e.g. searching in '/foo/**' }
TCTStarDirectoryCache = class(TCTDirectoryBaseCache)
private
@ -346,8 +349,10 @@ type
const Iterate: TCTOnIterateFile);
function FindDiskFilename(const Filename: string;
{%H-}SearchCaseInsensitive: boolean = false): string; // using Pascal case insensitivity, not UTF-8
function FindIncludeFileInDirectory(const Directory, IncFileName: string;
function FindIncludeFileInDirectory(Directory, IncFileName: string;
AnyCase: boolean = false): string;
function FindIncludeFileInCompletePath(Directory, IncFilename: string;
AnyCase: boolean = false): string;
function FindUnitInDirectory(const Directory, AUnitName: string;
AnyCase: boolean = false): string;
function FindVirtualFile(const Filename: string): string;
@ -378,7 +383,7 @@ function CompareCTDirectoryCaches(Data1, Data2: Pointer): integer;
function CompareAnsiStringAndDirectoryCache(Dir, Cache: Pointer): integer;
function ComparePCharFirstCaseInsAThenCase(Data1, Data2: Pointer): integer; // insensitive ASCII then byte wise
function ComparePCharCaseInsensitiveA(Data1, Data2: Pointer): integer; // insensitive ASCII
function ComparePCharCaseInsensitiveASCII(Data1, Data2: Pointer): integer; // insensitive ASCII
function ComparePCharCaseSensitive(Data1, Data2: Pointer): integer; // byte wise
// star directories
@ -465,12 +470,12 @@ end;
function ComparePCharFirstCaseInsAThenCase(Data1, Data2: Pointer): integer;
begin
Result:=ComparePCharCaseInsensitiveA(Data1,Data2);
Result:=ComparePCharCaseInsensitiveASCII(Data1,Data2);
if Result=0 then
Result:=ComparePCharCaseSensitive(Data1,Data2);
end;
function ComparePCharCaseInsensitiveA(Data1, Data2: Pointer): integer;
function ComparePCharCaseInsensitiveASCII(Data1, Data2: Pointer): integer;
var
p1: PChar absolute Data1;
p2: PChar absolute Data2;
@ -482,7 +487,7 @@ begin
Result:=ord(FPUpChars[p1^])-ord(FPUpChars[p2^]);
end;
function ComparePCharCaseInsensitiveA(Data1, Data2: Pointer; MaxCount: PtrInt): integer;
function ComparePCharCaseInsensitiveASCII(Data1, Data2: Pointer; MaxCount: PtrInt): integer;
var
p1: PChar absolute Data1;
p2: PChar absolute Data2;
@ -543,6 +548,59 @@ begin
until false;
end;
function CheckLoUpCase(Find, Candidate: PChar; MaxCount: PtrInt): boolean;
var
i: PtrInt;
CurFind, CurCandidate: PChar;
c: Char;
begin
// check case sensitive
CurFind:=Find;
CurCandidate:=Candidate;
i:=0;
repeat
if i=MaxCount then exit(true);
if (CurFind^<>CurCandidate^) then break;
if CurFind^=#0 then exit(true);
inc(i);
inc(CurFind);
inc(CurCandidate);
until false;
// check lowercase Find
CurFind:=Find;
CurCandidate:=Candidate;
i:=0;
repeat
if i=MaxCount then exit(true);
c:=CurFind^;
case c of
'A'..'Z':
if ord(c)+32<>ord(CurCandidate^) then break;
else if c<>CurCandidate^ then break;
end;
if CurFind^=#0 then exit(true);
inc(i);
inc(CurFind);
inc(CurCandidate);
until false;
// check uppercase Find
CurFind:=Find;
CurCandidate:=Candidate;
i:=0;
repeat
if i=MaxCount then exit(true);
if (FPUpChars[CurFind^]<>CurCandidate^) then break;
if CurFind^=#0 then exit(true);
inc(i);
inc(CurFind);
inc(CurCandidate);
until false;
Result:=false;
end;
function IsCTStarDirectory(const Directory: string; out p: integer
): TCTStarDirectoryKind;
var
@ -598,7 +656,7 @@ begin
ComparePCharCaseInsensitiveA(Pointer(TheUnitName),@UnitLinks[UnitLinkStart],UnitLinkLen)]);
{$ENDIF}
if (UnitLinkLen=length(TheUnitName))
and (ComparePCharCaseInsensitiveA(Pointer(TheUnitName),@UnitLinks[UnitLinkStart],
and (ComparePCharCaseInsensitiveASCII(Pointer(TheUnitName),@UnitLinks[UnitLinkStart],
UnitLinkLen)=0)
then begin
// unit found -> parse filename
@ -966,7 +1024,7 @@ begin
while l<=r do begin
m:=(l+r) shr 1;
CurFilename:=@Files[FListing.Starts[m]+DirListNameOffset];
cmp:=ComparePCharCaseInsensitiveA(ShortFilename,CurFilename);
cmp:=ComparePCharCaseInsensitiveASCII(ShortFilename,CurFilename);
if cmp>0 then
l:=m+1
else if cmp<0 then
@ -1114,11 +1172,11 @@ end;
function TCTDirectoryCache.FindIncludeFile(const IncFilename: string;
AnyCase: boolean): string;
var
Files, CurFilename, IncExtP, CurExtP: PChar;
Files, CurFilename, IncExtP, CurExtP, IncFilenameP: PChar;
Starts: PInteger;
l, r, m, first, cmp, Best: Integer;
AUnitName: String;
Fits: Boolean;
Stop: Boolean;
Ext, BestExt: TCTPascalIncExtType;
begin
Result:='';
@ -1126,6 +1184,9 @@ begin
//if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
DebugLn('TCTDirectoryCache.FindIncludeFile IncName="',IncFilename,'" AnyCase=',dbgs(AnyCase),' Directory=',Directory);
{$ENDIF}
{$IFDEF CaseInsensitiveFilenames}
AnyCase:=true;
{$ENDIF}
if IncFilename='' then exit;
if Directory<>'' then begin
UpdateListing;
@ -1133,13 +1194,10 @@ begin
if Files=nil then exit;
Starts:=FListing.Starts;
// AnyCase:
// search IncFilename
// if IncFilename has no ext, then seach IncFilename.inc, IncFilename.pp, IncFilename.pas
// not AnyCase:
// search IncFilename
// if IncFilename has no ext, then seach IncFilename.inc, IncFilename.pp, IncFilename.pas
// first search IncFilename
// if IncFilename has no ext, then seach IncFilename.inc, IncFilename.pp, IncFilename.pas
IncFilenameP:=PChar(IncFilename);
l:=length(IncFilename);
while (l>0) and (IncFilename[l]<>'.') do dec(l);
if l>0 then begin
@ -1187,54 +1245,41 @@ begin
CurExtP:=CurFilename+length(AUnitname);
{$IFDEF DebugDirCacheFindIncFile}
//if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
DebugLn('TCTDirectoryCache.FindIncludeFile NEXT "',CurFilename,'" ExtStart=',dbgstr(CurExt^));
DebugLn('TCTDirectoryCache.FindIncludeFile NEXT "',CurFilename,'" ExtStart=',dbgstr(CurExtP^));
{$ENDIF}
Fits:=false;
Stop:=false;
if IncExtP<>nil then begin
// include file with extension
if ComparePCharCaseInsensitiveA(CurExtP,IncExtP)=0 then
Fits:=true;
end else begin
// include file without extension -> search without and default extension
if (CurExtP^=#0) then begin
Fits:=true;
Ext:=pietNone;
end else begin
Ext:=IsPascalIncExt(IncExtP);
Fits:=Ext>pietNone;
end;
end;
if Fits then begin
// the extension fits -> check case
Result:=CurFilename;
{$IFDEF DebugDirCacheFindIncFile}
//if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
DebugLn('TCTDirectoryCache.FindIncludeFile CHECKING CASE "',CurFilename,'"');
{$ENDIF}
if AnyCase then begin
if ComparePCharCaseInsensitiveASCII(CurExtP,IncExtP)=0 then
// any case with extension fits -> can't get any better
Stop:=true;
end else if CheckLoUpCase(IncFilenameP,CurFilename,length(IncFilename)+1) then
Stop:=true; // mixed case with extension fits -> can't get any better
end else begin
// include file without extension -> search without and with default extension
if (CurExtP^=#0) then begin
if AnyCase or CheckLoUpCase(IncFilenameP,CurFilename,length(IncFilename)+1) then
// file without extension fits a file without extension -> can't get any better
Stop:=true;
end else begin
// check case platform dependent
{$IFDEF CaseInsensitiveFilenames}
{$ELSE}
if (LeftStr(Result,length(AUnitName))<>AUnitName)
and (Result<>lowercase(Result))
and (Result<>uppercase(Result)) then
Fits:=false;
{$ENDIF}
end;
if Fits then begin
if IncExtP<>nil then begin
// include file with extension -> found
exit;
end else begin
// include file without extension -> search best extension
Ext:=IsPascalIncExt(CurExtP);
if Ext>pietNone then begin
// file without extension fits an include file with extension
// Note: the compiler prefers file.inc over file.pas
if (Best<0) or (BestExt>Ext) then begin
Best:=m;
BestExt:=Ext;
if AnyCase or CheckLoUpCase(IncFilenameP,CurFilename,length(IncFilename)) then begin
Best:=m;
BestExt:=Ext;
end;
end;
end;
end;
end;
if Stop then begin
Best:=m;
break;
end;
inc(m);
end;
if Best>=0 then begin
@ -1256,6 +1301,71 @@ begin
Result:='';
end;
function TCTDirectoryCache.FindIncludeFileInPath(IncFilename: string;
AnyCase: boolean): string;
var
HasPathDelims: Boolean;
SearchPath: String;
begin
Result:='';
{$IFDEF DebugDirCacheFindIncFile}
//if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
DebugLn('TCTDirectoryCache.FindIncludeFileInPath IncName="',IncFilename,'" AnyCase=',dbgs(AnyCase),' Directory=',Directory);
{$ENDIF}
if IncFilename='' then exit;
IncFilename:=ResolveDots(IncFilename);
HasPathDelims:=(System.Pos('/',IncFilename)>0) or (System.Pos('\',IncFilename)>0);
if HasPathDelims then begin
Result:=Pool.FindIncludeFileInCompletePath(Directory,IncFilename,AnyCase);
exit;
end;
SearchPath:=Strings[ctdcsIncludePath];
Result:=FindIncludeFileInCleanPath(IncFilename,SearchPath,AnyCase);
end;
function TCTDirectoryCache.FindIncludeFileInCleanPath(IncFilename,
SearchPath: string; AnyCase: boolean): string;
var
StartPos, p: Integer;
l: SizeInt;
CurPath: String;
IsAbsolute, HasPathDelims: Boolean;
begin
Result:='';
HasPathDelims:=(System.Pos('/',IncFilename)>0) or (System.Pos('\',IncFilename)>0);
if HasPathDelims then
exit;
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.FindIncludeFileInCleanPath CurPath="',CurPath,'"');
if IsAbsolute then begin
CurPath:=AppendPathDelim(CurPath);
Result:=Pool.FindIncludeFileInDirectory(CurPath,IncFilename,AnyCase);
end else if (CurPath='.') and (Directory='') then
Result:=Pool.FindVirtualInclude(IncFilename)
else
Result:='';
if Result<>'' then exit;
end;
StartPos:=p+1;
end;
Result:='';
end;
function TCTDirectoryCache.FileAge(const ShortFilename: string): TCTFileAgeTime;
var
i: Integer;
@ -1764,13 +1874,13 @@ end;
function TCTStarDirectoryCache.FindIncludeFile(const IncFilename: string;
AnyCase: boolean): string;
var
Files, CurFilename, IncExtP, CurExtP: PChar;
Files, IncExtP, CurExtP, CurFilename, IncFilenameP: PChar;
Starts: PInteger;
l, r, m, first, cmp, DirIndex: TListingPosition;
l, r, m, first, cmp: TListingPosition;
AUnitName: String;
Fits: Boolean;
Ext, BestExt: TCTPascalIncExtType;
BestDirIndex, Best: Integer;
Best: Integer;
Stop: Boolean;
begin
Result:='';
{$IFDEF DebugDirCacheFindIncFile}
@ -1783,6 +1893,7 @@ begin
if Files=nil then exit;
Starts:=FListing.Starts;
IncFilenameP:=PChar(IncFilename);
l:=length(IncFilename);
while (l>0) and (IncFilename[l]<>'.') do dec(l);
if l>0 then begin
@ -1817,7 +1928,6 @@ begin
m:=first;
// -> now find a filename with correct case and extension
Best:=-1;
BestDirIndex:=-1;
BestExt:=high(TCTPascalIncExtType);
while m<FListing.Count do begin
CurFilename:=@Files[Starts[m]+SizeOf(TListingHeader)];
@ -1830,61 +1940,41 @@ begin
CurExtP:=CurFilename+length(AUnitname);
{$IFDEF DebugDirCacheFindIncFile}
//if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
DebugLn('TCTDirectoryCache.FindIncludeFile NEXT "',CurFilename,'" ExtStart=',dbgstr(CurExt^));
DebugLn('TCTDirectoryCache.FindIncludeFile NEXT "',CurFilename,'" ExtStart=',dbgstr(CurExtP^));
{$ENDIF}
Fits:=false;
Stop:=false;
if IncExtP<>nil then begin
// include file with extension
if ComparePCharCaseInsensitiveA(CurExtP,IncExtP)=0 then
Fits:=true;
end else begin
// include file without extension -> search without and default extension
if (CurExtP^=#0) then begin
Fits:=true;
Ext:=pietNone;
end else begin
Ext:=IsPascalIncExt(IncExtP);
Fits:=Ext>pietNone;
end;
end;
if Fits then begin
// the extension fits -> check case
Result:=CurFilename;
{$IFDEF DebugDirCacheFindIncFile}
//if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
DebugLn('TCTDirectoryCache.FindIncludeFile CHECKING CASE "',CurFilename,'"');
{$ENDIF}
if AnyCase then begin
if ComparePCharCaseInsensitiveASCII(CurExtP,IncExtP)=0 then
// any case with extension fits -> can't get any better
Stop:=true;
end else if CheckLoUpCase(IncFilenameP,CurFilename,length(IncFilename)+1) then
Stop:=true; // mixed case with extension fits -> can't get any better
end else begin
// include file without extension -> search without and with default extension
if (CurExtP^=#0) then begin
if AnyCase or CheckLoUpCase(IncFilenameP,CurFilename,length(IncFilename)+1) then
// file without extension fits a file without extension -> can't get any better
Stop:=true;
end else begin
// check case platform dependent
{$IFDEF CaseInsensitiveFilenames}
{$ELSE}
if (LeftStr(Result,length(AUnitName))<>AUnitName)
and (Result<>lowercase(Result))
and (Result<>uppercase(Result)) then
Fits:=false;
{$ENDIF}
end;
if Fits then begin
if IncExtP<>nil then begin
// include file with extension -> found
Result:=FListing.GetSubDirFilename(m);
exit;
end else begin
// include file without extension
// the first in inc path wins
// filename.inc is better than filename.pas
DirIndex:=FListing.GetSubDirIndex(m);
if (Best<0)
or (BestDirIndex>DirIndex)
or ((BestDirIndex=DirIndex) and (BestExt>Ext)) then
begin
Best:=m;
BestExt:=Ext;
Ext:=IsPascalIncExt(CurExtP);
if Ext>pietNone then begin
// file without extension fits an include file with extension
// Note: the compiler prefers file.inc over file.pas
if (Best<0) or (BestExt>Ext) then begin
if AnyCase or CheckLoUpCase(IncFilenameP,CurFilename,length(IncFilename)) then begin
Best:=m;
BestExt:=Ext;
end;
end;
end;
end;
end;
if Stop then begin
Best:=m;
break;
end;
inc(m);
end;
if Best>=0 then begin
@ -2012,7 +2102,7 @@ begin
while l<=r do begin
m:=(l+r) shr 1;
CurFilename:=@Files[FListing.Starts[m]+SizeOf(TListingHeader)];
cmp:=ComparePCharCaseInsensitiveA(ShortFilename,CurFilename);
cmp:=ComparePCharCaseInsensitiveASCII(ShortFilename,CurFilename);
if cmp>0 then
l:=m+1
else if cmp<0 then
@ -2065,13 +2155,19 @@ var
WorkingListingCount: integer;
WorkingListingCapacity: integer;
procedure TraverseDir(const CurSubDir: string; SubDirIndex, Level: integer);
procedure TraverseDir(const CurSubDir: string; Level: integer);
var
SubDirIndex: TListingPosition;
Dir: TCTDirectoryCache;
DirListing: TCTDirectoryListing;
i, NewCapacity: Integer;
WorkingItem: PWorkStarFileInfo;
begin
if Level=0 then
SubDirIndex:=-1
else
SubDirIndex:=FListing.SubDirs.Add(CurSubDir);
Dir:=Pool.GetCache(Directory+CurSubDir,true,false);
Dir.UpdateListing;
DirListing:=Dir.Listing;
@ -2086,7 +2182,7 @@ var
if WorkingListingCapacity>0 then
NewCapacity:=WorkingListingCapacity*2
else
NewCapacity:=256;
NewCapacity:=128;
ReAllocMem(WorkingListing,SizeOf(TWorkStarFileInfo)*NewCapacity);
FillByte(WorkingListing[WorkingListingCount],
SizeOf(TWorkStarFileInfo)*(NewCapacity-WorkingListingCapacity),0);
@ -2108,8 +2204,10 @@ var
for i:=0 to DirListing.Count-1 do begin
if DirListing.GetAttr(i) and faDirectory=0 then continue;
// add sub directory
SubDirIndex:=FListing.SubDirs.Add(CurSubDir);
TraverseDir(CurSubDir+PathDelim+DirListing.GetFilename(i),SubDirIndex,Level);
if Level=1 then
TraverseDir(DirListing.GetFilename(i),Level)
else
TraverseDir(CurSubDir+PathDelim+DirListing.GetFilename(i),Level);
end;
end;
@ -2131,7 +2229,7 @@ begin
WorkingListingCount:=0;
WorkingListingCapacity:=0;
try
TraverseDir('',-1,0);
TraverseDir('',0);
if WorkingListingCount=0 then exit;
@ -2175,11 +2273,11 @@ end;
procedure TCTStarDirectoryCache.WriteListing;
var
i: Integer;
Filename: PChar;
Filename: String;
begin
writeln('TCTStarDirectoryCache.WriteListing Count=',FListing.Count,' Size=',FListing.Size);
for i:=0 to FListing.Count-1 do begin
Filename:=FListing.GetShortFilename(i);
Filename:=FListing.GetSubDirFilename(i);
writeln(i,' "',Filename,'"');
end;
end;
@ -2285,7 +2383,7 @@ begin
if (Index<0) or (Index>=Count) then
RaiseIndexOutOfBounds;
i:=PListingHeader(@Files[Starts[Index]])^.SubDirIndex;
if i>=0 then
if i<0 then
Result:=''
else
Result:=SubDirs[i]+PathDelim;
@ -2417,7 +2515,7 @@ begin
if Node<>nil then begin
Result:=TCTStarDirectoryCache(Node.Data);
end else if CreateIfNotExists then begin
Dir:=FindDiskFilename(Directory);
Dir:=AppendPathDelim(FindDiskFilename(Directory));
Result:=TCTStarDirectoryCache.Create(Dir,Kind,Self);
FStarDirectories[Kind].Add(Result);
end else
@ -2647,7 +2745,7 @@ begin
Result:=Cache.Directory+Result;
end;
function TCTDirectoryCachePool.FindIncludeFileInDirectory(const Directory,
function TCTDirectoryCachePool.FindIncludeFileInDirectory(Directory,
IncFileName: string; AnyCase: boolean): string;
var
Cache: TCTDirectoryBaseCache;
@ -2769,6 +2867,15 @@ begin
Result:=Cache.FindUnitSourceInCompletePath(AUnitName,InFilename,AnyCase);
end;
function TCTDirectoryCachePool.FindIncludeFileInCompletePath(Directory,
IncFilename: string; AnyCase: boolean): string;
var
Cache: TCTDirectoryCache;
begin
Cache:=GetCache(Directory,true,false);
Result:=Cache.FindIncludeFileInPath(IncFilename,AnyCase);
end;
function TCTDirectoryCachePool.FindCompiledUnitInCompletePath(
const Directory: string; var AnUnitname: string; AnyCase: boolean): string;
var

View File

@ -806,11 +806,13 @@ type
function IgnoreErrorAfterValid: boolean;
function CleanPosIsAfterIgnorePos(CleanPos: integer): boolean;
function LoadSourceCaseLoUp(const AFilename: string; AllowVirtual: boolean = false): TSourceLog;
function LoadIncludeFile(const AFilename: string; AllowVirtual: boolean = false): TSourceLog;
function LoadIncludeFile(
const AFilename: string; // epxanded filename or virtual file
AllowVirtual: boolean = false): TSourceLog; // trying different case and extensions, does not search in include path
class function GetPascalCompiler(Evals: TExpressionEvaluator): TPascalCompiler;
function SearchIncludeFile(AFilename: string; out NewCode: TSourceLog;
var MissingIncludeFile: TMissingIncludeFile): boolean;
var MissingIncludeFile: TMissingIncludeFile): boolean; // search in includepath
{$IFDEF GuessMisplacedIfdef}
function GuessMisplacedIfdefEndif(StartCursorPos: integer;
StartCode: pointer;
@ -818,7 +820,6 @@ type
out EndCode: Pointer): boolean;
{$ENDIF}
function GetHiddenUsedUnits: string; // comma separated
property DirectoryCachePool: TCTDirectoryCachePool read FDirectoryCachePool write FDirectoryCachePool;
// global write lock
procedure ActivateGlobalWriteLock;
@ -829,6 +830,7 @@ type
read FOnSetGlobalWriteLock write FOnSetGlobalWriteLock;
// properties
property DirectoryCachePool: TCTDirectoryCachePool read FDirectoryCachePool write FDirectoryCachePool;
property OnLoadSource: TOnLoadSource read FOnLoadSource write FOnLoadSource;
property OnDeleteSource: TOnDeleteSource read FOnDeleteSource write FOnDeleteSource;
property OnGetSourceStatus: TOnGetSourceStatus
@ -4042,7 +4044,7 @@ end;
function TLinkScanner.LoadIncludeFile(const AFilename: string;
AllowVirtual: boolean): TSourceLog;
var
Path, FileNameOnly: string;
Path, FileNameNoPath: string;
SecondaryFileName: String;
function Search(const ShortFilename: string; var r: TSourceLog): boolean;
@ -4070,15 +4072,15 @@ begin
Path:=ResolveDots(ExtractFilePath(AFilename));
if (not AllowVirtual) and not FilenameIsAbsolute(Path) then
exit(nil);
FileNameOnly:=ExtractFilename(AFilename);
FileNameNoPath:=ExtractFilename(AFilename);
if Search(FileNameOnly,Result) then exit;
if Search(FileNameNoPath,Result) then exit;
if ExtractFileExt(FileNameOnly)='' then begin
if ExtractFileExt(FileNameNoPath)='' then begin
// search with the default file extensions
if Search(FileNameOnly+'.inc',Result) then exit;
if Search(FileNameOnly+'.pp',Result) then exit;
if Search(FileNameOnly+'.pas',Result) then exit;
if Search(FileNameNoPath+'.inc',Result) then exit;
if Search(FileNameNoPath+'.pp',Result) then exit;
if Search(FileNameNoPath+'.pas',Result) then exit;
end;
Result:=nil;
@ -4107,8 +4109,6 @@ end;
function TLinkScanner.SearchIncludeFile(AFilename: string; out
NewCode: TSourceLog; var MissingIncludeFile: TMissingIncludeFile): boolean;
var
IncludePath: string;
ExpFilename: string;
HasPathDelims: Boolean;
procedure SetMissingIncludeFile;
@ -4118,90 +4118,78 @@ var
MissingIncludeFile.IncludePath:=Values.Variables[ExternalMacroStart+'INCPATH'];
end;
function SearchPath(const APath, RelFilename: string): boolean;
begin
Result:=false;
if APath='' then exit;
{$IFDEF VerboseIncludeSearch}
DebugLn('TLinkScanner.SearchPath CurIncPath="',APath,'" / "',RelFilename,'"');
{$ENDIF}
ExpFilename:=AppendPathDelim(APath)+RelFilename;
if not FilenameIsAbsolute(ExpFilename) then
ExpFilename:=ExtractFilePath(FMainSourceFilename)+ExpFilename;
NewCode:=LoadIncludeFile(ExpFilename);
Result:=NewCode<>nil;
end;
function Search(const RelFilename: string): boolean;
function Search(RelFilename: string): boolean;
var
IsVirtualUnit: Boolean;
Dir, CurPath: String;
PathStart, PathEnd: integer;
AnyCase: Boolean;
Dir, FoundFilename: String;
begin
Dir:=ExtractFilePath(FMainSourceFilename);
IsVirtualUnit:=not FilenameIsAbsolute(Dir);
if IsVirtualUnit then begin
// main source is virtual -> allow virtual include file
NewCode:=LoadIncludeFile(RelFilename,true);
Result:=(NewCode<>nil);
if Result then exit;
end else begin
// main source has absolute filename
// -> search in directory of unit
ExpFilename:=Dir+RelFilename;
NewCode:=LoadIncludeFile(ExpFilename);
Result:=(NewCode<>nil);
if Result then exit;
AnyCase:=Values.IsDefined('PAS2JS');
if HasPathDelims then begin
// find an include file with a path e.g. 'foo/bar.inc'
// -> search in 'foo/', do not search in include path
if Dir='' then begin
// searching in virtual directory is not yet supported
exit(false);
end;
Dir:=ResolveDots(Dir+ExtractFilePath(RelFilename));
RelFilename:=ExtractFileName(RelFilename);
FoundFilename:=DirectoryCachePool.FindIncludeFileInDirectory(Dir,RelFilename,AnyCase);
if FoundFilename<>'' then begin
{$IFDEF VerboseIncludeSearch}
DebugLn('TLinkScanner.Search Filename="',AFilename,'" Found="',FoundFilename,'"');
{$ENDIF}
NewCode:=FOnLoadSource(Self,FoundFilename,true);
if (NewCode<>nil) then
exit(true);
end;
exit(false);
end;
// search in dir of unit
FoundFilename:=DirectoryCachePool.FindIncludeFileInDirectory(Dir,RelFilename,AnyCase);
if FoundFilename<>'' then begin
{$IFDEF VerboseIncludeSearch}
DebugLn('TLinkScanner.Search Filename="',AFilename,'" Found="',FoundFilename,'"');
{$ENDIF}
NewCode:=FOnLoadSource(Self,FoundFilename,true);
if (NewCode<>nil) then
exit(true);
end;
// search in include path
FoundFilename:=DirectoryCachePool.FindIncludeFileInCompletePath(Dir,RelFilename,AnyCase);
if FoundFilename<>'' then begin
{$IFDEF VerboseIncludeSearch}
DebugLn('TLinkScanner.Search Filename="',AFilename,'" Found="',FoundFilename,'"');
{$ENDIF}
NewCode:=FOnLoadSource(Self,FoundFilename,true);
if (NewCode<>nil) then
exit(true);
end;
if not HasPathDelims then begin
// file without path -> search in inc paths
if MissingIncludeFile=nil then
IncludePath:=Values.Variables[ExternalMacroStart+'INCPATH']
else
IncludePath:=MissingIncludeFile.IncludePath;
{$IFDEF VerboseIncludeSearch}
DebugLn('TLinkScanner.SearchIncludeFile IncPath="',IncludePath,'"');
{$ENDIF}
PathStart:=1;
PathEnd:=PathStart;
while PathEnd<=length(IncludePath) do begin
if IncludePath[PathEnd]=';' then begin
if PathEnd>PathStart then begin
CurPath:=TrimFilename(copy(IncludePath,PathStart,PathEnd-PathStart));
Result:=SearchPath(CurPath,RelFilename);
if Result then exit;
end;
PathStart:=PathEnd+1;
PathEnd:=PathStart;
end else
inc(PathEnd);
end;
if PathEnd>PathStart then begin
CurPath:=TrimFilename(copy(IncludePath,PathStart,PathEnd-PathStart));
Result:=SearchPath(CurPath,RelFilename);
if Result then exit;
end;
// then search the include file in directories defines in fpc.cfg (by -Fi option)
if (not IsVirtualUnit) and OnFindIncFileInFPCSrcDir(Self,AFilename,ExpFilename) then
// search the include file in directories defines in fpc.cfg (by -Fi option)
if FilenameIsAbsolute(Dir)
and Values.IsDefined('FPC')
and OnFindIncFileInFPCSrcDir(Self,AFilename,FoundFilename) then
begin
NewCode:=FOnLoadSource(Self,ExpFilename,true);
Result:=(NewCode<>nil);
exit;
NewCode:=FOnLoadSource(Self,FoundFilename,true);
if NewCode<>nil then
exit(true);
end;
end;
// search in directory of source of include directive
// Note: fpc 3.2.2 does not do that
// last: search in directory of source of include directive
// Note: fpc 3.2.2 does not do that, but for codetools it is more convenient to find it
if FilenameIsAbsolute(SrcFilename)
and (CompareFilenames(SrcFilename,FMainSourceFilename)<>0) then begin
ExpFilename:=ExtractFilePath(SrcFilename)+RelFilename;
NewCode:=LoadIncludeFile(ExpFilename);
Result:=(NewCode<>nil);
if Result then exit;
FoundFilename:=ExtractFilePath(SrcFilename)+RelFilename;
NewCode:=LoadIncludeFile(FoundFilename);
if NewCode<>nil then
exit(true);
end;
Result:=false;
@ -4212,7 +4200,6 @@ begin
DebugLn('TLinkScanner.SearchIncludeFile Filename="',AFilename,'"');
{$ENDIF}
NewCode:=nil;
IncludePath:='';
// beware of 'dir/file.inc'
HasPathDelims:=(System.Pos('/',AFilename)>0) or (System.Pos('\',AFilename)>0);

View File

@ -0,0 +1 @@
var Green: word;

View File

@ -0,0 +1 @@
the correct include file is Green.inc, if this file is found there is a bug

View File

@ -0,0 +1,7 @@
unit Star.Green3;
interface
implementation
end.

View File

@ -0,0 +1,9 @@
unit star.green1;
interface
uses star.green2;
implementation
end.

View File

@ -0,0 +1,7 @@
unit star.green2;
interface
implementation
end.

View File

@ -0,0 +1 @@
duplicate

View File

@ -0,0 +1,9 @@
unit star.orange1;
interface
uses star.green1;
implementation
end.

View File

@ -0,0 +1 @@
duplicate

View File

@ -0,0 +1,9 @@
unit star.red1;
interface
uses star.green1;
implementation
end.

View File

@ -0,0 +1,11 @@
unit star.main;
interface
uses star.red1, star.green1, star.green2, star.orange1;
{$I green/Green.inc}
implementation
end.

View File

@ -328,13 +328,13 @@ procedure TTestBasicCodeTools.TestDateToCfgStr;
Actual: String;
ActualDate: TDateTime;
begin
Actual:=DateToCfgStr(Date,aFormat);
Actual:=LazConfigStorage.DateToCfgStr(Date,aFormat);
if Actual<>Expected then begin
writeln(dbgsDiff(Expected,Actual));
AssertEquals('DateToCfgStr failed: Format="'+aFormat+'"',Expected,Actual);
exit;
end;
if (not CfgStrToDate(Actual,ActualDate,aFormat)) then begin
if (not LazConfigStorage.CfgStrToDate(Actual,ActualDate,aFormat)) then begin
AssertEquals('CfgStrToDate failed: Format="'+aFormat+'" Cfg="'+Actual+'"',false,true);
exit;
end;

View File

@ -58,12 +58,11 @@ unit TestFindDeclaration;
interface
uses
Classes, SysUtils, contnrs,
fpcunit, testregistry,
FileProcs, LazFileUtils, LazLogger,
CodeToolManager, ExprEval, CodeCache, BasicCodeTools,
Classes, SysUtils, contnrs, fpcunit, testregistry, FileProcs, LazFileUtils,
LazLogger, CodeToolManager, ExprEval, CodeCache, BasicCodeTools,
CustomCodeTool, CodeTree, FindDeclarationTool, KeywordFuncLists,
IdentCompletionTool, DefineTemplates, StrUtils, TestPascalParser;
IdentCompletionTool, DefineTemplates, DirectoryCacher, StrUtils,
TestPascalParser;
const
MarkDecl = '#'; // a declaration, must be unique
@ -139,12 +138,20 @@ type
procedure TestFindDeclaration_ArrayMultiDimDot;
procedure TestFindDeclaration_VarArgsOfType;
procedure TestFindDeclaration_ProcRef;
// unit/include search
procedure TestFindDeclaration_UnitSearch_CurrentDir;
procedure TestFindDeclaration_UnitSearch_StarStar;
procedure TestFindDeclaration_IncludeSearch_DirectiveWithPath;
procedure TestFindDeclaration_IncludeSearch_StarStar;
// directives
procedure TestFindDeclaration_DirectiveWithIn;
// test all files in directories:
procedure TestFindDeclaration_FPCTests;
procedure TestFindDeclaration_LazTests;
procedure TestFindDeclaration_LazTestsBugs;
procedure TestFindDeclaration_DirectiveWithIn;
end;
implementation
@ -1262,19 +1269,120 @@ begin
end;
end;
procedure TTestFindDeclaration.TestFindDeclaration_FPCTests;
procedure TTestFindDeclaration.TestFindDeclaration_UnitSearch_StarStar;
var
StarDir, UnitPath, Expected, anUnitName, InFilename,
FoundFilename: String;
DirDef, UnitPathDef: TDefineTemplate;
DirCache: TCTDirectoryCachePool;
begin
TestFiles('fpctests');
StarDir:=ExpandFileNameUTF8(SetDirSeparators('moduletests/star'));
DirDef:=TDefineTemplate.Create('TTestFindDeclaration_UnitSearch','','',StarDir,da_Directory);
try
CodeToolBoss.DefineTree.Add(DirDef);
UnitPathDef:=TDefineTemplate.Create('UnitPath','',UnitPathMacroName,DefinePathMacro+PathDelim+'**',da_DefineRecurse);
DirDef.AddChild(UnitPathDef);
CodeToolBoss.DefineTree.ClearCache;
// check unit path in some directories
Expected:=StarDir+PathDelim+'**';
UnitPath:=CodeToolBoss.GetUnitPathForDirectory(StarDir);
AssertEquals('unit path',Expected,UnitPath);
UnitPath:=CodeToolBoss.GetUnitPathForDirectory(StarDir+PathDelim+'green');
AssertEquals('unit path',Expected,UnitPath);
DirCache:=CodeToolBoss.DirectoryCachePool;
// searching a lowercase unit
anUnitName:='Star.Red1';
InFilename:='';
FoundFilename:=DirCache.FindUnitSourceInCompletePath(StarDir,anUnitName,InFilename);
Expected:=StarDir+PathDelim+'red/star.red1.pas';
AssertEquals('searching '+anUnitName,Expected,FoundFilename);
// searching a mixedcase unit
anUnitName:='Star.Green3';
InFilename:='';
FoundFilename:=DirCache.FindUnitSourceInCompletePath(StarDir,anUnitName,InFilename);
Expected:=StarDir+PathDelim+'green/Star.Green3.pas';
AssertEquals('searching '+anUnitName,Expected,FoundFilename);
// searching an anycase unit
anUnitName:='star.green3';
InFilename:='';
FoundFilename:=DirCache.FindUnitSourceInCompletePath(StarDir,anUnitName,InFilename,true);
Expected:=StarDir+PathDelim+'green/Star.Green3.pas';
AssertEquals('searching '+anUnitName,Expected,FoundFilename);
finally
CodeToolBoss.DefineTree.RemoveDefineTemplate(DirDef);
end;
end;
procedure TTestFindDeclaration.TestFindDeclaration_LazTests;
procedure TTestFindDeclaration.
TestFindDeclaration_IncludeSearch_DirectiveWithPath;
var
aFilename: String;
StarCode: TCodeBuffer;
Tool: TCodeTool;
begin
TestFiles('laztests');
aFilename:=ExpandFileNameUTF8(SetDirSeparators('moduletests/star/star.main.pas'));
StarCode:=CodeToolBoss.LoadFile(aFilename,true,false);
if not CodeToolBoss.Explore(STarCode,Tool,true) then begin
debugln('Error: '+CodeToolBoss.ErrorDbgMsg);
Fail('Explore failed: '+CodeToolBoss.ErrorMessage);
end;
end;
procedure TTestFindDeclaration.TestFindDeclaration_LazTestsBugs;
procedure TTestFindDeclaration.TestFindDeclaration_IncludeSearch_StarStar;
var
StarDir, IncPath, Expected, IncFilename, FoundFilename: String;
DirDef, IncPathDef: TDefineTemplate;
DirCache: TCTDirectoryCachePool;
begin
TestFiles('laztests', 'b*.p*');
StarDir:=ExpandFileNameUTF8(SetDirSeparators('moduletests/star'));
DirDef:=TDefineTemplate.Create('TTestFindDeclaration_IncudeSearch','','',StarDir,da_Directory);
try
CodeToolBoss.DefineTree.Add(DirDef);
IncPathDef:=TDefineTemplate.Create('IncPath','',IncludePathMacroName,DefinePathMacro+PathDelim+'**',da_DefineRecurse);
DirDef.AddChild(IncPathDef);
CodeToolBoss.DefineTree.ClearCache;
// check include search path in some directories
Expected:=StarDir+PathDelim+'**';
IncPath:=CodeToolBoss.GetIncludePathForDirectory(StarDir);
AssertEquals('include path',Expected,IncPath);
IncPath:=CodeToolBoss.GetIncludePathForDirectory(StarDir+PathDelim+'green');
AssertEquals('include path',Expected,IncPath);
DirCache:=CodeToolBoss.DirectoryCachePool;
// searching a lowercase include
IncFilename:='Star.inc';
FoundFilename:=DirCache.FindIncludeFileInCompletePath(StarDir,IncFilename);
Expected:=StarDir+PathDelim+'star.inc';
AssertEquals('searching '+IncFilename,Expected,FoundFilename);
// searching a mixedcase include
IncFilename:='Green.inc';
FoundFilename:=DirCache.FindIncludeFileInCompletePath(StarDir,IncFilename);
Expected:=StarDir+PathDelim+'green/Green.inc';
AssertEquals('searching '+IncFilename,Expected,FoundFilename);
// searching an include file without extension
IncFilename:='Green';
FoundFilename:=DirCache.FindIncludeFileInCompletePath(StarDir,IncFilename);
Expected:=StarDir+PathDelim+'green/Green.inc';
AssertEquals('searching '+IncFilename,Expected,FoundFilename);
finally
CodeToolBoss.DefineTree.RemoveDefineTemplate(DirDef);
end;
end;
procedure TTestFindDeclaration.TestFindDeclaration_DirectiveWithIn;
@ -1300,6 +1408,21 @@ begin
FindDeclarations(Code);
end;
procedure TTestFindDeclaration.TestFindDeclaration_FPCTests;
begin
TestFiles('fpctests');
end;
procedure TTestFindDeclaration.TestFindDeclaration_LazTests;
begin
TestFiles('laztests');
end;
procedure TTestFindDeclaration.TestFindDeclaration_LazTestsBugs;
begin
TestFiles('laztests', 'b*.p*');
end;
initialization
RegisterTests([TTestFindDeclaration]);
end.