coetools: extract proc: implemented collecting and ingnoring missing identifiers

git-svn-id: trunk@13559 -
This commit is contained in:
mattias 2008-01-01 19:35:37 +00:00
parent f67db8d99a
commit fd8a3de7c9
7 changed files with 618 additions and 405 deletions

View File

@ -39,7 +39,7 @@ uses
{$IFDEF MEM_CHECK}
MemCheck,
{$ENDIF}
Classes, SysUtils, FileProcs, CodeCache, KeywordFuncLists;
Classes, SysUtils, FileProcs, AVL_Tree, CodeCache, KeywordFuncLists;
type
TCodePosition = record
@ -147,11 +147,18 @@ function CompareCodeXYPositions(Pos1, Pos2: PCodeXYPosition): integer;
function CompareCodePositions(Pos1, Pos2: PCodePosition): integer;
procedure AddCodePosition(var ListOfPCodeXYPosition: TFPList;
const NewCodePos: TCodeXYPosition);
const NewCodePos: TCodeXYPosition);
function IndexOfCodePosition(var ListOfPCodeXYPosition: TFPList;
const APosition: PCodeXYPosition): integer;
const APosition: PCodeXYPosition): integer;
procedure FreeListOfPCodeXYPosition(ListOfPCodeXYPosition: TFPList);
function CreateTreeOfPCodeXYPosition: TAVLTree;
procedure AddCodePosition(var TreeOfPCodeXYPosition: TAVLTree;
const NewCodePos: TCodeXYPosition);
procedure FreeTreeOfPCodeXYPosition(TreeOfPCodeXYPosition: TAVLTree);
procedure AddListToTreeOfPCodeXYPosition(SrcList: TFPList;
DestTree: TAVLTree; ClearList, CreateCopies: boolean);
var
WordToAtomFlag: TWordToAtomFlag;
@ -244,6 +251,67 @@ begin
ListOfPCodeXYPosition.Free;
end;
function CreateTreeOfPCodeXYPosition: TAVLTree;
begin
Result:=TAVLTree.Create(TListSortCompare(@CompareCodeXYPositions));
end;
procedure AddCodePosition(var TreeOfPCodeXYPosition: TAVLTree;
const NewCodePos: TCodeXYPosition);
var
AddCodePos: PCodeXYPosition;
begin
if TreeOfPCodeXYPosition=nil then
TreeOfPCodeXYPosition:=TAVLTree.Create(TListSortCompare(@CompareCodeXYPositions));
New(AddCodePos);
AddCodePos^:=NewCodePos;
TreeOfPCodeXYPosition.Add(AddCodePos);
end;
procedure FreeTreeOfPCodeXYPosition(TreeOfPCodeXYPosition: TAVLTree);
var
ANode: TAVLTreeNode;
CursorPos: PCodeXYPosition;
begin
if TreeOfPCodeXYPosition=nil then exit;
ANode:=TreeOfPCodeXYPosition.FindLowest;
while ANode<>nil do begin
CursorPos:=PCodeXYPosition(ANode.Data);
if CursorPos<>nil then
Dispose(CursorPos);
ANode:=TreeOfPCodeXYPosition.FindSuccessor(ANode);
end;
TreeOfPCodeXYPosition.Free;
end;
procedure AddListToTreeOfPCodeXYPosition(SrcList: TFPList; DestTree: TAVLTree;
ClearList, CreateCopies: boolean);
var
i: Integer;
CodePos: PCodeXYPosition;
NewCodePos: PCodeXYPosition;
begin
if SrcList=nil then exit;
for i:=SrcList.Count-1 downto 0 do begin
CodePos:=PCodeXYPosition(SrcList[i]);
if DestTree.Find(CodePos)=nil then begin
// new position -> add
if CreateCopies and (not ClearList) then begin
// list items should be kept and copies should be added to the tree
New(NewCodePos);
NewCodePos^:=CodePos^;
end else
NewCodePos:=CodePos;
DestTree.Add(NewCodePos);
end else if ClearList then begin
// position alread exists and items should be deleted
Dispose(NewCodePos);
end;
end;
if ClearList then
SrcList.Clear;
end;
function DbgsCXY(const p: TCodeXYPosition): string;
begin
if p.Code=nil then

View File

@ -480,9 +480,12 @@ type
// extract proc (creates a new procedure from code in selection)
function CheckExtractProc(Code: TCodeBuffer;
const StartPoint, EndPoint: TPoint;
var MethodPossible, SubProcSameLvlPossible: boolean): boolean;
out MethodPossible, SubProcSameLvlPossible: boolean;
out MissingIdentifiers: TAVLTree // tree of PCodeXYPosition
): boolean;
function ExtractProc(Code: TCodeBuffer; const StartPoint, EndPoint: TPoint;
ProcType: TExtractProcType; const ProcName: string;
IgnoreIdentifiers: TAVLTree; // tree of PCodeXYPosition
var NewCode: TCodeBuffer; var NewX, NewY, NewTopLine: integer
): boolean;
@ -1409,52 +1412,20 @@ begin
end;
procedure TCodeToolManager.FreeTreeOfPCodeXYPosition(var Tree: TAVLTree);
var
ANode: TAVLTreeNode;
CursorPos: PCodeXYPosition;
begin
if Tree=nil then exit;
ANode:=Tree.FindLowest;
while ANode<>nil do begin
CursorPos:=PCodeXYPosition(ANode.Data);
if CursorPos<>nil then
Dispose(CursorPos);
ANode:=Tree.FindSuccessor(ANode);
end;
Tree.Free;
CodeAtom.FreeTreeOfPCodeXYPosition(Tree);
Tree:=nil;
end;
function TCodeToolManager.CreateTreeOfPCodeXYPosition: TAVLTree;
begin
Result:=TAVLTree.Create(TListSortCompare(@CompareCodeXYPositions));
Result:=CodeAtom.CreateTreeOfPCodeXYPosition;
end;
procedure TCodeToolManager.AddListToTreeOfPCodeXYPosition(SrcList: TFPList;
DestTree: TAVLTree; ClearList, CreateCopies: boolean);
var
i: Integer;
CodePos: PCodeXYPosition;
NewCodePos: PCodeXYPosition;
begin
if SrcList=nil then exit;
for i:=SrcList.Count-1 downto 0 do begin
CodePos:=PCodeXYPosition(SrcList[i]);
if DestTree.Find(CodePos)=nil then begin
// new position -> add
if CreateCopies and (not ClearList) then begin
// list items should be kept and copies should be added to the tree
New(NewCodePos);
NewCodePos^:=CodePos^;
end else
NewCodePos:=CodePos;
DestTree.Add(NewCodePos);
end else if ClearList then begin
// position alread exists and items should be deleted
Dispose(NewCodePos);
end;
end;
if ClearList then
SrcList.Clear;
CodeAtom.AddListToTreeOfPCodeXYPosition(SrcList,DestTree,ClearList,CreateCopies);
end;
function TCodeToolManager.Explore(Code: TCodeBuffer;
@ -3350,7 +3321,9 @@ begin
end;
function TCodeToolManager.CheckExtractProc(Code: TCodeBuffer; const StartPoint,
EndPoint: TPoint; var MethodPossible, SubProcSameLvlPossible: boolean): boolean;
EndPoint: TPoint; out MethodPossible, SubProcSameLvlPossible: boolean;
out MissingIdentifiers: TAVLTree // tree of PCodeXYPosition
): boolean;
var
StartPos, EndPos: TCodeXYPosition;
begin
@ -3367,7 +3340,7 @@ begin
EndPos.Code:=Code;
try
Result:=FCurCodeTool.CheckExtractProc(StartPos,EndPos,MethodPossible,
SubProcSameLvlPossible);
SubProcSameLvlPossible,MissingIdentifiers);
except
on e: Exception do Result:=HandleException(e);
end;
@ -3375,6 +3348,7 @@ end;
function TCodeToolManager.ExtractProc(Code: TCodeBuffer; const StartPoint,
EndPoint: TPoint; ProcType: TExtractProcType; const ProcName: string;
IgnoreIdentifiers: TAVLTree; // tree of PCodeXYPosition
var NewCode: TCodeBuffer; var NewX, NewY, NewTopLine: integer): boolean;
var
StartPos, EndPos: TCodeXYPosition;
@ -3393,7 +3367,7 @@ begin
EndPos.Code:=Code;
try
Result:=FCurCodeTool.ExtractProc(StartPos,EndPos,ProcType,ProcName,
NewPos,NewTopLine,SourceChangeCache);
IgnoreIdentifiers,NewPos,NewTopLine,SourceChangeCache);
if Result then begin
NewX:=NewPos.X;
NewY:=NewPos.Y;

View File

@ -43,6 +43,7 @@ interface
uses
Classes, SysUtils, FileProcs, CodeToolsStrConsts, CodeTree, CodeAtom,
CustomCodeTool,
PascalParserTool, CodeCompletionTool, KeywordFuncLists, BasicCodeTools,
LinkScanner, AVL_Tree, SourceChanger,
FindDeclarationTool;
@ -62,11 +63,24 @@ type
);
TExtractProcTool = class(TCodeCompletionCodeTool)
protected
function ScanNodesForVariables(const StartPos, EndPos: TCodeXYPosition;
out BlockStartPos, BlockEndPos: integer; // the selection
out ProcNode: TCodeTreeNode;
VarTree: TAVLTree; // tree of TExtractedProcVariable
IgnoreIdentifiers: TAVLTree; // tree of PCodeXYPosition
MissingIdentifiers: TAVLTree // tree of PCodeXYPosition
): boolean;
function InitExtractProc(const StartPos, EndPos: TCodeXYPosition;
out MethodPossible, SubProcSameLvlPossible: boolean): boolean;
public
function CheckExtractProc(const StartPos, EndPos: TCodeXYPosition;
var MethodPossible, SubProcSameLvlPossible: boolean): boolean;
out MethodPossible, SubProcSameLvlPossible: boolean;
out MissingIdentifiers: TAVLTree // tree of PCodeXYPosition
): boolean;
function ExtractProc(const StartPos, EndPos: TCodeXYPosition;
ProcType: TExtractProcType; const ProcName: string;
IgnoreIdentifiers: TAVLTree; // tree of PCodeXYPosition
out NewPos: TCodeXYPosition; out NewTopLine: integer;
SourceChangeCache: TSourceChangeCache): boolean;
end;
@ -141,9 +155,9 @@ end;
{ TExtractProcTool }
function TExtractProcTool.CheckExtractProc(const StartPos,
function TExtractProcTool.InitExtractProc(const StartPos,
EndPos: TCodeXYPosition;
var MethodPossible, SubProcSameLvlPossible: boolean): boolean;
out MethodPossible, SubProcSameLvlPossible: boolean): boolean;
var
CleanStartPos, CleanEndPos: integer;
CursorNode: TCodeTreeNode;
@ -157,15 +171,15 @@ begin
MethodPossible:=false;
SubProcSameLvlPossible:=false;
{$IFDEF CTDebug}
DebugLn('TExtractProcTool.CheckExtractProc syntax and cursor check ..');
DebugLn('TExtractProcTool.InitExtractProc syntax and cursor check ..');
{$ENDIF}
// check syntax
BuildTreeAndGetCleanPos(trAll,StartPos,CleanStartPos,[]);
if CaretToCleanPos(EndPos,CleanEndPos)<>0 then exit;
if CleanStartPos>=CleanEndPos then exit;
{$IFDEF CTDebug}
debugln('TExtractProcTool.CheckExtractProc Selection="',copy(Src,CleanStartPos,CleanEndPos-CleanStartPos),'"');
DebugLn('TExtractProcTool.CheckExtractProc node check ..');
debugln('TExtractProcTool.InitExtractProc Selection="',copy(Src,CleanStartPos,CleanEndPos-CleanStartPos),'"');
DebugLn('TExtractProcTool.InitExtractProc node check ..');
{$ENDIF}
// check if in a Begin..End block
CursorNode:=FindDeepestNodeAtPos(CleanStartPos,true);
@ -173,7 +187,7 @@ begin
BeginBlockNode:=CursorNode.GetNodeOfType(ctnBeginBlock);
if BeginBlockNode=nil then exit;
{$IFDEF CTDebug}
DebugLn('TExtractProcTool.CheckExtractProc Start/End check ..');
DebugLn('TExtractProcTool.InitExtractProc Start/End check ..');
{$ENDIF}
// check if Start and End on same block level
MoveCursorToNodeStart(CursorNode);
@ -183,16 +197,16 @@ begin
if (CurPos.EndPos>CleanEndPos) or (CurPos.StartPos>SrcLen)
or (CurPos.StartPos>CursorNode.EndPos) then
break;
//debugln('TExtractProcTool.CheckExtractProc A "',GetAtom,'"');
//debugln('TExtractProcTool.InitExtractProc A "',GetAtom,'"');
if WordIsBlockStatementStart.DoItUpperCase(UpperSrc,
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
then begin
//debugln('TExtractProcTool.CheckExtractProc WordIsBlockStatementStart "',GetAtom,'"');
//debugln('TExtractProcTool.InitExtractProc WordIsBlockStatementStart "',GetAtom,'"');
BlockCleanStart:=CurPos.StartPos;
if not ReadTilBlockStatementEnd(true) then exit;
BlockCleanEnd:=CurPos.EndPos;
debugln(copy(Src,BlockCleanStart,BlockCleanEnd-BlockCleanStart));
//debugln('TExtractProcTool.CheckExtractProc BlockEnd "',GetAtom,'" BlockCleanEnd=',dbgs(BlockCleanEnd),' CleanEndPos=',dbgs(CleanEndPos),' Result=',dbgs(Result),' BlockStartedInside=',dbgs(BlockCleanStart>=CleanStartPos));
//debugln('TExtractProcTool.InitExtractProc BlockEnd "',GetAtom,'" BlockCleanEnd=',dbgs(BlockCleanEnd),' CleanEndPos=',dbgs(CleanEndPos),' Result=',dbgs(Result),' BlockStartedInside=',dbgs(BlockCleanStart>=CleanStartPos));
if BlockCleanStart<CleanStartPos then begin
// this block started outside the selection
// -> it should end outside
@ -214,7 +228,7 @@ begin
exit;
end;
end;
//debugln('TExtractProcTool.CheckExtractProc Block ok');
//debugln('TExtractProcTool.InitExtractProc Block ok');
end
else if WordIsBlockStatementEnd.DoItUpperCase(UpperSrc,
CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)
@ -234,7 +248,7 @@ begin
// check if end not in a statement
// ToDo
{$IFDEF CTDebug}
DebugLn('TExtractProcTool.CheckExtractProc Method check ..');
DebugLn('TExtractProcTool.InitExtractProc Method check ..');
{$ENDIF}
// check if in a method body
ANode:=CursorNode;
@ -250,216 +264,46 @@ begin
end;
SubProcSameLvlPossible:=(ProcLvl>1);
{$IFDEF CTDebug}
DebugLn('TExtractProcTool.CheckExtractProc END');
DebugLn('TExtractProcTool.InitExtractProc END');
{$ENDIF}
Result:=true;
end;
function TExtractProcTool.CheckExtractProc(const StartPos,
EndPos: TCodeXYPosition; out MethodPossible, SubProcSameLvlPossible: boolean;
out MissingIdentifiers: TAVLTree): boolean;
var
BlockStartPos: integer;
BlockEndPos: integer;
ProcNode: TCodeTreeNode;
begin
Result:=false;
if not InitExtractProc(StartPos,EndPos,MethodPossible,SubProcSameLvlPossible)
then exit;
MissingIdentifiers:=CreateTreeOfPCodeXYPosition;
if not ScanNodesForVariables(StartPos,EndPos,BlockStartPos,BlockEndPos,
ProcNode,nil,nil,MissingIdentifiers) then exit;
Result:=true;
end;
function TExtractProcTool.ExtractProc(const StartPos, EndPos: TCodeXYPosition;
ProcType: TExtractProcType; const ProcName: string;
IgnoreIdentifiers: TAVLTree; // tree of PCodeXYPosition
out NewPos: TCodeXYPosition; out NewTopLine: integer;
SourceChangeCache: TSourceChangeCache): boolean;
type
TParameterType = (ptNone, ptConst, ptVar, ptOut, ptNoSpecifier);
const
ShortProcFormat = [phpWithoutClassKeyword];
{$IFDEF CTDebug}
ParameterTypeNames: array[TParameterType] of string = (
'ptNone', 'ptConst', 'ptVar', 'ptOut', 'ptNoSpecifier');
{$ENDIF}
type
TParameterType = (ptNone, ptConst, ptVar, ptOut, ptNoSpecifier);
var
BlockStartPos, BlockEndPos: integer; // the selection
ProcNode: TCodeTreeNode; // the main proc node of the selection
VarTree: TAVLTree;
procedure AddVariableToTree(VarNode: TCodeTreeNode; IsInSelection,
IsAfterSelection, IsChanged: boolean; ParameterType: TParameterType);
var
AVLNode: TAVLTreeNode;
ProcVar: TExtractedProcVariable;
begin
{$IFDEF CTDebug}
DebugLn('AddVariableToTree A Ident=',GetIdentifier(@Src[VarNode.StartPos]),
' IsInSelection=',dbgs(IsInSelection),
' ParameterType=',ParameterTypeNames[ParameterType]);
{$ENDIF}
if VarTree=nil then
VarTree:=TAVLTree.Create(TListSortCompare(@CompareExtractedProcVariables));
AVLNode:=VarTree.FindKey(VarNode,TListSortCompare(@CompareNodeWithExtractedProcVariable));
if AVLNode<>nil then begin
ProcVar:=TExtractedProcVariable(AVLNode.Data);
end else begin
ProcVar:=TExtractedProcVariable.Create;
ProcVar.Node:=VarNode;
end;
ProcVar.ReadInSelection:=ProcVar.ReadInSelection or IsInSelection;
ProcVar.WriteInSelection:=ProcVar.WriteInSelection
or (IsInSelection and IsChanged);
ProcVar.UsedInNonSelection:=ProcVar.UsedInNonSelection
or (not IsInSelection) or (ParameterType<>ptNone);
if (not ProcVar.ReadAfterSelectionValid) then begin
// a) variable is a var or out parameter
// => the variable value IS needed after the extracted proc
// b) just after the selection the variable is read
// => the variable value IS needed after the extracted proc
// c) just after the selection the variable is written
// => the variable value IS NOT needed after the extracted proc
if (ParameterType in [ptOut,ptVar]) then begin
ProcVar.ReadAfterSelectionValid:=true;
ProcVar.ReadAfterSelection:=true;
end else if (not IsInSelection) and IsAfterSelection then begin
ProcVar.ReadAfterSelectionValid:=true;
ProcVar.ReadAfterSelection:=not IsChanged;
end;
end;
if AVLNode=nil then begin
if ParameterType<>ptNone then
ProcVar.VarType:=epvtParameter
else
ProcVar.VarType:=epvtLocalVar;
VarTree.Add(ProcVar);
end;
end;
function VariableIsChanged(VarStartPos: integer): boolean;
begin
Result:=false;
MoveCursorToCleanPos(VarStartPos);
// read identifier
ReadNextAtom;
if CurPos.Flag in [cafRoundBracketOpen] then
ReadTilBracketClose(true);
// read next atom
ReadNextAtom;
if AtomIs(':=') or AtomIs('+=') or AtomIs('-=') or AtomIs('*=')
or AtomIs('/=') then begin
Result:=true;
exit;
end;
end;
function CheckVariableAtCursor: boolean;
// find declaration of identifier at cursor and add to variable tree
var
Params: TFindDeclarationParams;
VarStartPos: Integer;
VarNode: TCodeTreeNode;
IsInSelection: Boolean;
ClosestProcNode: TCodeTreeNode;
IsParameter: boolean;
IsChanged: Boolean;
IsAfterSelection: Boolean;
ParameterType: TParameterType;
begin
Result:=false;
// find start of variable
VarStartPos:=FindStartOfVariable(CurPos.StartPos);
IsInSelection:=(VarStartPos>=BlockStartPos) and (VarStartPos<BlockEndPos);
IsAfterSelection:=(VarStartPos>=BlockEndPos);
MoveCursorToCleanPos(VarStartPos);
Params:=TFindDeclarationParams.Create;
try
// find declaration
Params.ContextNode:=FindDeepestNodeAtPos(VarStartPos,true);
Params.Flags:=[fdfSearchInParentNodes,fdfExceptionOnNotFound,
fdfTopLvlResolving,fdfSearchInAncestors];
// ToDo: Params.OnTopLvlIdentifierFound:=@OnTopLvlIdentifierFound;
Params.SetIdentifier(Self,@Src[VarStartPos],@CheckSrcIdentifier);
{$IFDEF CTDebug}
DebugLn('AddVariableAtCursor Searching ',GetIdentifier(Params.Identifier));
{$ENDIF}
if not FindDeclarationOfIdentAtParam(Params) then begin
{$IFDEF CTDebug}
DebugLn('AddVariableAtCursor B not found');
{$ENDIF}
exit;
end;
// check if declaration is local variable
if (Params.NewCodeTool=Self) and (Params.NewNode<>nil) then begin
VarNode:=Params.NewNode;
if (VarNode.Desc=ctnVarDefinition)
and (VarNode.HasAsParent(ProcNode)) then begin
// Now we know: VarNode is a variable defined in the main proc
// or one of its sub procs
ClosestProcNode:=VarNode.GetNodeOfType(ctnProcedure);
if ClosestProcNode=ProcNode then begin
// VarNode is a variable defined by the main proc
IsParameter:=VarNode.GetNodeOfType(ctnProcedureHead)<>nil;
ParameterType:=ptNone;
if IsParameter then begin
MoveCursorToParameterSpecifier(VarNode);
if UpAtomIs('CONST') then
ParameterType:=ptConst
else if UpAtomIs('VAR') then
ParameterType:=ptVar
else if UpAtomIs('OUT') then
ParameterType:=ptOut
else
ParameterType:=ptNoSpecifier;
end;
IsChanged:=VariableIsChanged(VarStartPos);
AddVariableToTree(VarNode,IsInSelection,IsAfterSelection,IsChanged,
ParameterType);
end;
end;
end;
finally
Params.Free;
end;
Result:=true;
end;
function ScanSourceForVariables(CleanStartPos, CleanEndPos: integer): boolean;
// scan part of the source for variables
var
LastAtomType: TCommonAtomFlag;
OldCursor: Integer;
begin
Result:=false;
{$IFDEF CTDebug}
DebugLn('TExtractProcTool.ScanSourceForVariables A "',copy(Src,CleanStartPos,CleanEndPos-CleanStartPos),'"');
{$ENDIF}
MoveCursorToNearestAtom(CleanStartPos);
while CurPos.StartPos<CleanEndPos do begin
LastAtomType:=CurPos.Flag;
ReadNextAtom;
if AtomIsIdentifier(false) and (LastAtomType<>cafPoint) then begin
// this could be the start of a variable -> check
{$IFDEF CTDebug}
DebugLn('ScanSourceForVariables B Identifier=',GetAtom);
{$ENDIF}
OldCursor:=CurPos.StartPos;
if not CheckVariableAtCursor then exit;
// restore cursor
MoveCursorToCleanPos(OldCursor);
ReadNextAtom;
end;
end;
Result:=true;
end;
function ScanNodesForVariables(StartNode: TCodeTreeNode): boolean;
// scan recursively all statements for variables
var
ChildNode: TCodeTreeNode;
begin
{$IFDEF CTDebug}
DebugLn('TExtractProcTool.ScanNodesForVariables A Node=',StartNode.DescAsString);
{$ENDIF}
Result:=false;
ChildNode:=StartNode.FirstChild;
while ChildNode<>nil do begin
if (ChildNode.Desc in [ctnBeginBlock,ctnAsmBlock])
and (ChildNode.Parent.Desc=ctnProcedure) then begin
if not ScanSourceForVariables(ChildNode.StartPos,ChildNode.EndPos) then
exit;
end;
if not ScanNodesForVariables(ChildNode) then exit;
ChildNode:=ChildNode.NextBrother;
end;
Result:=true;
end;
function ReplaceSelectionWithCall: boolean;
var
Indent: Integer;
@ -1154,12 +998,10 @@ var
ProcCode: string;
begin
Result:=false;
MethodPossible:=false;
SubProcSameLvlPossible:=false;
{$IFDEF CTDebug}
DebugLn('ExtractProc A ProcName="',ProcName,'" ProcType=',ExtractProcTypeNames[ProcType]);
{$ENDIF}
if not CheckExtractProc(StartPos,EndPos,MethodPossible,SubProcSameLvlPossible)
if not InitExtractProc(StartPos,EndPos,MethodPossible,SubProcSameLvlPossible)
then exit;
if (not MethodPossible) and (ProcType in [eptPrivateMethod,eptProtectedMethod,
eptPublicMethod,eptPublishedMethod])
@ -1167,16 +1009,13 @@ begin
exit;
if (not SubProcSameLvlPossible) and (ProcType=eptSubProcedureSameLvl) then
exit;
if CaretToCleanPos(StartPos,BlockStartPos)<>0 then exit;
if CaretToCleanPos(EndPos,BlockEndPos)<>0 then exit;
BuildSubTree(BlockStartPos);
CodeCompleteSrcChgCache:=SourceChangeCache;
ProcNode:=FindDeepestNodeAtPos(BlockStartPos,true).GetNodeOfType(ctnProcedure);
VarTree:=nil;
VarTree:=TAVLTree.Create(TListSortCompare(@CompareExtractedProcVariables));
NewProcPath:=nil;
try
if not ScanNodesForVariables(ProcNode) then exit;
if not ScanNodesForVariables(StartPos,EndPos,BlockStartPos,BlockEndPos,
ProcNode,VarTree,IgnoreIdentifiers,nil) then exit;
if not ReplaceSelectionWithCall then exit;
if not DeleteMovedLocalVariables then exit;
if not CreateProcNameParts(ProcClassName,ProcClassNode) then exit;
@ -1205,5 +1044,234 @@ begin
Result:=true;
end;
function TExtractProcTool.ScanNodesForVariables(const StartPos,
EndPos: TCodeXYPosition; out BlockStartPos, BlockEndPos: integer;
out ProcNode: TCodeTreeNode;
VarTree: TAVLTree; // tree of TExtractedProcVariable
IgnoreIdentifiers: TAVLTree; // tree of PCodeXYPosition
MissingIdentifiers: TAVLTree// tree of PCodeXYPosition
): boolean;
type
TParameterType = (ptNone, ptConst, ptVar, ptOut, ptNoSpecifier);
procedure AddVariableToTree(VarNode: TCodeTreeNode; IsInSelection,
IsAfterSelection, IsChanged: boolean; ParameterType: TParameterType);
var
AVLNode: TAVLTreeNode;
ProcVar: TExtractedProcVariable;
begin
{$IFDEF CTDebug}
DebugLn('AddVariableToTree A Ident=',GetIdentifier(@Src[VarNode.StartPos]),
' IsInSelection=',dbgs(IsInSelection),
' ParameterType=',ParameterTypeNames[ParameterType]);
{$ENDIF}
if VarTree=nil then exit;
AVLNode:=VarTree.FindKey(VarNode,TListSortCompare(@CompareNodeWithExtractedProcVariable));
if AVLNode<>nil then begin
ProcVar:=TExtractedProcVariable(AVLNode.Data);
end else begin
ProcVar:=TExtractedProcVariable.Create;
ProcVar.Node:=VarNode;
end;
ProcVar.ReadInSelection:=ProcVar.ReadInSelection or IsInSelection;
ProcVar.WriteInSelection:=ProcVar.WriteInSelection
or (IsInSelection and IsChanged);
ProcVar.UsedInNonSelection:=ProcVar.UsedInNonSelection
or (not IsInSelection) or (ParameterType<>ptNone);
if (not ProcVar.ReadAfterSelectionValid) then begin
// a) variable is a var or out parameter
// => the variable value IS needed after the extracted proc
// b) just after the selection the variable is read
// => the variable value IS needed after the extracted proc
// c) just after the selection the variable is written
// => the variable value IS NOT needed after the extracted proc
if (ParameterType in [ptOut,ptVar]) then begin
ProcVar.ReadAfterSelectionValid:=true;
ProcVar.ReadAfterSelection:=true;
end else if (not IsInSelection) and IsAfterSelection then begin
ProcVar.ReadAfterSelectionValid:=true;
ProcVar.ReadAfterSelection:=not IsChanged;
end;
end;
if AVLNode=nil then begin
if ParameterType<>ptNone then
ProcVar.VarType:=epvtParameter
else
ProcVar.VarType:=epvtLocalVar;
VarTree.Add(ProcVar);
end;
end;
function VariableIsChanged(VarStartPos: integer): boolean;
begin
Result:=false;
MoveCursorToCleanPos(VarStartPos);
// read identifier
ReadNextAtom;
if CurPos.Flag in [cafRoundBracketOpen] then
ReadTilBracketClose(true);
// read next atom
ReadNextAtom;
if AtomIs(':=') or AtomIs('+=') or AtomIs('-=') or AtomIs('*=')
or AtomIs('/=') then begin
Result:=true;
exit;
end;
end;
function CheckVariableAtCursor: boolean;
// find declaration of identifier at cursor and add to variable tree
var
Params: TFindDeclarationParams;
VarStartPos: Integer;
VarNode: TCodeTreeNode;
IsInSelection: Boolean;
ClosestProcNode: TCodeTreeNode;
IsParameter: boolean;
IsChanged: Boolean;
IsAfterSelection: Boolean;
ParameterType: TParameterType;
NewCodePos: TCodeXYPosition;
begin
Result:=false;
// find start of variable
VarStartPos:=FindStartOfVariable(CurPos.StartPos);
if (IgnoreIdentifiers<>nil) then begin
if not CleanPosToCaret(VarStartPos,NewCodePos) then exit;
if IgnoreIdentifiers.Find(@NewCodePos)<>nil then exit(true);
end;
IsInSelection:=(VarStartPos>=BlockStartPos) and (VarStartPos<BlockEndPos);
IsAfterSelection:=(VarStartPos>=BlockEndPos);
MoveCursorToCleanPos(VarStartPos);
Params:=TFindDeclarationParams.Create;
try
// find declaration
Params.ContextNode:=FindDeepestNodeAtPos(VarStartPos,true);
Params.Flags:=[fdfSearchInParentNodes,fdfExceptionOnNotFound,
fdfTopLvlResolving,fdfSearchInAncestors];
Params.SetIdentifier(Self,@Src[VarStartPos],@CheckSrcIdentifier);
{$IFDEF CTDebug}
DebugLn('AddVariableAtCursor Searching ',GetIdentifier(Params.Identifier));
{$ENDIF}
try
FindDeclarationOfIdentAtParam(Params);
except
on E: ECodeToolError do begin
{$IFDEF CTDebug}
DebugLn('AddVariableAtCursor identifier not found ',GetIdentifier(@Src[VarStartPos]));
{$ENDIF}
if MissingIdentifiers=nil then
raise;
// collect missing identifiers
if not CleanPosToCaret(VarStartPos,NewCodePos) then exit;
AddCodePosition(MissingIdentifiers,NewCodePos);
Result:=true;
exit;
end;
end;
// check if declaration is local variable
if (Params.NewCodeTool=Self) and (Params.NewNode<>nil) then begin
VarNode:=Params.NewNode;
if (VarNode.Desc=ctnVarDefinition)
and (VarNode.HasAsParent(ProcNode)) then begin
// Now we know: VarNode is a variable defined in the main proc
// or one of its sub procs
ClosestProcNode:=VarNode.GetNodeOfType(ctnProcedure);
if ClosestProcNode=ProcNode then begin
// VarNode is a variable defined by the main proc
IsParameter:=VarNode.GetNodeOfType(ctnProcedureHead)<>nil;
ParameterType:=ptNone;
if IsParameter then begin
MoveCursorToParameterSpecifier(VarNode);
if UpAtomIs('CONST') then
ParameterType:=ptConst
else if UpAtomIs('VAR') then
ParameterType:=ptVar
else if UpAtomIs('OUT') then
ParameterType:=ptOut
else
ParameterType:=ptNoSpecifier;
end;
IsChanged:=VariableIsChanged(VarStartPos);
AddVariableToTree(VarNode,IsInSelection,IsAfterSelection,IsChanged,
ParameterType);
end;
end;
end;
finally
Params.Free;
end;
Result:=true;
end;
function ScanSourceForVariables(CleanStartPos, CleanEndPos: integer): boolean;
// scan part of the source for variables
var
LastAtomType: TCommonAtomFlag;
OldCursor: Integer;
begin
Result:=false;
{$IFDEF CTDebug}
DebugLn('TExtractProcTool.ScanSourceForVariables A "',copy(Src,CleanStartPos,CleanEndPos-CleanStartPos),'"');
{$ENDIF}
MoveCursorToNearestAtom(CleanStartPos);
while CurPos.StartPos<CleanEndPos do begin
LastAtomType:=CurPos.Flag;
ReadNextAtom;
if AtomIsIdentifier(false) and (LastAtomType<>cafPoint) then begin
// this could be the start of a variable -> check
{$IFDEF CTDebug}
DebugLn('ScanSourceForVariables B Identifier=',GetAtom);
{$ENDIF}
OldCursor:=CurPos.StartPos;
if not CheckVariableAtCursor then exit;
// restore cursor
MoveCursorToCleanPos(OldCursor);
ReadNextAtom;
end;
end;
Result:=true;
end;
function ScanNodesForVariablesRecursive(StartNode: TCodeTreeNode): boolean;
// scan recursively all statements for variables
var
ChildNode: TCodeTreeNode;
begin
{$IFDEF CTDebug}
DebugLn('ScanNodesForVariablesRecursive A Node=',StartNode.DescAsString);
{$ENDIF}
Result:=false;
ChildNode:=StartNode.FirstChild;
while ChildNode<>nil do begin
if (ChildNode.Desc in [ctnBeginBlock,ctnAsmBlock])
and (ChildNode.Parent.Desc=ctnProcedure) then begin
if not ScanSourceForVariables(ChildNode.StartPos,ChildNode.EndPos) then
exit;
end;
if not ScanNodesForVariablesRecursive(ChildNode) then exit;
ChildNode:=ChildNode.NextBrother;
end;
Result:=true;
end;
begin
Result:=false;
if CaretToCleanPos(StartPos,BlockStartPos)<>0 then exit;
if CaretToCleanPos(EndPos,BlockEndPos)<>0 then exit;
BuildSubTree(BlockStartPos);
ProcNode:=FindDeepestNodeAtPos(BlockStartPos,true).GetNodeOfType(ctnProcedure);
ActivateGlobalWriteLock;
try
if not ScanNodesForVariablesRecursive(ProcNode) then exit;
finally
DeactivateGlobalWriteLock;
end;
Result:=true;
end;
end.

View File

@ -933,7 +933,7 @@ begin
Add('LO' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('HI' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('ORD' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('PREC' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('PRED' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('SUCC' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('LENGTH' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('SETLENGTH' ,{$ifdef FPC}@{$endif}AllwaysTrue);
@ -954,6 +954,8 @@ begin
Add('EXIT' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('BREAK' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('CONTINUE' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('NEW' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('DISPOSE' ,{$ifdef FPC}@{$endif}AllwaysTrue);
end;
WordIsTermOperator:=TKeyWordFunctionList.Create;
@ -1323,6 +1325,7 @@ begin
Add('BYTE' ,{$ifdef FPC}@{$endif}AllwaysTrue);
Add('VARIANT' ,{$ifdef FPC}@{$endif}AllwaysTrue);
end;
// functions
WordIsPredefinedFPCIdentifier.Add(IsWordBuiltInFunc);
WordIsPredefinedDelphiIdentifier:=TKeyWordFunctionList.Create;

View File

@ -1,25 +1,24 @@
object ExtractProcDialog: TExtractProcDialog
Left = 378
Height = 201
Height = 355
Top = 374
Width = 420
HorzScrollBar.Page = 419
VertScrollBar.Page = 200
Width = 425
HorzScrollBar.Page = 424
VertScrollBar.Page = 354
ActiveControl = NameEdit
BorderIcons = [biSystemMenu]
Caption = 'ExtractProcDialog'
ClientHeight = 201
ClientWidth = 420
ClientHeight = 355
ClientWidth = 425
OnClose = ExtractProcDialogClose
OnCreate = ExtractProcDialogCREATE
object TypeRadiogroup: TRadioGroup
AnchorSideBottom.Control = NameGroupbox
Left = 6
Height = 91
Height = 171
Top = 6
Width = 408
Align = alTop
Anchors = [akTop, akLeft, akRight, akBottom]
Width = 413
Align = alClient
AutoFill = True
BorderSpacing.Around = 6
Caption = 'TypeRadiogroup'
@ -42,86 +41,110 @@ object ExtractProcDialog: TExtractProcDialog
AnchorSideBottom.Control = CancelButton
Left = 6
Height = 54
Top = 103
Width = 408
Anchors = [akLeft, akRight, akBottom]
Top = 183
Width = 413
Align = alBottom
AutoSize = True
BorderSpacing.Around = 6
Caption = 'NameGroupbox'
ClientHeight = 35
ClientWidth = 404
ClientWidth = 409
ParentCtl3D = False
TabOrder = 0
object NameEdit: TEdit
Left = 6
Height = 23
Top = 6
Width = 392
Width = 397
Align = alTop
BorderSpacing.Around = 6
TabOrder = 0
Text = 'NameEdit'
end
end
object HelpButton: TBitBtn
AnchorSideLeft.Control = Owner
AnchorSideBottom.Control = Owner
AnchorSideBottom.Side = asrBottom
Left = 6
Height = 36
Top = 159
Width = 75
Anchors = [akLeft, akBottom]
object BtnPanel: TPanel
Height = 48
Top = 307
Width = 425
Align = alBottom
AutoSize = True
BorderSpacing.Around = 6
Caption = '&Help'
Constraints.MinHeight = 25
Constraints.MinWidth = 75
Kind = bkHelp
NumGlyphs = 0
OnClick = HelpButtonClick
BevelOuter = bvNone
ClientHeight = 48
ClientWidth = 425
TabOrder = 2
object CancelButton: TBitBtn
AnchorSideRight.Side = asrBottom
AnchorSideBottom.Side = asrBottom
Left = 341
Height = 36
Top = 6
Width = 78
Align = alRight
AutoSize = True
BorderSpacing.Around = 6
Cancel = True
Caption = 'Cancel'
Constraints.MinHeight = 25
Constraints.MinWidth = 75
Kind = bkCancel
ModalResult = 2
NumGlyphs = 0
TabOrder = 0
end
object OkButton: TBitBtn
AnchorSideBottom.Side = asrBottom
Left = 260
Height = 36
Top = 6
Width = 75
Align = alRight
AutoSize = True
BorderSpacing.Around = 6
Caption = '&OK'
Constraints.MinHeight = 25
Constraints.MinWidth = 75
Default = True
Kind = bkOK
ModalResult = 1
NumGlyphs = 0
OnClick = OkButtonCLICK
TabOrder = 1
end
object HelpButton: TBitBtn
AnchorSideBottom.Side = asrBottom
Left = 6
Height = 36
Top = 6
Width = 75
Align = alLeft
AutoSize = True
BorderSpacing.Around = 6
Caption = '&Help'
Constraints.MinHeight = 25
Constraints.MinWidth = 75
Kind = bkHelp
NumGlyphs = 0
OnClick = HelpButtonClick
TabOrder = 2
end
end
object CancelButton: TBitBtn
AnchorSideRight.Control = Owner
AnchorSideRight.Side = asrBottom
AnchorSideBottom.Control = Owner
AnchorSideBottom.Side = asrBottom
Left = 336
Height = 32
Top = 163
Width = 78
Anchors = [akRight, akBottom]
AutoSize = True
object MissingIdentifiersGroupBox: TGroupBox
Left = 6
Height = 58
Top = 243
Width = 413
Align = alBottom
BorderSpacing.Around = 6
Cancel = True
Caption = 'Cancel'
Constraints.MinHeight = 25
Constraints.MinWidth = 75
Kind = bkCancel
ModalResult = 2
NumGlyphs = 0
Caption = 'MissingIdentifiersGroupBox'
ClientHeight = 39
ClientWidth = 409
TabOrder = 3
end
object OkButton: TBitBtn
AnchorSideRight.Control = CancelButton
AnchorSideBottom.Control = Owner
AnchorSideBottom.Side = asrBottom
Left = 255
Height = 36
Top = 159
Width = 75
Anchors = [akRight, akBottom]
AutoSize = True
BorderSpacing.Around = 6
Caption = '&OK'
Constraints.MinHeight = 25
Constraints.MinWidth = 75
Default = True
Kind = bkOK
ModalResult = 1
NumGlyphs = 0
OnClick = OkButtonCLICK
TabOrder = 4
object MissingIdentifiersListBox: TListBox
Height = 39
Width = 409
Align = alClient
TabOrder = 0
TopIndex = -1
end
end
end

View File

@ -2,47 +2,50 @@
LazarusResources.Add('TExtractProcDialog','FORMDATA',[
'TPF0'#18'TExtractProcDialog'#17'ExtractProcDialog'#4'Left'#3'z'#1#6'Height'#3
+#201#0#3'Top'#3'v'#1#5'Width'#3#164#1#18'HorzScrollBar.Page'#3#163#1#18'Vert'
+'ScrollBar.Page'#3#200#0#13'ActiveControl'#7#8'NameEdit'#11'BorderIcons'#11
+#12'biSystemMenu'#0#7'Caption'#6#17'ExtractProcDialog'#12'ClientHeight'#3#201
+#0#11'ClientWidth'#3#164#1#7'OnClose'#7#22'ExtractProcDialogClose'#8'OnCreat'
+'e'#7#23'ExtractProcDialogCREATE'#0#11'TRadioGroup'#14'TypeRadiogroup'#24'An'
+'chorSideBottom.Control'#7#12'NameGroupbox'#4'Left'#2#6#6'Height'#2'['#3'Top'
+#2#6#5'Width'#3#152#1#5'Align'#7#5'alTop'#7'Anchors'#11#5'akTop'#6'akLeft'#7
+'akRight'#8'akBottom'#0#8'AutoFill'#9#20'BorderSpacing.Around'#2#6#7'Caption'
+#6#14'TypeRadiogroup'#28'ChildSizing.LeftRightSpacing'#2#6#28'ChildSizing.To'
+'pBottomSpacing'#2#6#29'ChildSizing.EnlargeHorizontal'#7#24'crsHomogenousChi'
+'ldResize'#27'ChildSizing.EnlargeVertical'#7#24'crsHomogenousChildResize'#28
+'ChildSizing.ShrinkHorizontal'#7#14'crsScaleChilds'#26'ChildSizing.ShrinkVer'
+'tical'#7#14'crsScaleChilds'#18'ChildSizing.Layout'#7#29'cclTopToBottomThenL'
+'eftToRight'#27'ChildSizing.ControlsPerLine'#2#1#12'ColumnLayout'#7#24'clVer'
+'ticalThenHorizontal'#8'TabOrder'#2#1#0#0#9'TGroupBox'#12'NameGroupbox'#22'A'
+'nchorSideLeft.Control'#7#5'Owner'#18'AnchorSideTop.Side'#7#9'asrBottom'#23
+'AnchorSideRight.Control'#7#5'Owner'#20'AnchorSideRight.Side'#7#9'asrBottom'
+#24'AnchorSideBottom.Control'#7#12'CancelButton'#4'Left'#2#6#6'Height'#2'6'#3
+'Top'#2'g'#5'Width'#3#152#1#7'Anchors'#11#6'akLeft'#7'akRight'#8'akBottom'#0
+#8'AutoSize'#9#20'BorderSpacing.Around'#2#6#7'Caption'#6#12'NameGroupbox'#12
+'ClientHeight'#2'#'#11'ClientWidth'#3#148#1#11'ParentCtl3D'#8#8'TabOrder'#2#0
+#0#5'TEdit'#8'NameEdit'#4'Left'#2#6#6'Height'#2#23#3'Top'#2#6#5'Width'#3#136
+#1#5'Align'#7#5'alTop'#20'BorderSpacing.Around'#2#6#8'TabOrder'#2#0#4'Text'#6
+#8'NameEdit'#0#0#0#7'TBitBtn'#10'HelpButton'#22'AnchorSideLeft.Control'#7#5
+'Owner'#24'AnchorSideBottom.Control'#7#5'Owner'#21'AnchorSideBottom.Side'#7#9
+'asrBottom'#4'Left'#2#6#6'Height'#2'$'#3'Top'#3#159#0#5'Width'#2'K'#7'Anchor'
+'s'#11#6'akLeft'#8'akBottom'#0#8'AutoSize'#9#20'BorderSpacing.Around'#2#6#7
+'Caption'#6#5'&Help'#21'Constraints.MinHeight'#2#25#20'Constraints.MinWidth'
+#2'K'#4'Kind'#7#6'bkHelp'#9'NumGlyphs'#2#0#7'OnClick'#7#15'HelpButtonClick'#8
+'TabOrder'#2#2#0#0#7'TBitBtn'#12'CancelButton'#23'AnchorSideRight.Control'#7
+#5'Owner'#20'AnchorSideRight.Side'#7#9'asrBottom'#24'AnchorSideBottom.Contro'
+'l'#7#5'Owner'#21'AnchorSideBottom.Side'#7#9'asrBottom'#4'Left'#3'P'#1#6'Hei'
+'ght'#2' '#3'Top'#3#163#0#5'Width'#2'N'#7'Anchors'#11#7'akRight'#8'akBottom'
+#0#8'AutoSize'#9#20'BorderSpacing.Around'#2#6#6'Cancel'#9#7'Caption'#6#6'Can'
+'cel'#21'Constraints.MinHeight'#2#25#20'Constraints.MinWidth'#2'K'#4'Kind'#7
+#8'bkCancel'#11'ModalResult'#2#2#9'NumGlyphs'#2#0#8'TabOrder'#2#3#0#0#7'TBit'
+'Btn'#8'OkButton'#23'AnchorSideRight.Control'#7#12'CancelButton'#24'AnchorSi'
+'deBottom.Control'#7#5'Owner'#21'AnchorSideBottom.Side'#7#9'asrBottom'#4'Lef'
+'t'#3#255#0#6'Height'#2'$'#3'Top'#3#159#0#5'Width'#2'K'#7'Anchors'#11#7'akRi'
+'ght'#8'akBottom'#0#8'AutoSize'#9#20'BorderSpacing.Around'#2#6#7'Caption'#6#3
+'&OK'#21'Constraints.MinHeight'#2#25#20'Constraints.MinWidth'#2'K'#7'Default'
+#9#4'Kind'#7#4'bkOK'#11'ModalResult'#2#1#9'NumGlyphs'#2#0#7'OnClick'#7#13'Ok'
+'ButtonCLICK'#8'TabOrder'#2#4#0#0#0
+'c'#1#3'Top'#3'v'#1#5'Width'#3#169#1#18'HorzScrollBar.Page'#3#168#1#18'VertS'
+'crollBar.Page'#3'b'#1#13'ActiveControl'#7#8'NameEdit'#11'BorderIcons'#11#12
+'biSystemMenu'#0#7'Caption'#6#17'ExtractProcDialog'#12'ClientHeight'#3'c'#1
+#11'ClientWidth'#3#169#1#7'OnClose'#7#22'ExtractProcDialogClose'#8'OnCreate'
+#7#23'ExtractProcDialogCREATE'#0#11'TRadioGroup'#14'TypeRadiogroup'#24'Ancho'
+'rSideBottom.Control'#7#12'NameGroupbox'#4'Left'#2#6#6'Height'#3#171#0#3'Top'
+#2#6#5'Width'#3#157#1#5'Align'#7#8'alClient'#8'AutoFill'#9#20'BorderSpacing.'
+'Around'#2#6#7'Caption'#6#14'TypeRadiogroup'#28'ChildSizing.LeftRightSpacing'
+#2#6#28'ChildSizing.TopBottomSpacing'#2#6#29'ChildSizing.EnlargeHorizontal'#7
+#24'crsHomogenousChildResize'#27'ChildSizing.EnlargeVertical'#7#24'crsHomoge'
+'nousChildResize'#28'ChildSizing.ShrinkHorizontal'#7#14'crsScaleChilds'#26'C'
+'hildSizing.ShrinkVertical'#7#14'crsScaleChilds'#18'ChildSizing.Layout'#7#29
+'cclTopToBottomThenLeftToRight'#27'ChildSizing.ControlsPerLine'#2#1#12'Colum'
+'nLayout'#7#24'clVerticalThenHorizontal'#8'TabOrder'#2#1#0#0#9'TGroupBox'#12
+'NameGroupbox'#22'AnchorSideLeft.Control'#7#5'Owner'#18'AnchorSideTop.Side'#7
+#9'asrBottom'#23'AnchorSideRight.Control'#7#5'Owner'#20'AnchorSideRight.Side'
+#7#9'asrBottom'#24'AnchorSideBottom.Control'#7#12'CancelButton'#4'Left'#2#6#6
+'Height'#2'6'#3'Top'#3#183#0#5'Width'#3#157#1#5'Align'#7#8'alBottom'#8'AutoS'
+'ize'#9#20'BorderSpacing.Around'#2#6#7'Caption'#6#12'NameGroupbox'#12'Client'
+'Height'#2'#'#11'ClientWidth'#3#153#1#11'ParentCtl3D'#8#8'TabOrder'#2#0#0#5
+'TEdit'#8'NameEdit'#4'Left'#2#6#6'Height'#2#23#3'Top'#2#6#5'Width'#3#141#1#5
+'Align'#7#5'alTop'#20'BorderSpacing.Around'#2#6#8'TabOrder'#2#0#4'Text'#6#8
+'NameEdit'#0#0#0#6'TPanel'#8'BtnPanel'#6'Height'#2'0'#3'Top'#3'3'#1#5'Width'
+#3#169#1#5'Align'#7#8'alBottom'#8'AutoSize'#9#10'BevelOuter'#7#6'bvNone'#12
+'ClientHeight'#2'0'#11'ClientWidth'#3#169#1#8'TabOrder'#2#2#0#7'TBitBtn'#12
+'CancelButton'#20'AnchorSideRight.Side'#7#9'asrBottom'#21'AnchorSideBottom.S'
+'ide'#7#9'asrBottom'#4'Left'#3'U'#1#6'Height'#2'$'#3'Top'#2#6#5'Width'#2'N'#5
+'Align'#7#7'alRight'#8'AutoSize'#9#20'BorderSpacing.Around'#2#6#6'Cancel'#9#7
+'Caption'#6#6'Cancel'#21'Constraints.MinHeight'#2#25#20'Constraints.MinWidth'
+#2'K'#4'Kind'#7#8'bkCancel'#11'ModalResult'#2#2#9'NumGlyphs'#2#0#8'TabOrder'
+#2#0#0#0#7'TBitBtn'#8'OkButton'#21'AnchorSideBottom.Side'#7#9'asrBottom'#4'L'
+'eft'#3#4#1#6'Height'#2'$'#3'Top'#2#6#5'Width'#2'K'#5'Align'#7#7'alRight'#8
+'AutoSize'#9#20'BorderSpacing.Around'#2#6#7'Caption'#6#3'&OK'#21'Constraints'
+'.MinHeight'#2#25#20'Constraints.MinWidth'#2'K'#7'Default'#9#4'Kind'#7#4'bkO'
+'K'#11'ModalResult'#2#1#9'NumGlyphs'#2#0#7'OnClick'#7#13'OkButtonCLICK'#8'Ta'
+'bOrder'#2#1#0#0#7'TBitBtn'#10'HelpButton'#21'AnchorSideBottom.Side'#7#9'asr'
+'Bottom'#4'Left'#2#6#6'Height'#2'$'#3'Top'#2#6#5'Width'#2'K'#5'Align'#7#6'al'
+'Left'#8'AutoSize'#9#20'BorderSpacing.Around'#2#6#7'Caption'#6#5'&Help'#21'C'
+'onstraints.MinHeight'#2#25#20'Constraints.MinWidth'#2'K'#4'Kind'#7#6'bkHelp'
+#9'NumGlyphs'#2#0#7'OnClick'#7#15'HelpButtonClick'#8'TabOrder'#2#2#0#0#0#9'T'
+'GroupBox'#26'MissingIdentifiersGroupBox'#4'Left'#2#6#6'Height'#2':'#3'Top'#3
+#243#0#5'Width'#3#157#1#5'Align'#7#8'alBottom'#20'BorderSpacing.Around'#2#6#7
+'Caption'#6#26'MissingIdentifiersGroupBox'#12'ClientHeight'#2''''#11'ClientW'
+'idth'#3#153#1#8'TabOrder'#2#3#0#8'TListBox'#25'MissingIdentifiersListBox'#6
+'Height'#2''''#5'Width'#3#153#1#5'Align'#7#8'alClient'#8'TabOrder'#2#0#8'Top'
+'Index'#2#255#0#0#0#0
]);

View File

@ -1,3 +1,30 @@
{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
***************************************************************************
Author: Mattias Gaertner
Abstract:
Dialog for the Extract Proc feature.
Allows user choose what kind of procedure to create and shows missing
identifiers.
}
unit ExtractProcDlg;
{$mode objfpc}{$H+}
@ -5,8 +32,9 @@ unit ExtractProcDlg;
interface
uses
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls,
Buttons, StdCtrls, CodeCache, CodeToolManager, ExtractProcTool,
Classes, SysUtils, AVL_Tree, LResources, Forms, Controls, Graphics, Dialogs,
ExtCtrls, Buttons, StdCtrls,
BasicCodeTools, CodeAtom, CodeCache, CodeToolManager, ExtractProcTool,
LazarusIDEStrConsts, IDEProcs, MiscOptions, IDEContextHelpEdit;
type
@ -14,11 +42,14 @@ type
{ TExtractProcDialog }
TExtractProcDialog = class(TForm)
MissingIdentifiersListBox: TListBox;
MissingIdentifiersGroupBox: TGroupBox;
NameEdit: TEDIT;
NameGroupbox: TGROUPBOX;
OkButton: TBitBtn;
CancelButton: TBitBtn;
HelpButton: TBitBtn;
BtnPanel: TPanel;
TypeRadiogroup: TRADIOGROUP;
procedure HelpButtonClick(Sender: TObject);
procedure ExtractProcDialogCREATE(Sender: TObject);
@ -27,13 +58,16 @@ type
procedure OkButtonCLICK(Sender: TObject);
private
FMethodPossible: boolean;
FMissingIdentifiers: TAVLTree;
FSubProcSameLvlPossible: boolean;
procedure SetMissingIdentifiers(const AValue: TAVLTree);
public
procedure UpdateAvailableTypes;
function GetProcType: TExtractProcType;
function GetProcName: string;
property MethodPossible: boolean read FMethodPossible write FMethodPossible;
property SubProcSameLvlPossible: boolean read FSubProcSameLvlPossible write FSubProcSameLvlPossible;
property MissingIdentifiers: TAVLTree read FMissingIdentifiers write SetMissingIdentifiers;
end;
function ShowExtractProcDialog(Code: TCodeBuffer;
@ -53,6 +87,7 @@ var
SubProcSameLvlPossible: boolean;
ProcName: String;
ProcType: TExtractProcType;
MissingIdentifiers: TAVLTree;
begin
Result:=mrCancel;
if CompareCaret(BlockBegin,BlockEnd)<=0 then begin
@ -61,42 +96,47 @@ begin
mtInformation,[mbCancel],0);
exit;
end;
// check if selected statements can be extracted
MethodPossible:=false;
SubProcSameLvlPossible:=false;
if not CodeToolBoss.CheckExtractProc(Code,BlockBegin,BlockEnd,MethodPossible,
SubProcSameLvlPossible)
then begin
if CodeToolBoss.ErrorMessage='' then begin
MessageDlg(lisInvalidSelection,
Format(lisThisStatementCanNotBeExtractedPleaseSelectSomeCode, [#13]),
mtInformation,[mbCancel],0);
end;
exit;
end;
// ask user how to extract
ExtractProcDialog:=TExtractProcDialog.Create(nil);
MissingIdentifiers:=nil;
try
ExtractProcDialog.MethodPossible:=MethodPossible;
ExtractProcDialog.SubProcSameLvlPossible:=SubProcSameLvlPossible;
ExtractProcDialog.UpdateAvailableTypes;
Result:=ExtractProcDialog.ShowModal;
if Result<>mrOk then exit;
ProcName:=ExtractProcDialog.GetProcName;
ProcType:=ExtractProcDialog.GetProcType;
finally
ExtractProcDialog.Free;
end;
// check if selected statements can be extracted
if not CodeToolBoss.CheckExtractProc(Code,BlockBegin,BlockEnd,MethodPossible,
SubProcSameLvlPossible,MissingIdentifiers)
then begin
if CodeToolBoss.ErrorMessage='' then begin
MessageDlg(lisInvalidSelection,
Format(lisThisStatementCanNotBeExtractedPleaseSelectSomeCode, [#13]),
mtInformation,[mbCancel],0);
end;
exit;
end;
// extract procedure/method
if not CodeToolBoss.ExtractProc(Code,BlockBegin,BlockEnd,ProcType,ProcName,
NewSource,NewX,NewY,NewTopLine)
then begin
Result:=mrCancel;
exit;
// ask user how to extract
ExtractProcDialog:=TExtractProcDialog.Create(nil);
try
ExtractProcDialog.MethodPossible:=MethodPossible;
ExtractProcDialog.SubProcSameLvlPossible:=SubProcSameLvlPossible;
ExtractProcDialog.MissingIdentifiers:=MissingIdentifiers;
ExtractProcDialog.UpdateAvailableTypes;
Result:=ExtractProcDialog.ShowModal;
if Result<>mrOk then exit;
ProcName:=ExtractProcDialog.GetProcName;
ProcType:=ExtractProcDialog.GetProcType;
finally
ExtractProcDialog.Free;
end;
// extract procedure/method
if not CodeToolBoss.ExtractProc(Code,BlockBegin,BlockEnd,ProcType,ProcName,
MissingIdentifiers,NewSource,NewX,NewY,NewTopLine)
then begin
Result:=mrCancel;
exit;
end;
Result:=mrOk;
finally
CodeToolBoss.FreeTreeOfPCodeXYPosition(MissingIdentifiers);
end;
Result:=mrOk;
end;
{ TExtractProcDialog }
@ -105,10 +145,12 @@ procedure TExtractProcDialog.ExtractProcDialogCREATE(Sender: TObject);
begin
Caption:=lisExtractProcedure;
NameGroupbox.Caption:=lisNameOfNewProcedure;
OkButton.Caption:=lisExtract;
CancelButton.Caption:=dlgCancel;
TypeRadiogroup.Caption:=dlgEnvType;
NameEdit.Text:=MiscellaneousOptions.ExtractProcName;
MissingIdentifiersGroupBox.Caption:='Missing identifiers';
OkButton.Caption:=lisExtract;
CancelButton.Caption:=dlgCancel;
end;
procedure TExtractProcDialog.HelpButtonClick(Sender: TObject);
@ -136,6 +178,38 @@ begin
ModalResult:=mrOk;
end;
procedure TExtractProcDialog.SetMissingIdentifiers(const AValue: TAVLTree);
var
Node: TAVLTreeNode;
CodePos: PCodeXYPosition;
p: integer;
Identifier: string;
s: String;
begin
if AValue=FMissingIdentifiers then exit;
FMissingIdentifiers:=AValue;
MissingIdentifiersListBox.Items.BeginUpdate;
MissingIdentifiersListBox.Items.Clear;
if FMissingIdentifiers<>nil then begin
Node:=FMissingIdentifiers.FindLowest;
while Node<>nil do begin
CodePos:=PCodeXYPosition(Node.Data);
CodePos^.Code.LineColToPosition(CodePos^.Y,CodePos^.X,p);
if p>=1 then
Identifier:=GetIdentifier(@CodePos^.Code.Source[p])
else
Identifier:='?';
s:=Identifier+' at '+IntToStr(CodePos^.Y)+','+IntToStr(CodePos^.X);
MissingIdentifiersListBox.Items.Add(s);
Node:=FMissingIdentifiers.FindSuccessor(Node);
end;
end;
MissingIdentifiersListBox.Items.EndUpdate;
// show/hide the MissingIdentifiersGroupBox
MissingIdentifiersGroupBox.Visible:=MissingIdentifiersListBox.Items.Count>0;
end;
procedure TExtractProcDialog.UpdateAvailableTypes;
begin
with TypeRadiogroup.Items do begin