implemented codetools functions to collect search paths and to unitfiles in searchpaths

git-svn-id: trunk@8350 -
This commit is contained in:
mattias 2005-12-21 11:47:03 +00:00
parent 46291e2df0
commit 55b7e83e1c
5 changed files with 418 additions and 98 deletions

View File

@ -32,11 +32,10 @@ unit BasicCodeTools;
interface
uses
Classes, SysUtils, SourceLog, KeywordFuncLists, FileProcs;
Classes, SysUtils, AVL_Tree, SourceLog, KeywordFuncLists, FileProcs;
//----------------------------------------------------------------------------
{ These functions are used by the codetools
}
{ These functions are used by the codetools }
// comments
function FindNextNonSpace(const ASource: string; StartPos: integer): integer;
@ -136,6 +135,29 @@ function SplitStringConstant(const StringConstant: string;
procedure ImproveStringConstantStart(const ACode: string; var StartPos: integer);
procedure ImproveStringConstantEnd(const ACode: string; var EndPos: integer);
// files
type
{ TUnitFileInfo }
TUnitFileInfo = class
private
FFilename: string;
FUnitName: string;
public
constructor Create(const TheUnitName, TheFilename: string);
property UnitName: string read FUnitName;
property Filename: string read FFilename;
end;
function GatherUnitFiles(const BaseDir, SearchPath,
Extensions: string; KeepDoubles, CaseInsensitive: boolean;
var TreeOfUnitFiles: TAVLTree): boolean;
procedure FreeTreeOfUnitFiles(TreeOfUnitFiles: TAVLTree);
function CompareUnitFileInfos(Data1, Data2: Pointer): integer;
function CompareUnitNameAndUnitFileInfo(UnitnamePAnsiString,
UnitFileInfo: Pointer): integer;
// other useful stuff
procedure RaiseCatchableException(const Msg: string);
@ -3122,6 +3144,206 @@ begin
until AtomEndPos>=EndPos;
end;
function GatherUnitFiles(const BaseDir, SearchPath,
Extensions: string; KeepDoubles, CaseInsensitive: boolean;
var TreeOfUnitFiles: TAVLTree): boolean;
// BaseDir: base directory, used when SearchPath is relative
// SearchPath: semicolon separated list of directories
// Extensions: semicolon separated list of extensions (e.g. 'pas;.pp;ppu')
// KeepDoubles: false to return only the first match of each unit
// CaseInsensitive: true to ignore case on comparing extensions
// TreeOfUnitFiles: tree of TUnitFileInfo
var
SearchedDirectories: TAVLTree; // tree of PAnsiString
function DirectoryAlreadySearched(const ADirectory: string): boolean;
begin
Result:=false;
end;
procedure MarkDirectoryAsSearched(const ADirectory: string);
var
s: String;
begin
// increase refcount
s:=ADirectory;
if SearchedDirectories=nil then
SearchedDirectories:=TAVLTree.Create(@ComparePAnsiStringFilenames);
SearchedDirectories.Add(@s);
Pointer(s):=nil;
end;
procedure FreeSearchedDirectories;
var
ANode: TAVLTreeNode;
s: String;
begin
if SearchedDirectories=nil then exit;
s:='';
ANode:=SearchedDirectories.FindLowest;
while ANode<>nil do begin
Pointer(s):=ANode.Data;
s:=''; // decrease refcount
ANode:=SearchedDirectories.FindSuccessor(ANode);
end;
if s='' then ;
SearchedDirectories.Free;
end;
function ExtensionFits(const Filename: string): boolean;
var
ExtStart: Integer;
ExtLen: Integer; // length without '.'
CurExtStart: Integer;
CurExtEnd: LongInt;
CompareCaseInsensitive: Boolean;
p: Integer;
begin
CompareCaseInsensitive:=CaseInsensitive;
{$IFDEF Win32}
CompareCaseInsensitive:=true;
{$ENDIF}
ExtStart:=length(Filename);
while (ExtStart>=1) and (not (Filename[ExtStart] in [PathDelim,'.'])) do
dec(ExtStart);
if (ExtStart>0) and (Filename[ExtStart]='.') then begin
// filename has an extension
ExtLen:=length(Filename)-ExtStart;
inc(ExtStart);
CurExtStart:=1;
while (CurExtStart<=length(Extensions)) do begin
// skip '.'
if Extensions[CurExtStart]='.' then inc(CurExtStart);
// read till semicolon
CurExtEnd:=CurExtStart;
while (CurExtEnd<=length(Extensions)) and (Extensions[CurExtEnd]<>';')
do
inc(CurExtEnd);
if (CurExtEnd>CurExtStart) and (CurExtEnd-CurExtStart=ExtLen) then begin
// compare extension
p:=ExtLen-1;
while (p>=0) do begin
if CompareCaseInsensitive then begin
if UpChars[Filename[ExtStart+p]]
<>UpChars[Extensions[CurExtStart+p]]
then
break;
end else begin
if Filename[ExtStart+p]<>Extensions[CurExtStart+p] then
break;
end;
dec(p);
end;
if p<0 then begin
// extension fit
Result:=true;
exit;
end;
end;
CurExtStart:=CurExtEnd+1;
end;
end;
Result:=false;
end;
procedure AddFilename(const Filename: string);
var
AnUnitName: String;
NewItem: TUnitFileInfo;
begin
AnUnitName:=ExtractFileNameOnly(Filename);
if (not KeepDoubles) then begin
if (TreeOfUnitFiles<>nil)
and (TreeOfUnitFiles.FindKey(Pointer(AnUnitName),
@CompareUnitNameAndUnitFileInfo)<>nil)
then begin
// an unit with the same name was already found and doubles are not
// wanted
exit;
end;
end;
// add
if TreeOfUnitFiles=nil then
TreeOfUnitFiles:=TAVLTree.Create(@CompareUnitFileInfos);
NewItem:=TUnitFileInfo.Create(AnUnitName,Filename);
TreeOfUnitFiles.Add(NewItem);
end;
function SearchDirectory(const ADirectory: string): boolean;
var
FileInfo: TSearchRec;
begin
Result:=true;
if DirectoryAlreadySearched(ADirectory) then exit;
MarkDirectoryAsSearched(ADirectory);
if not DirPathExists(ADirectory) then exit;
if SysUtils.FindFirst(ADirectory+FileMask,faAnyFile,FileInfo)=0 then begin
repeat
// check if special file
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
then
continue;
if ExtensionFits(FileInfo.Name) then begin
AddFilename(FileInfo.Name);
end;
until SysUtils.FindNext(FileInfo)<>0;
end;
SysUtils.FindClose(FileInfo);
end;
var
PathStartPos: Integer;
PathEndPos: LongInt;
CurDir: String;
begin
Result:=false;
SearchedDirectories:=nil;
try
// search all paths in SearchPath
PathStartPos:=1;
while PathStartPos<=length(SearchPath) do begin
PathEndPos:=PathStartPos;
while (PathEndPos<=length(SearchPath)) and (SearchPath[PathEndPos]<>';')
do
inc(PathEndPos);
if PathEndPos>PathStartPos then begin
CurDir:=AppendPathDelim(TrimFilename(
copy(SearchPath,PathStartPos,PathEndPos-PathStartPos)));
if not FilenameIsAbsolute(CurDir) then
CurDir:=AppendPathDelim(BaseDir)+CurDir;
if not SearchDirectory(AppendPathDelim(CurDir)) then exit;
end;
PathStartPos:=PathEndPos;
while (PathStartPos<=length(SearchPath))
and (SearchPath[PathStartPos]=';') do
inc(PathStartPos);
end;
Result:=true;
finally
FreeSearchedDirectories;
end;
end;
procedure FreeTreeOfUnitFiles(TreeOfUnitFiles: TAVLTree);
begin
TreeOfUnitFiles.FreeAndClear;
end;
function CompareUnitFileInfos(Data1, Data2: Pointer): integer;
begin
Result:=SysUtils.CompareText(TUnitFileInfo(Data1).UnitName,
TUnitFileInfo(Data2).UnitName);
end;
function CompareUnitNameAndUnitFileInfo(UnitnamePAnsiString,
UnitFileInfo: Pointer): integer;
begin
Result:=SysUtils.CompareText(PAnsiString(UnitnamePAnsiString)^,
TUnitFileInfo(UnitFileInfo).UnitName);
end;
procedure RaiseCatchableException(const Msg: string);
begin
{ Raises an exception.
@ -3234,6 +3456,14 @@ begin
Result:=CompareText(Txt1,Len1,Txt2,Len2,CaseSensitive);
end;
{ TUnitFileInfo }
constructor TUnitFileInfo.Create(const TheUnitName, TheFilename: string);
begin
FUnitName:=TheUnitName;
FFilename:=TheFilename;
end;
//=============================================================================

View File

@ -84,6 +84,8 @@ function ClearFile(const Filename: string; RaiseOnError: boolean): boolean;
function GetTempFilename(const Path, Prefix: string): string;
function FindDiskFilename(const Filename: string): string;
function ComparePAnsiStringFilenames(Data1, data2: Pointer): integer;
type
TCTPascalExtType = (petNone, petPAS, petPP, petP);
@ -364,6 +366,16 @@ begin
until StartPos>length(Result);
end;
function ComparePAnsiStringFilenames(Data1, data2: Pointer): integer;
var
s1: String;
s2: String;
begin
s1:=PAnsiString(Data1)^;
s2:=PAnsiString(Data1)^;
Result:=CompareFilenames(s1,s2);
end;
function CompareFilenames(const Filename1, Filename2: string): integer;
begin
{$IFDEF WIN32}

View File

@ -422,7 +422,7 @@ type
IdentifierTool: TFindDeclarationTool;
FoundProc: PFoundProc;
end;
TFindDeclarationParams = class(TObject)
public
// input parameters:
@ -482,7 +482,6 @@ type
const
AllFindSmartFlags = [fsfIncludeDirective];
type
//----------------------------------------------------------------------------
ECodeToolUnitNotFound = class(ECodeToolFileNotFound)
@ -659,6 +658,7 @@ type
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
function FindUnitSource(const AnUnitName,
AnUnitInFilename: string; ExceptionOnNotFound: boolean): TCodeBuffer;
procedure GatherUnitAndSrcPath(var UnitPath, SrcPath: string);
function FindSmartHint(const CursorPos: TCodeXYPosition): string;
function BaseTypeOfNodeHasSubIdents(ANode: TCodeTreeNode): boolean;
function FindBaseTypeOfNode(Params: TFindDeclarationParams;
@ -1503,7 +1503,8 @@ var
end;
function SearchUnitInUnitLinks(const TheUnitName: string): TCodeBuffer;
var UnitLinks, CurFilename: string;
var
UnitLinks, CurFilename: string;
UnitLinkStart, UnitLinkEnd, UnitLinkLen: integer;
pe: TCTPascalExtType;
begin
@ -1632,7 +1633,7 @@ begin
end;
end else begin
// normal unit name
// first source search in current directory (= where the maincode is)
// first search in current directory (= where the maincode is)
{$IFDEF ShowTriedFiles}
DebugLn('TFindDeclarationTool.FindUnitSource Search in current dir=',CurDir);
{$ENDIF}
@ -1706,8 +1707,7 @@ begin
// there is a compiled unit, only the source was not found
RaiseExceptionInstance(
ECodeToolUnitNotFound.Create(Self,
Format(ctsSourceNotFoundUnit, [CompiledResult.Filename]),
AnUnitName));
Format(ctsSourceNotFoundUnit, [CompiledResult.Filename]),AnUnitName));
end else begin
// nothing found
RaiseExceptionInstance(
@ -1717,6 +1717,71 @@ begin
end;
end;
procedure TFindDeclarationTool.GatherUnitAndSrcPath(var UnitPath,
SrcPath: string);
var
CurDir: String;
procedure SearchCompiledSrcPaths(const APath: string);
var
PathStart, PathEnd: integer;
ADir: string;
CurCompiledSrcPath: string;
begin
if not Assigned(OnGetSrcPathForCompiledUnit) then begin
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
SrcPath:=SrcPath+';'+CurCompiledSrcPath;
end;
end;
PathStart:=PathEnd+1;
end;
end;
var
MainCodeIsVirtual: Boolean;
begin
UnitPath:='';
SrcPath:='';
MainCodeIsVirtual:=TCodeBuffer(Scanner.MainCode).IsVirtual;
if not MainCodeIsVirtual then begin
CurDir:=ExtractFilePath(TCodeBuffer(Scanner.MainCode).Filename);
end else begin
CurDir:='';
end;
// first search in current directory (= where the maincode is)
UnitPath:=CurDir;
// search source in search path
if Assigned(OnGetUnitSourceSearchPath) then begin
SrcPath:=SrcPath+';'+OnGetUnitSourceSearchPath(Self);
end else begin
UnitPath:=UnitPath+';'+Scanner.Values[ExternalMacroStart+'UnitPath'];
SrcPath:=SrcPath+';'+Scanner.Values[ExternalMacroStart+'SrcPath'];
end;
// search for compiled unit
// -> search in every unit path for a CompiledSrcPath and search there
SearchCompiledSrcPaths(UnitPath);
end;
function TFindDeclarationTool.FindSmartHint(const CursorPos: TCodeXYPosition
): string;
var

View File

@ -226,6 +226,8 @@ type
const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions);
procedure GatherUsefulIdentifiers(CleanPos: integer;
const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions);
procedure GatherUnitnames(CleanPos: integer;
const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions);
public
function GatherIdentifiers(const CursorPos: TCodeXYPosition;
var IdentifierList: TIdentifierList;
@ -938,6 +940,12 @@ begin
end;
end;
procedure TIdentCompletionTool.GatherUnitnames(CleanPos: integer;
const Context: TFindContext; BeautifyCodeOptions: TBeautifyCodeOptions);
begin
end;
function TIdentCompletionTool.GatherIdentifiers(
const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList;
BeautifyCodeOptions: TBeautifyCodeOptions): boolean;
@ -1007,99 +1015,103 @@ begin
' Ident=',copy(Src,IdentStartPos,IdentEndPos-IdentStartPos));
{$ENDIF}
GatherContext:=CreateFindContext(Self,CursorNode);
ContextExprStartPos:=GetContextExprStartPos(IdentStartPos,CursorNode);
if GatherContext.Node.Desc=ctnWithVariable then
GatherContext.Node:=GatherContext.Node.Parent;
if CursorNode.Desc=ctnUsesSection then begin
GatherUnitNames(IdentStartPos,GatherContext,BeautifyCodeOptions);
end else begin
ContextExprStartPos:=GetContextExprStartPos(IdentStartPos,CursorNode);
if GatherContext.Node.Desc=ctnWithVariable then
GatherContext.Node:=GatherContext.Node.Parent;
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers C',
' ContextExprStartPos=',dbgs(ContextExprStartPos),
' Expr=',StringToPascalConst(copy(Src,ContextExprStartPos,
IdentStartPos-ContextExprStartPos)));
{$ENDIF}
StartInSubContext:=false;
if ContextExprStartPos<IdentStartPos then begin
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers C',
' ContextExprStartPos=',dbgs(ContextExprStartPos),
' Expr=',StringToPascalConst(copy(Src,ContextExprStartPos,
IdentStartPos-ContextExprStartPos)));
{$ENDIF}
StartInSubContext:=false;
if ContextExprStartPos<IdentStartPos then begin
MoveCursorToCleanPos(IdentStartPos);
Params.ContextNode:=CursorNode;
Params.SetIdentifier(Self,nil,nil);
Params.Flags:=[fdfExceptionOnNotFound,
fdfSearchInParentNodes,fdfSearchInAncestors];
ExprType:=FindExpressionTypeOfVariable(ContextExprStartPos,IdentStartPos,
Params);
if (ExprType.Desc=xtContext) then begin
GatherContext:=ExprType.Context;
StartInSubContext:=true;
end;
end;
// search and gather identifiers in context
if (GatherContext.Tool<>nil) and (GatherContext.Node<>nil) then begin
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers D CONTEXT: ',
GatherContext.Tool.MainFilename,
' ',GatherContext.Node.DescAsString,
' "',StringToPascalConst(copy(GatherContext.Tool.Src,GatherContext.Node.StartPos,50)),'"');
{$ENDIF}
// gather all identifiers in context
Params.ContextNode:=GatherContext.Node;
Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable];
if not StartInSubContext then
Include(Params.Flags,fdfSearchInParentNodes);
if Params.ContextNode.Desc in [ctnClass,ctnClassInterface] then
Exclude(Params.Flags,fdfSearchInParentNodes);
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers F');
{$ENDIF}
CurrentIdentifierList.Context:=GatherContext;
GatherContext.Tool.FindIdentifierInContext(Params);
end;
// add useful identifiers without context
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers G');
{$ENDIF}
GatherUsefulIdentifiers(IdentStartPos,GatherContext,BeautifyCodeOptions);
// check for incomplete context
// context bracket level
CurrentIdentifierList.StartBracketLvl:=
GetBracketLvl(Src,CursorNode.StartPos,IdentStartPos,
Scanner.NestedComments);
// context behind
if IdentEndPos<SrcLen then begin
MoveCursorToCleanPos(IdentEndPos);
ReadNextAtom;
CurrentIdentifierList.StartAtomBehind:=CurPos;
// check if in statement
if CursorNode.Desc in AllPascalStatements then begin
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartInStatement];
// check if at end of statement
if (CurPos.Flag in [cafEnd,cafBegin])
or ((not UpAtomIs('ELSE'))
and (CurPos.Flag=cafWord)
and (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
then
if CurrentIdentifierList.StartBracketLvl=0 then
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfContextNeedsEndSemicolon];
end;
end;
// context in front of
MoveCursorToCleanPos(IdentStartPos);
Params.ContextNode:=CursorNode;
Params.SetIdentifier(Self,nil,nil);
Params.Flags:=[fdfExceptionOnNotFound,
fdfSearchInParentNodes,fdfSearchInAncestors];
ExprType:=FindExpressionTypeOfVariable(ContextExprStartPos,IdentStartPos,
Params);
if (ExprType.Desc=xtContext) then begin
GatherContext:=ExprType.Context;
StartInSubContext:=true;
end;
end;
// search and gather identifiers in context
if (GatherContext.Tool<>nil) and (GatherContext.Node<>nil) then begin
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers D CONTEXT: ',
GatherContext.Tool.MainFilename,
' ',GatherContext.Node.DescAsString,
' "',StringToPascalConst(copy(GatherContext.Tool.Src,GatherContext.Node.StartPos,50)),'"');
{$ENDIF}
// gather all identifiers in context
Params.ContextNode:=GatherContext.Node;
Params.SetIdentifier(Self,nil,@CollectAllIdentifiers);
Params.Flags:=[fdfSearchInAncestors,fdfCollect,fdfFindVariable];
if not StartInSubContext then
Include(Params.Flags,fdfSearchInParentNodes);
if Params.ContextNode.Desc in [ctnClass,ctnClassInterface] then
Exclude(Params.Flags,fdfSearchInParentNodes);
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers F');
{$ENDIF}
CurrentIdentifierList.Context:=GatherContext;
GatherContext.Tool.FindIdentifierInContext(Params);
end;
// add useful identifiers without context
{$IFDEF CTDEBUG}
DebugLn('TIdentCompletionTool.GatherIdentifiers G');
{$ENDIF}
GatherUsefulIdentifiers(IdentStartPos,GatherContext,BeautifyCodeOptions);
// check for incomplete context
// context bracket level
CurrentIdentifierList.StartBracketLvl:=
GetBracketLvl(Src,CursorNode.StartPos,IdentStartPos,
Scanner.NestedComments);
// context behind
if IdentEndPos<SrcLen then begin
MoveCursorToCleanPos(IdentEndPos);
ReadNextAtom;
CurrentIdentifierList.StartAtomBehind:=CurPos;
// check if in statement
if CursorNode.Desc in AllPascalStatements then begin
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartInStatement];
// check if at end of statement
if (CurPos.Flag in [cafEnd,cafBegin])
or ((not UpAtomIs('ELSE'))
and (CurPos.Flag=cafWord)
and (not PositionsInSameLine(Src,IdentEndPos,CurPos.StartPos)))
ReadPriorAtom;
CurrentIdentifierList.StartAtomInFront:=CurPos;
// check if LValue
if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags) then begin
if (CurPos.Flag in [cafSemicolon,cafBegin,cafEnd])
or WordIsBlockKeyWord.DoItUpperCase(UpperSrc,
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
then
if CurrentIdentifierList.StartBracketLvl=0 then
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfContextNeedsEndSemicolon];
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartIsLValue];
end;
end;
// context in front of
MoveCursorToCleanPos(IdentStartPos);
ReadPriorAtom;
CurrentIdentifierList.StartAtomInFront:=CurPos;
// check if LValue
if (ilcfStartInStatement in CurrentIdentifierList.ContextFlags) then begin
if (CurPos.Flag in [cafSemicolon,cafBegin,cafEnd])
or WordIsBlockKeyWord.DoItUpperCase(UpperSrc,
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
then
CurrentIdentifierList.ContextFlags:=
CurrentIdentifierList.ContextFlags+[ilcfStartIsLValue];
end;
Result:=true;
finally

View File

@ -120,7 +120,8 @@ function TrimSearchPath(const SearchPath, BaseDirectory: string): string;
function MergeSearchPaths(const OldSearchPath, AddSearchPath: string): string;
function RemoveSearchPaths(const SearchPath, RemoveSearchPath: string): string;
function CreateRelativeSearchPath(const SearchPath, BaseDirectory: string): string;
function ShortenSearchPath(const SearchPath, BaseDirectory, ChompDirectory: string): string;
function ShortenSearchPath(const SearchPath, BaseDirectory,
ChompDirectory: string): string;
function GetNextDirectoryInSearchPath(const SearchPath: string;
var NextStartPos: integer): string;
function GetNextUsedDirectoryInSearchPath(const SearchPath,