diff --git a/components/codetools/codeatom.pas b/components/codetools/codeatom.pas index b636c8e24a..eeca015f68 100644 --- a/components/codetools/codeatom.pas +++ b/components/codetools/codeatom.pas @@ -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 diff --git a/components/codetools/codetoolmanager.pas b/components/codetools/codetoolmanager.pas index 1b5bc99b4e..eab5b61a74 100644 --- a/components/codetools/codetoolmanager.pas +++ b/components/codetools/codetoolmanager.pas @@ -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; diff --git a/components/codetools/extractproctool.pas b/components/codetools/extractproctool.pas index c9db40a26a..6e636b3c58 100644 --- a/components/codetools/extractproctool.pas +++ b/components/codetools/extractproctool.pas @@ -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 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); - 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.StartPoscafPoint) 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); + 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.StartPoscafPoint) 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. diff --git a/components/codetools/keywordfunclists.pas b/components/codetools/keywordfunclists.pas index 7bf221918b..9c90f698f9 100644 --- a/components/codetools/keywordfunclists.pas +++ b/components/codetools/keywordfunclists.pas @@ -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; diff --git a/ide/extractprocdlg.lfm b/ide/extractprocdlg.lfm index 99054377b4..73c504fa03 100644 --- a/ide/extractprocdlg.lfm +++ b/ide/extractprocdlg.lfm @@ -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 diff --git a/ide/extractprocdlg.lrs b/ide/extractprocdlg.lrs index 316d94c134..f651f3de91 100644 --- a/ide/extractprocdlg.lrs +++ b/ide/extractprocdlg.lrs @@ -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 ]); diff --git a/ide/extractprocdlg.pas b/ide/extractprocdlg.pas index 59e169fd99..4a5d492fc0 100644 --- a/ide/extractprocdlg.pas +++ b/ide/extractprocdlg.pas @@ -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 . 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