mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-16 18:09:23 +02:00
codetools: added FindWithBlockStatement
git-svn-id: trunk@50264 -
This commit is contained in:
parent
ab78cd25a0
commit
a98baa8442
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user