codetools: added FindWithBlockStatement

git-svn-id: trunk@50264 -
This commit is contained in:
mattias 2015-11-09 16:32:28 +00:00
parent ab78cd25a0
commit a98baa8442
3 changed files with 124 additions and 34 deletions

View File

@ -242,13 +242,15 @@ type
function MoveCursorToParameterSpecifier(DefinitionNode: TCodeTreeNode function MoveCursorToParameterSpecifier(DefinitionNode: TCodeTreeNode
): boolean; ): boolean;
function GetFirstGroupVarNode(VarNode: TCodeTreeNode): TCodeTreeNode; function GetFirstGroupVarNode(VarNode: TCodeTreeNode): TCodeTreeNode;
function FindEndOfWithVar(WithVarNode: TCodeTreeNode): integer;
function NodeIsIdentifierInInterface(Node: TCodeTreeNode): boolean; function NodeIsIdentifierInInterface(Node: TCodeTreeNode): boolean;
function NodeCanHaveForwardType(TypeNode: TCodeTreeNode): boolean; function NodeCanHaveForwardType(TypeNode: TCodeTreeNode): boolean;
function NodeIsForwardType(TypeNode: TCodeTreeNode): boolean; function NodeIsForwardType(TypeNode: TCodeTreeNode): boolean;
function FindForwardTypeNode(TypeNode: TCodeTreeNode; function FindForwardTypeNode(TypeNode: TCodeTreeNode;
SearchFirst: boolean): TCodeTreeNode; SearchFirst: boolean): TCodeTreeNode;
function FindTypeOfForwardNode(TypeNode: TCodeTreeNode): TCodeTreeNode; function FindTypeOfForwardNode(TypeNode: TCodeTreeNode): TCodeTreeNode;
function FindEndOfWithExpr(WithVarNode: TCodeTreeNode): integer;
function ExtractWithBlockExpression(WithVarNode: TCodeTreeNode; Attr: TProcHeadAttributes = []): string;
function FindWithBlockStatement(WithVarNode: TCodeTreeNode): TCodeTreeNode;
// arrays // arrays
function ExtractArrayRange(ArrayNode: TCodeTreeNode; function ExtractArrayRange(ArrayNode: TCodeTreeNode;
@ -2830,14 +2832,41 @@ begin
end; end;
end; end;
function TPascalReaderTool.FindEndOfWithVar(WithVarNode: TCodeTreeNode): integer; function TPascalReaderTool.FindEndOfWithExpr(WithVarNode: TCodeTreeNode): integer;
begin begin
if WithVarNode.Desc<>ctnWithVariable then exit(-1);
MoveCursorToCleanPos(WithVarNode.StartPos); MoveCursorToCleanPos(WithVarNode.StartPos);
ReadNextAtom;
if not ReadTilVariableEnd(true,true) then exit(-1); if not ReadTilVariableEnd(true,true) then exit(-1);
UndoReadNextAtom; UndoReadNextAtom;
Result:=CurPos.EndPos; Result:=CurPos.EndPos;
end; end;
function TPascalReaderTool.ExtractWithBlockExpression(
WithVarNode: TCodeTreeNode; Attr: TProcHeadAttributes): string;
var
EndPos: Integer;
begin
EndPos:=FindEndOfWithExpr(WithVarNode);
if EndPos<1 then exit('');
Result:=ExtractCode(WithVarNode.StartPos,EndPos,Attr);
end;
function TPascalReaderTool.FindWithBlockStatement(WithVarNode: TCodeTreeNode
): TCodeTreeNode;
begin
Result:=WithVarNode;
repeat
if Result=nil then exit;
if Result.Desc<>ctnWithVariable then exit(nil);
if Result.FirstChild<>nil then begin
Result:=Result.FirstChild;
if Result.Desc=ctnWithStatement then exit;
exit(nil);
end;
until false;
end;
function TPascalReaderTool.NodeIsIdentifierInInterface(Node: TCodeTreeNode): boolean; function TPascalReaderTool.NodeIsIdentifierInInterface(Node: TCodeTreeNode): boolean;
// true if identifier is visible from other units (without prefixing) // true if identifier is visible from other units (without prefixing)
begin begin

View File

@ -297,8 +297,7 @@ type
function Apply: boolean; function Apply: boolean;
function FindEntryInRange(FromPos, ToPos: integer): TSourceChangeCacheEntry; function FindEntryInRange(FromPos, ToPos: integer): TSourceChangeCacheEntry;
function FindEntryAtPos(APos: integer): TSourceChangeCacheEntry; function FindEntryAtPos(APos: integer): TSourceChangeCacheEntry;
property BuffersToModify[Index: integer]: TCodeBuffer property BuffersToModify[Index: integer]: TCodeBuffer read GetBuffersToModify;
read GetBuffersToModify;
function BuffersToModifyCount: integer; function BuffersToModifyCount: integer;
function BufferIsModified(ACode: TCodeBuffer): boolean; function BufferIsModified(ACode: TCodeBuffer): boolean;
property OnBeforeApplyChanges: TOnBeforeApplyChanges property OnBeforeApplyChanges: TOnBeforeApplyChanges
@ -642,7 +641,8 @@ end;
function TSourceChangeCache.FindEntryInRange( function TSourceChangeCache.FindEntryInRange(
FromPos, ToPos: integer): TSourceChangeCacheEntry; FromPos, ToPos: integer): TSourceChangeCacheEntry;
var ANode: TAVLTreeNode; var
ANode: TAVLTreeNode;
NextNode: TAVLTreeNode; NextNode: TAVLTreeNode;
begin begin
ANode:=FEntries.Root; ANode:=FEntries.Root;

View File

@ -10,9 +10,12 @@ unit RefactoringTests;
interface interface
uses uses
Classes, SysUtils, CodeToolManager, CodeCache, CodeTree, LazLogger, Classes, SysUtils, CodeToolManager, CodeCache, CodeTree,
LazFileUtils, fpcunit, testregistry, FindDeclarationTests; BasicCodeTools, LazLogger, LazFileUtils, fpcunit, testregistry,
FindDeclarationTests;
const
ExplodeWithMarker = 'explodewith:';
type type
{ TTestRefactoring } { TTestRefactoring }
@ -31,49 +34,107 @@ implementation
{ TTestRefactoring } { TTestRefactoring }
procedure TTestRefactoring.TestExplodeWith; procedure TTestRefactoring.TestExplodeWith;
type
TWithBlock = record
CodeXYPos: TCodeXYPosition;
WithExpr: string;
StatementStartPos: integer;
StatementEndPos: integer;
end;
PWithBlock = ^TWithBlock;
var var
Code: TCodeBuffer; Code: TCodeBuffer;
Tool: TCodeTool; Tool: TCodeTool;
Node: TCodeTreeNode; Node, StatementNode: TCodeTreeNode;
CodeXYPos: TCodeXYPosition; CodeXYPos: TCodeXYPosition;
ListOfPCodeXYPosition: TFPList; ListOfWiths: array of TWithBlock;
i: Integer; i, NewStartPos, NewEndPos, p, CommentStartPos, CommentEndPos: Integer;
Filename, OldSource: String; Filename, OldSource, Src, ID, ExpectedInsertion: String;
aWith: PWithBlock;
begin begin
Filename:=ExpandFileNameUTF8('rt_explodewith.pas'); Filename:=ExpandFileNameUTF8('rt_explodewith.pas');
Code:=CodeToolBoss.LoadFile(Filename,true,false); Code:=CodeToolBoss.LoadFile(Filename,true,false);
AssertEquals('Load file error: '+Filename,true,Code<>nil); AssertEquals('Load file error: '+Filename,true,Code<>nil);
if not CodeToolBoss.Explore(Code,Tool,true) then if not CodeToolBoss.Explore(Code,Tool,true) then
AssertEquals('Parse error: ','',CodeToolBoss.ErrorMessage); AssertEquals('Parse error: ','',CodeToolBoss.ErrorMessage);
ListOfPCodeXYPosition:=nil; // collect all With-Blocks
try Node:=Tool.Tree.Root;
// collect all With-Blocks SetLength(ListOfWiths,0);
Node:=Tool.Tree.Root; while Node<>nil do begin
while Node<>nil do begin if Node.Desc=ctnWithVariable then begin
if Node.Desc=ctnWithVariable then begin Tool.CleanPosToCaret(Node.StartPos,CodeXYPos);
Tool.CleanPosToCaret(Node.StartPos,CodeXYPos); StatementNode:=Tool.FindWithBlockStatement(Node);
AddCodePosition(ListOfPCodeXYPosition,CodeXYPos); if StatementNode<>nil then begin
SetLength(ListOfWiths,length(ListOfWiths)+1);
aWith:=@ListOfWiths[High(ListOfWiths)];
aWith^.CodeXYPos:=CodeXYPos;
aWith^.WithExpr:=Tool.ExtractWithBlockExpression(Node,[]);
aWith^.StatementStartPos:=FindPrevNonSpace(Code.Source,StatementNode.StartPos);
aWith^.StatementEndPos:=StatementNode.EndPos;
end; end;
Node:=Node.Next;
end; end;
Node:=Node.Next;
end;
for i:=0 to ListOfPCodeXYPosition.Count-1 do begin for i:=0 to High(ListOfWiths) do begin
CodeXYPos:=PCodeXYPosition(ListOfPCodeXYPosition[i])^; aWith:=@ListOfWiths[i];
debugln(['TTestRefactoring.TestExplodeWith ',dbgs(CodeXYPos)]); CodeXYPos:=aWith^.CodeXYPos;
OldSource:=Code.Source; //debugln(['TTestRefactoring.TestExplodeWith ',dbgs(CodeXYPos)]);
try OldSource:=Code.Source;
if CodeToolBoss.RemoveWithBlock(Code,CodeXYPos.X,CodeXYPos.Y) then begin try
// check changes if CodeToolBoss.RemoveWithBlock(Code,CodeXYPos.X,CodeXYPos.Y) then begin
// success
end else begin // => check changes
AssertEquals('CodeToolBoss.RemoveWithBlock failed at '+dbgs(CodeXYPos),'',CodeToolBoss.ErrorMessage); // get new bounds
NewStartPos:=aWith^.StatementStartPos;
NewEndPos:=aWith^.StatementEndPos;
Code.AdjustPosition(NewStartPos);
Code.AdjustPosition(NewEndPos);
if (NewStartPos<1) or (NewStartPos>Code.SourceLength)
or (NewEndPos<1) or (NewEndPos>Code.SourceLength)
or (NewEndPos<NewStartPos)
then begin
debugln(['TTestRefactoring.TestExplodeWith WrongCode: ']);
debugln(Code.Source);
Fail('CodeToolBoss.RemoveWithBlock failed at '+dbgs(CodeXYPos));
end; end;
finally // check each marker
Code.Source:=OldSource; Src:=Code.Source;
//debugln(['TTestRefactoring.TestExplodeWith ',copy(Src,NewStartPos,NewEndPos-NewStartPos)]);
p:=NewStartPos;
repeat
CommentStartPos:=FindNextComment(Src,p,NewEndPos);
if CommentStartPos>=NewEndPos then break;
p:=CommentStartPos;
CommentEndPos:=FindCommentEnd(Src,CommentStartPos,Tool.Scanner.NestedComments);
if Src[p]='{' then begin
inc(p);
if copy(Src,p,length(ExplodeWithMarker))=ExplodeWithMarker then begin
inc(p,length(ExplodeWithMarker));
ID:=copy(Src,p,CommentEndPos-p-1);
if ID=aWith^.WithExpr then begin
// this marker expects an insertion
ExpectedInsertion:=Id+'.';
if copy(Src,CommentEndPos,length(ExpectedInsertion))<>ExpectedInsertion
then begin
Fail('CodeToolBoss.RemoveWithBlock failed at '+dbgs(CodeXYPos)
+': Expected insertion "'+ExpectedInsertion+'"'
+' at '+Code.AbsoluteToLineColStr(CommentEndPos)
+', but found "'+dbgstr(Src,CommentStartPos,20)+'"');
end;
end;
end;
end;
p:=CommentEndPos;
until false;
end else begin
Fail('CodeToolBoss.RemoveWithBlock failed at '+dbgs(CodeXYPos)+': '+CodeToolBoss.ErrorMessage);
end; end;
finally
Code.Source:=OldSource;
end; end;
finally
FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
end; end;
end; end;