codetools: FindFileAtCursor: unit name, search in comment

git-svn-id: trunk@53311 -
This commit is contained in:
mattias 2016-11-08 12:20:40 +00:00
parent 6e64d8a23d
commit 4eaf02c3a1
4 changed files with 130 additions and 41 deletions

View File

@ -1504,6 +1504,7 @@ end;
function FindCommentEnd(const ASource: string; StartPos: integer;
NestedComments: boolean): integer;
// returns position after the comment end, e.g. after }
// failure: returns length(ASource)+1
var
CommentLvl: integer;
p: PChar;
@ -2900,7 +2901,7 @@ begin
else
if Result>1 then begin
c2:=Source[Result-1];
// test for double char operators :=, +=, -=, /=, *=, <>, <=, >=, **, ><, ..
// test for double char operators :=, +=, -=, /=, *=, <>, <=, >=, **, ><, @@ ..
if ((c2='=') and (IsEqualOperatorStartChar[c]))
or ((c='<') and (c2='>'))
or ((c='>') and (c2='<'))

View File

@ -668,7 +668,8 @@ type
ffatResource,
ffatDisabledResource,
ffatLiteral,
ffatComment
ffatComment,
ffatUnit // unit by name
);
TFindFileAtCursorFlags = set of TFindFileAtCursorFlag;
const
@ -3560,10 +3561,71 @@ var
end;
end;
function CheckPlainComments(Source: string; CurAbsPos: integer): boolean;
var
Filename: String;
p, EndPos, FileStartPos, FileEndPos, MinPos, MaxPos: Integer;
begin
// check if cursor in a comment (ignoring directives)
Result:=false;
CursorPos.Code.LineColToPosition(CursorPos.Y,CursorPos.X,CurAbsPos);
Source:=CursorPos.Code.Source;
if (CurAbsPos<1) or (CurAbsPos>length(Source)) then exit;
p:=1;
repeat
p:=FindNextComment(Source,p);
if p>CurAbsPos then break;
EndPos:=FindCommentEnd(Source,p,Scanner.NestedComments);
if EndPos>CurAbsPos then begin
// cursor in comment
MinPos:=p+1;
MaxPos:=EndPos-1;
if Source[p]<>'{' then begin
inc(MinPos);
dec(MaxPos);
end;
FileStartPos:=CurAbsPos;
while (FileStartPos>MinPos) and not (Source[FileStartPos-1] in [#0..#32]) do
dec(FileStartPos);
FileEndPos:=CurAbsPos;
while (FileEndPos<MaxPos) and not (Source[FileEndPos] in [#0..#32]) do
inc(FileEndPos);
Filename:=TrimFilename(copy(Source,FileStartPos,FileEndPos-FileStartPos));
if not FilenameIsAbsolute(Filename) then
Filename:=ResolveDots(ExtractFilePath(MainFilename)+Filename);
if Scanner.OnLoadSource(Scanner,Filename,false)<>nil then begin
Found:=ffatComment;
FoundFilename:=Filename;
exit(true);
end;
exit;
end;
p:=EndPos;
until false;
end;
function CheckUnitByWordAtCursor(Source: string; CurAbsPos: integer): boolean;
// e.g. 'Sy|sUtils.CompareText'
var
AnUnitName: String;
Code: TCodeBuffer;
p: Integer;
begin
Result:=false;
p:=FindStartOfAtom(Source,CurAbsPos);
if p<1 then exit;
AnUnitName:=GetIdentifier(@Source[p]);
Code:=FindUnitSource(AnUnitName,'',false);
if Code=nil then exit;
Found:=ffatUnit;
FoundFilename:=Code.Filename;
Result:=true;
end;
var
CommentStart, CommentEnd, Col, StartCol: integer;
CommentStart, CommentEnd, Col, StartCol, CurAbsPos: integer;
Node: TCodeTreeNode;
aUnitName, UnitInFilename, Line, Literal: string;
aUnitName, UnitInFilename, Line, Literal, aSource: string;
NewCode: TCodeBuffer;
p, StartP: PChar;
begin
@ -3571,7 +3633,10 @@ begin
Found:=ffatNone;
FoundFilename:='';
if StartPos<>nil then
StartPos^:=CleanCodeXYPosition;
StartPos^:=CleanCodeXYPosition;
if CursorPos.Code.LineColIsOutside(CursorPos.Y,CursorPos.X) then exit;
if CursorPos.Code.LineColIsSpace(CursorPos.Y,CursorPos.X) then exit;
if (CursorPos.Y<1) or (CursorPos.Y>CursorPos.Code.LineCount) then exit;
{$IFDEF VerboseFindFileAtCursor}
debugln(['TFindDeclarationTool.FindFileAtCursor START']);
{$ENDIF}
@ -3663,42 +3728,52 @@ begin
if ffatLiteral in SearchFor then begin
// check literal
p:=PChar(Line);
Col:=1;
repeat
if p^=#0 then begin
case p^ of
#0:
break;
end else if p^='''' then begin
StartCol:=Col;
inc(Col);
inc(p);
StartP:=p;
repeat
case p^ of
#0: break;
'''': break;
#9: Col:=(Col+8) and not 7;
else inc(p,UTF8CharacterLength(p));
end;
until false;
if (CursorPos.X>=StartCol) and (CursorPos.X<=Col) then begin
Literal:=copy(Line,Col,p-StartP);
if not FilenameIsAbsolute(Literal) then
Literal:=TrimFilename(ExtractFilePath(Scanner.MainFilename)+Literal);
if FilenameIsAbsolute(Literal)
and DirectoryCache.Pool.FileExists(Literal) then begin
'''':
begin
StartCol:=p-PChar(Line)+1;
inc(p);
StartP:=p;
repeat
case p^ of
#0,'''': break;
else inc(p);
end;
until false;
Col:=p-PChar(Line)+1;
writeln('TFindDeclarationTool.FindFileAtCursor Col=',Col,' CursorCol=',CursorPos.X,' Literal=',copy(Line,StartCol+1,p-StartP));
if (p>StartP) and (CursorPos.X>=StartCol) and (CursorPos.X<=Col) then begin
Literal:=copy(Line,StartCol+1,p-StartP);
if not FilenameIsAbsolute(Literal) then
Literal:=TrimFilename(ExtractFilePath(Scanner.MainFilename)+Literal);
Found:=ffatLiteral;
FoundFilename:=Literal;
exit(true);
end;
if p^=#0 then break;
// p is now on the ending '
end;
if p^=#0 then break;
end;
inc(p);
inc(Col);
until false;
end;
// search without node tree with basic tools
CursorPos.Code.LineColToPosition(CursorPos.Y,CursorPos.X,CurAbsPos);
aSource:=CursorPos.Code.Source;
if (CurAbsPos<1) or (CurAbsPos>length(aSource)) then exit;
if ffatComment in SearchFor then begin
// ToDo: check simple
// ignore syntax and only read comments
if CheckPlainComments(aSource,CurAbsPos) then exit(true);
end;
if ffatUnit in SearchFor then begin
if CheckUnitByWordAtCursor(aSource,CurAbsPos) then exit(true);
end;
end;

View File

@ -57,7 +57,6 @@ type
procedure ExtendXmlDocument(Doc: TXMLDocument); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
@ -142,16 +141,11 @@ begin
Doc.FirstChild.AppendChild(env);
end;
constructor TCTTestRunner.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
DefaultFormat:=fPlain;
DefaultRunAllTests:=true;
end;
var
App: TCTTestRunner;
begin
DefaultFormat:=fPlain;
DefaultRunAllTests:=True;
App := TCTTestRunner.Create(nil);
App.Initialize;
App.Title := 'FPCUnit Console runner for the CodeTools Find Declaration Suite.';

View File

@ -402,17 +402,36 @@ begin
AssertEquals('FindFileAtCursor in literal Found',ord(ffatLiteral),ord(Found));
AssertEquals('FindFileAtCursor in literal FoundFilename','unit2.pas',FoundFilename);
// --- comment ---
Code.Source:='program test1;'+LineEnding
+'{unit2.pas}'+LineEnding;
if not CodeToolBoss.FindFileAtCursor(Code,3,2,Found,FoundFilename) then
Fail('CodeToolBoss.FindFileAtCursor in comment');
AssertEquals('FindFileAtCursor in comment Found',ord(ffatComment),ord(Found));
AssertEquals('FindFileAtCursor in comment FoundFilename','unit2.pas',FoundFilename);
// --- unit name search in comment ---
Code.Source:='program test1;'+LineEnding
+'{unit2}'+LineEnding;
if not CodeToolBoss.FindFileAtCursor(Code,3,2,Found,FoundFilename) then
Fail('CodeToolBoss.FindFileAtCursor in comment');
AssertEquals('FindFileAtCursor in comment Found',ord(ffatUnit),ord(Found));
AssertEquals('FindFileAtCursor in comment FoundFilename','unit2.pas',FoundFilename);
// --- unit name search in code ---
Code.Source:='program test1;'+LineEnding
+'begin'+LineEnding
+' unit2.Test;'+LineEnding;
if not CodeToolBoss.FindFileAtCursor(Code,3,3,Found,FoundFilename) then
Fail('CodeToolBoss.FindFileAtCursor in comment');
AssertEquals('FindFileAtCursor in comment Found',ord(ffatUnit),ord(Found));
AssertEquals('FindFileAtCursor in comment FoundFilename','unit2.pas',FoundFilename);
finally
Code.IsDeleted:=true;
SubUnit2Code.IsDeleted:=true;
LFMCode.IsDeleted:=true;
end;
// ToDo: test $i 'file with spaces' in code
// ToDo: test $i in disabled code
// ToDo: test 'readme.txt' in active code
// ToDo: test readme.txt in active code fails
// ToDo: test readme.txt in comment works
end;
procedure TTestFindDeclaration.TestFindDeclaration_FPCTests;