mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 01:19:47 +01:00 
			
		
		
		
	codetools: started FindAssignMethod
git-svn-id: trunk@30491 -
This commit is contained in:
		
							parent
							
								
									1a0a37aaf9
								
							
						
					
					
						commit
						90a1dcfc9b
					
				
							
								
								
									
										3
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							@ -415,6 +415,8 @@ components/codetools/examples/addeventmethod.lpi svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/addeventmethod.lpr svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/addmethod.lpi svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/addmethod.lpr svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/addmethodassign.lpi svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/addmethodassign.lpr svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/autoindent.lpi svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/autoindent.pas svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/codecompletion.lpi svneol=native#text/plain
 | 
			
		||||
@ -461,6 +463,7 @@ components/codetools/examples/runcfgscript.lpr svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/scanexamples/BigLettersUnit.pas svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/scanexamples/abstractclass1.pas svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/scanexamples/addeventexample.pas svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/scanexamples/assignexample1.pas svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/scanexamples/brokenfilenames.pas svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/scanexamples/brokenincfiles.inc svneol=native#text/plain
 | 
			
		||||
components/codetools/examples/scanexamples/completion1.pas svneol=native#text/plain
 | 
			
		||||
 | 
			
		||||
@ -244,6 +244,18 @@ type
 | 
			
		||||
                        SourceChangeCache: TSourceChangeCache): boolean;
 | 
			
		||||
    function AddPublishedVariable(const UpperClassName,VarName, VarType: string;
 | 
			
		||||
                      SourceChangeCache: TSourceChangeCache): boolean; override;
 | 
			
		||||
    function GatherPublishedMethods(ClassNode: TCodeTreeNode;
 | 
			
		||||
                              out ListOfPFindContext: TFPList): boolean;
 | 
			
		||||
 | 
			
		||||
    // graph of definitions of a unit
 | 
			
		||||
    function GatherUnitDefinitions(out TreeOfCodeTreeNodeExt: TAVLTree;
 | 
			
		||||
                      OnlyInterface, ExceptionOnRedefinition: boolean): boolean;
 | 
			
		||||
    function BuildUnitDefinitionGraph(
 | 
			
		||||
                        out DefinitionsTreeOfCodeTreeNodeExt: TAVLTree;
 | 
			
		||||
                        out Graph: TCodeGraph; OnlyInterface: boolean): boolean;
 | 
			
		||||
    procedure WriteCodeGraphDebugReport(Graph: TCodeGraph);
 | 
			
		||||
 | 
			
		||||
    // redefinitions
 | 
			
		||||
    function GetRedefinitionNodeText(Node: TCodeTreeNode): string;
 | 
			
		||||
    function FindRedefinitions(out TreeOfCodeTreeNodeExt: TAVLTree;
 | 
			
		||||
                        WithEnums: boolean): boolean;
 | 
			
		||||
@ -253,22 +265,24 @@ type
 | 
			
		||||
                                  OnlyWrongType: boolean): boolean;
 | 
			
		||||
    function FixAliasDefinitions(TreeOfCodeTreeNodeExt: TAVLTree;
 | 
			
		||||
                                SourceChangeCache: TSourceChangeCache): boolean;
 | 
			
		||||
 | 
			
		||||
    // const functions
 | 
			
		||||
    function FindConstFunctions(out TreeOfCodeTreeNodeExt: TAVLTree): boolean;
 | 
			
		||||
    function ReplaceConstFunctions(TreeOfCodeTreeNodeExt: TAVLTree;
 | 
			
		||||
                                SourceChangeCache: TSourceChangeCache): boolean;
 | 
			
		||||
    function FindTypeCastFunctions(out TreeOfCodeTreeNodeExt: TAVLTree): boolean;
 | 
			
		||||
 | 
			
		||||
    // typecast functions
 | 
			
		||||
    function ReplaceTypeCastFunctions(TreeOfCodeTreeNodeExt: TAVLTree;
 | 
			
		||||
                                SourceChangeCache: TSourceChangeCache): boolean;
 | 
			
		||||
    function MovePointerTypesToTargetSections(
 | 
			
		||||
                                SourceChangeCache: TSourceChangeCache): boolean;
 | 
			
		||||
 | 
			
		||||
    // sort procs
 | 
			
		||||
    function FixForwardDefinitions(SourceChangeCache: TSourceChangeCache
 | 
			
		||||
                                   ): boolean;
 | 
			
		||||
    function GatherUnitDefinitions(out TreeOfCodeTreeNodeExt: TAVLTree;
 | 
			
		||||
                      OnlyInterface, ExceptionOnRedefinition: boolean): boolean;
 | 
			
		||||
    function BuildUnitDefinitionGraph(
 | 
			
		||||
                        out DefinitionsTreeOfCodeTreeNodeExt: TAVLTree;
 | 
			
		||||
                        out Graph: TCodeGraph; OnlyInterface: boolean): boolean;
 | 
			
		||||
    procedure WriteCodeGraphDebugReport(Graph: TCodeGraph);
 | 
			
		||||
 | 
			
		||||
    // empty functions
 | 
			
		||||
    function FindEmptyMethods(CursorPos: TCodeXYPosition;
 | 
			
		||||
                              const AClassName: string; // can be ''
 | 
			
		||||
                              const Sections: TPascalClassSections;
 | 
			
		||||
@ -286,12 +300,23 @@ type
 | 
			
		||||
                              out AllRemoved: boolean;
 | 
			
		||||
                              const Attr: TProcHeadAttributes;
 | 
			
		||||
                              out RemovedProcHeads: TStrings): boolean;
 | 
			
		||||
    function GatherPublishedMethods(ClassNode: TCodeTreeNode;
 | 
			
		||||
                              out ListOfPFindContext: TFPList): boolean;
 | 
			
		||||
 | 
			
		||||
    // assign/init records/classes
 | 
			
		||||
    function FindAssignMethod(CursorPos: TCodeXYPosition;
 | 
			
		||||
        out ClassNode, AncestorClassNode: TCodeTreeNode;
 | 
			
		||||
        out AssignDeclNode: TCodeTreeNode;
 | 
			
		||||
        var MemberNodeExts: TAVLTree; // tree of TCodeTreeNodeExtension, Node=var or property, Data=write property
 | 
			
		||||
        out AssignBodyNode: TCodeTreeNode): boolean;
 | 
			
		||||
    function AddAssignMethod(MemberNodeExts: TAVLTree;
 | 
			
		||||
        const ProcName, ParamName, ParamType: string;
 | 
			
		||||
        CallInherited, CallInheritedOnlyInElse: boolean;
 | 
			
		||||
        SourceChanger: TSourceChangeCache): boolean;
 | 
			
		||||
 | 
			
		||||
    // custom class completion
 | 
			
		||||
    function InitClassCompletion(const AClassName: string;
 | 
			
		||||
                                 SourceChangeCache: TSourceChangeCache): boolean;
 | 
			
		||||
    function InitClassCompletion(ClassNode: TCodeTreeNode;
 | 
			
		||||
                                 SourceChangeCache: TSourceChangeCache): boolean;
 | 
			
		||||
    function ApplyClassCompletion(AddMissingProcBodies: boolean): boolean;
 | 
			
		||||
    function ProcExistsInCodeCompleteClass(
 | 
			
		||||
                                    const NameAndParamsUpCase: string): boolean;
 | 
			
		||||
@ -4987,6 +5012,237 @@ begin
 | 
			
		||||
  end;
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
function TCodeCompletionCodeTool.FindAssignMethod(CursorPos: TCodeXYPosition;
 | 
			
		||||
  out ClassNode, AncestorClassNode: TCodeTreeNode;
 | 
			
		||||
  out AssignDeclNode: TCodeTreeNode; var MemberNodeExts: TAVLTree;
 | 
			
		||||
  out AssignBodyNode: TCodeTreeNode): boolean;
 | 
			
		||||
{ if CursorPos is in a class declaration search for a method "Assign"
 | 
			
		||||
  and its corresponding body.
 | 
			
		||||
  If CursorPos is in a method body use this as a Assign method and return
 | 
			
		||||
  its corresponding declararion.
 | 
			
		||||
  If neither return false.
 | 
			
		||||
  Also return a tree of all variables and properties (excluding ancestors).
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  procedure SearchAssign(Node: TCodeTreeNode);
 | 
			
		||||
  var
 | 
			
		||||
    Child: TCodeTreeNode;
 | 
			
		||||
  begin
 | 
			
		||||
    if Node=nil then exit;
 | 
			
		||||
    Child:=Node.FirstChild;
 | 
			
		||||
    while Child<>nil do begin
 | 
			
		||||
      if Child.Desc in AllClassSections then
 | 
			
		||||
        SearchAssign(Child)
 | 
			
		||||
      else if Child.Desc=ctnProcedure then begin
 | 
			
		||||
        if ExtractProcName(Child,[phpInUpperCase])='ASSIGN' then begin
 | 
			
		||||
          if AssignDeclNode<>nil then begin
 | 
			
		||||
            debugln(['WARNING: TCodeCompletionCodeTool.FindAssignMethod.SearchAssign'
 | 
			
		||||
              +' multiple Assign methods found, using the first at ',CleanPosToStr(AssignDeclNode.StartPos)]);
 | 
			
		||||
          end else
 | 
			
		||||
            AssignDeclNode:=Child;
 | 
			
		||||
        end;
 | 
			
		||||
      end;
 | 
			
		||||
      Child:=Child.NextBrother;
 | 
			
		||||
    end;
 | 
			
		||||
  end;
 | 
			
		||||
 | 
			
		||||
  procedure GatherAssignableMembers(Node: TCodeTreeNode);
 | 
			
		||||
  var
 | 
			
		||||
    Child: TCodeTreeNode;
 | 
			
		||||
    NodeExt: TCodeTreeNodeExtension;
 | 
			
		||||
  begin
 | 
			
		||||
    if Node=nil then exit;
 | 
			
		||||
    Child:=Node.FirstChild;
 | 
			
		||||
    while Child<>nil do begin
 | 
			
		||||
      if Child.Desc in AllClassSections then
 | 
			
		||||
        GatherAssignableMembers(Child)
 | 
			
		||||
      else if (Child.Desc=ctnVarDefinition)
 | 
			
		||||
      or ((Child.Desc=ctnProperty)
 | 
			
		||||
        and (PropertyHasSpecifier(Child,'read'))
 | 
			
		||||
        and (PropertyHasSpecifier(Child,'write')))
 | 
			
		||||
      then begin
 | 
			
		||||
        // a variable or a property which is readable and writable
 | 
			
		||||
        if MemberNodeExts=nil then
 | 
			
		||||
          MemberNodeExts:=TAVLTree.Create(@CompareCodeTreeNodeExtTxtAndPos);
 | 
			
		||||
        NodeExt:=TCodeTreeNodeExtension.Create;
 | 
			
		||||
        NodeExt.Node:=Child;
 | 
			
		||||
        NodeExt.Position:=Child.StartPos;
 | 
			
		||||
        if Child.Desc=ctnVarDefinition then
 | 
			
		||||
          NodeExt.Txt:=ExtractDefinitionName(Child)
 | 
			
		||||
        else
 | 
			
		||||
          NodeExt.Txt:=ExtractPropName(Child,false);
 | 
			
		||||
        MemberNodeExts.Add(NodeExt);
 | 
			
		||||
      end;
 | 
			
		||||
 | 
			
		||||
      Child:=Child.NextBrother;
 | 
			
		||||
    end;
 | 
			
		||||
  end;
 | 
			
		||||
 | 
			
		||||
  procedure FindVarsWrittenByProperties;
 | 
			
		||||
  var
 | 
			
		||||
    AVLNode: TAVLTreeNode;
 | 
			
		||||
    NodeExt: TCodeTreeNodeExtension;
 | 
			
		||||
    WrittenNodeExt: TCodeTreeNodeExtension;
 | 
			
		||||
  begin
 | 
			
		||||
    if MemberNodeExts=nil then exit;
 | 
			
		||||
    AVLNode:=MemberNodeExts.FindLowest;
 | 
			
		||||
    while AVLNode<>nil do begin
 | 
			
		||||
      NodeExt:=TCodeTreeNodeExtension(AVLNode.Data);
 | 
			
		||||
      if NodeExt.Node.Desc=ctnProperty then begin
 | 
			
		||||
        if PropertyHasSpecifier(NodeExt.Node,'write') then begin
 | 
			
		||||
          ReadNextAtom;
 | 
			
		||||
          if AtomIsIdentifier(false) then begin
 | 
			
		||||
            WrittenNodeExt:=FindCodeTreeNodeExtWithIdentifier(MemberNodeExts,
 | 
			
		||||
                                      @Src[CurPos.StartPos]);
 | 
			
		||||
            if WrittenNodeExt<>nil then
 | 
			
		||||
              WrittenNodeExt.Data:=NodeExt.Node;
 | 
			
		||||
          end;
 | 
			
		||||
        end;
 | 
			
		||||
      end;
 | 
			
		||||
      AVLNode:=MemberNodeExts.FindSuccessor(AVLNode);
 | 
			
		||||
    end;
 | 
			
		||||
  end;
 | 
			
		||||
 | 
			
		||||
  procedure FindAncestor;
 | 
			
		||||
  var
 | 
			
		||||
    Params: TFindDeclarationParams;
 | 
			
		||||
  begin
 | 
			
		||||
    if ClassNode=nil then exit;
 | 
			
		||||
    Params:=TFindDeclarationParams.Create;
 | 
			
		||||
    try
 | 
			
		||||
      //FindAncestorOfClassInheritance();
 | 
			
		||||
 | 
			
		||||
    finally
 | 
			
		||||
      Params.Free;
 | 
			
		||||
    end;
 | 
			
		||||
  end;
 | 
			
		||||
 | 
			
		||||
var
 | 
			
		||||
  CleanPos: integer;
 | 
			
		||||
  CursorNode: TCodeTreeNode;
 | 
			
		||||
  Node: TCodeTreeNode;
 | 
			
		||||
begin
 | 
			
		||||
  Result:=false;
 | 
			
		||||
  ClassNode:=nil;
 | 
			
		||||
  AncestorClassNode:=nil;
 | 
			
		||||
  AssignDeclNode:=nil;
 | 
			
		||||
  AssignBodyNode:=nil;
 | 
			
		||||
  BuildTreeAndGetCleanPos(CursorPos,CleanPos);
 | 
			
		||||
  // check context
 | 
			
		||||
  CursorNode:=FindDeepestNodeAtPos(CleanPos,true);
 | 
			
		||||
  Node:=CursorNode;
 | 
			
		||||
  while (Node<>nil) do begin
 | 
			
		||||
    if (Node.Desc=ctnProcedure) then begin
 | 
			
		||||
      if NodeIsMethodBody(Node) then begin
 | 
			
		||||
        // cursor in method body
 | 
			
		||||
        AssignBodyNode:=Node;
 | 
			
		||||
        Result:=true;
 | 
			
		||||
        AssignDeclNode:=FindCorrespondingProcNode(AssignBodyNode);
 | 
			
		||||
        if AssignDeclNode<>nil then
 | 
			
		||||
          ClassNode:=FindClassOrInterfaceNode(AssignDeclNode.Parent);
 | 
			
		||||
        break;
 | 
			
		||||
      end;
 | 
			
		||||
    end else if (Node.Desc in AllClassObjects) then begin
 | 
			
		||||
      // cursor in class/record
 | 
			
		||||
      Result:=true;
 | 
			
		||||
      ClassNode:=Node;
 | 
			
		||||
      SearchAssign(ClassNode);
 | 
			
		||||
      if AssignDeclNode<>nil then
 | 
			
		||||
        AssignBodyNode:=FindCorrespondingProcNode(AssignDeclNode);
 | 
			
		||||
      break;
 | 
			
		||||
    end;
 | 
			
		||||
    Node:=Node.Parent;
 | 
			
		||||
  end;
 | 
			
		||||
  if ClassNode=nil then exit;
 | 
			
		||||
  GatherAssignableMembers(ClassNode);
 | 
			
		||||
  FindVarsWrittenByProperties;
 | 
			
		||||
  FindAncestor;
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
function TCodeCompletionCodeTool.AddAssignMethod(MemberNodeExts: TAVLTree;
 | 
			
		||||
  const ProcName, ParamName, ParamType: string; CallInherited,
 | 
			
		||||
  CallInheritedOnlyInElse: boolean; SourceChanger: TSourceChangeCache): boolean;
 | 
			
		||||
var
 | 
			
		||||
  AVLNode: TAVLTreeNode;
 | 
			
		||||
  NodeExt: TCodeTreeNodeExtension;
 | 
			
		||||
  Node: TCodeTreeNode;
 | 
			
		||||
  CleanDef: String;
 | 
			
		||||
  Def: String;
 | 
			
		||||
  ClassNode: TCodeTreeNode;
 | 
			
		||||
  aClassName: String;
 | 
			
		||||
  ProcBody: String;
 | 
			
		||||
  e: String;
 | 
			
		||||
  SameType: boolean;
 | 
			
		||||
  Indent: Integer;
 | 
			
		||||
  IndentStep: LongInt;
 | 
			
		||||
  LocalVar: String;
 | 
			
		||||
begin
 | 
			
		||||
  Result:=false;
 | 
			
		||||
  if (MemberNodeExts=nil) or (MemberNodeExts.Count=0) then exit(true);
 | 
			
		||||
  if (ParamName='') or (ParamType='') then exit;
 | 
			
		||||
  ClassNode:=nil;
 | 
			
		||||
  CleanDef:='procedure '+ProcName+'(:'+ParamType+')';
 | 
			
		||||
  Def:='procedure '+ProcName+'('+ParamName+':'+ParamType+')';
 | 
			
		||||
  ProcBody:='';
 | 
			
		||||
  AVLNode:=MemberNodeExts.FindLowest;
 | 
			
		||||
  e:=SourceChanger.BeautifyCodeOptions.LineEnd;
 | 
			
		||||
  SameType:=true;
 | 
			
		||||
  Indent:=0;
 | 
			
		||||
  IndentStep:=SourceChanger.BeautifyCodeOptions.Indent;
 | 
			
		||||
  LocalVar:=ParamName;
 | 
			
		||||
  while AVLNode<>nil do begin
 | 
			
		||||
    NodeExt:=TCodeTreeNodeExtension(AVLNode.Data);
 | 
			
		||||
    Node:=NodeExt.Node;
 | 
			
		||||
    if ClassNode=nil then begin
 | 
			
		||||
      // first assignment
 | 
			
		||||
      // => get the classnode, create the proc header
 | 
			
		||||
      ClassNode:=FindClassOrInterfaceNode(Node.Parent);
 | 
			
		||||
      aClassName:=ExtractClassName(ClassNode,false);
 | 
			
		||||
      SameType:=CompareIdentifiers(PChar(aClassName),PChar(ParamType))=0;
 | 
			
		||||
      ProcBody:='procedure '+aClassName+'.'+ProcName+'('+ParamName+':'+ParamType+')'+e;
 | 
			
		||||
      if not SameType then begin
 | 
			
		||||
        LocalVar:='aSrc';
 | 
			
		||||
        if CompareIdentifiers(PChar(LocalVar),PChar(ParamName))=0 then
 | 
			
		||||
          LocalVar:='aSource';
 | 
			
		||||
        ProcBody:=ProcBody+'var'+e
 | 
			
		||||
           +GetIndentStr(Indent+IndentStep)+LocalVar+':'+aClassName+';'+e;
 | 
			
		||||
      end;
 | 
			
		||||
      ProcBody:=ProcBody+'begin'+e;
 | 
			
		||||
      inc(Indent,IndentStep);
 | 
			
		||||
      if CallInherited and (not CallInheritedOnlyInElse) then
 | 
			
		||||
        ProcBody:=ProcBody
 | 
			
		||||
          +GetIndentStr(Indent)+'inherited '+ProcName+'('+ParamName+');'+e;
 | 
			
		||||
      if not SameType then begin
 | 
			
		||||
        // add a parameter check to the new procedure
 | 
			
		||||
        ProcBody:=ProcBody
 | 
			
		||||
            +GetIndentStr(Indent)+'if '+ParamName+' is '+aClassName+' then'+e
 | 
			
		||||
            +GetIndentStr(Indent)+'begin'+e;
 | 
			
		||||
        inc(Indent,IndentStep);
 | 
			
		||||
        ProcBody:=ProcBody+GetIndentStr(Indent)+LocalVar+':='+aClassName+'('+ParamName+');'+e;
 | 
			
		||||
      end;
 | 
			
		||||
    end;
 | 
			
		||||
    // add assignment
 | 
			
		||||
    ProcBody:=ProcBody+GetIndentStr(Indent)+NodeExt.Txt+':='+LocalVar+'.'+NodeExt.Txt+';'+e;
 | 
			
		||||
    AVLNode:=MemberNodeExts.FindSuccessor(AVLNode);
 | 
			
		||||
  end;
 | 
			
		||||
  if not SameType then begin
 | 
			
		||||
    // close if block
 | 
			
		||||
    dec(Indent,IndentStep);
 | 
			
		||||
    if CallInherited and CallInheritedOnlyInElse then begin
 | 
			
		||||
      ProcBody:=ProcBody+GetIndentStr(Indent)+'end else'+e
 | 
			
		||||
          +GetIndentStr(Indent+IndentStep)+'inherited '+ProcName+'('+ParamName+');'+e;
 | 
			
		||||
    end else begin
 | 
			
		||||
      ProcBody:=ProcBody+GetIndentStr(Indent)+'end;'+e
 | 
			
		||||
    end;
 | 
			
		||||
  end;
 | 
			
		||||
  // close procedure body
 | 
			
		||||
  ProcBody:=ProcBody+'end;';
 | 
			
		||||
  // apply
 | 
			
		||||
  if not InitClassCompletion(ClassNode,SourceChanger) then exit;
 | 
			
		||||
  AddClassInsertion(CleanDef,Def,ProcName,ncpPublicProcs,nil,ProcBody);
 | 
			
		||||
  Result:=ApplyClassCompletion(true);
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
function TCodeCompletionCodeTool.GatherPublishedMethods(
 | 
			
		||||
  ClassNode: TCodeTreeNode; out ListOfPFindContext: TFPList): boolean;
 | 
			
		||||
var
 | 
			
		||||
@ -5020,7 +5276,13 @@ begin
 | 
			
		||||
  if ScannedRange<>lsrEnd then exit;
 | 
			
		||||
  if (SourceChangeCache=nil) or (Scanner=nil) then exit;
 | 
			
		||||
  ClassNode:=FindClassNodeInUnit(AClassName,true,false,false,true);
 | 
			
		||||
  if (ClassNode=nil) then exit;
 | 
			
		||||
  Result:=InitClassCompletion(ClassNode,SourceChangeCache);
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
function TCodeCompletionCodeTool.InitClassCompletion(ClassNode: TCodeTreeNode;
 | 
			
		||||
  SourceChangeCache: TSourceChangeCache): boolean;
 | 
			
		||||
begin
 | 
			
		||||
  if (ClassNode=nil) then exit(false);
 | 
			
		||||
  CodeCompleteClassNode:=ClassNode;
 | 
			
		||||
  CodeCompleteSrcChgCache:=SourceChangeCache;
 | 
			
		||||
  FreeClassInsertionList;
 | 
			
		||||
 | 
			
		||||
@ -550,6 +550,13 @@ type
 | 
			
		||||
          FunctionResultVariableStartPos: integer = 0
 | 
			
		||||
          ): boolean;
 | 
			
		||||
 | 
			
		||||
    // Assign method
 | 
			
		||||
    function FindAssignMethod(Code: TCodeBuffer; X, Y: integer;
 | 
			
		||||
          out Tool: TCodeTool; out ClassNode, AncestorClassNode: TCodeTreeNode;
 | 
			
		||||
          out AssignDeclNode: TCodeTreeNode;
 | 
			
		||||
          var MemberNodeExts: TAVLTree; // tree of TCodeTreeNodeExtension, Node=var or property, Data=write property
 | 
			
		||||
          out AssignBodyNode: TCodeTreeNode): boolean;
 | 
			
		||||
 | 
			
		||||
    // code templates
 | 
			
		||||
    function InsertCodeTemplate(Code: TCodeBuffer;
 | 
			
		||||
          SelectionStart, SelectionEnd: TPoint;
 | 
			
		||||
@ -3896,6 +3903,32 @@ begin
 | 
			
		||||
  end;
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
function TCodeToolManager.FindAssignMethod(Code: TCodeBuffer; X, Y: integer;
 | 
			
		||||
  out Tool: TCodeTool; out ClassNode, AncestorClassNode: TCodeTreeNode;
 | 
			
		||||
  out AssignDeclNode: TCodeTreeNode; var MemberNodeExts: TAVLTree;
 | 
			
		||||
  out AssignBodyNode: TCodeTreeNode): boolean;
 | 
			
		||||
var
 | 
			
		||||
  CodePos: TCodeXYPosition;
 | 
			
		||||
begin
 | 
			
		||||
  {$IFDEF CTDEBUG}
 | 
			
		||||
  DebugLn('TCodeToolManager.FindAssignMethod A ',Code.Filename);
 | 
			
		||||
  {$ENDIF}
 | 
			
		||||
  Result:=false;
 | 
			
		||||
  AssignDeclNode:=nil;
 | 
			
		||||
  AssignBodyNode:=nil;
 | 
			
		||||
  if not InitCurCodeTool(Code) then exit;
 | 
			
		||||
  Tool:=FCurCodeTool;
 | 
			
		||||
  CodePos.X:=X;
 | 
			
		||||
  CodePos.Y:=Y;
 | 
			
		||||
  CodePos.Code:=Code;
 | 
			
		||||
  try
 | 
			
		||||
    Result:=FCurCodeTool.FindAssignMethod(CodePos,ClassNode,AncestorClassNode,
 | 
			
		||||
           AssignDeclNode,MemberNodeExts,AssignBodyNode);
 | 
			
		||||
  except
 | 
			
		||||
    on e: Exception do Result:=HandleException(e);
 | 
			
		||||
  end;
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
function TCodeToolManager.InsertCodeTemplate(Code: TCodeBuffer;
 | 
			
		||||
  SelectionStart, SelectionEnd: TPoint; TopLine: integer;
 | 
			
		||||
  CodeTemplate: TCodeToolTemplate; var NewCode: TCodeBuffer; var NewX, NewY,
 | 
			
		||||
 | 
			
		||||
@ -309,10 +309,11 @@ function CompareTxtWithCodeTreeNodeExt(p: Pointer;
 | 
			
		||||
                                       NodeData: pointer): integer;
 | 
			
		||||
function CompareIdentifierWithCodeTreeNodeExt(p: Pointer;
 | 
			
		||||
                                              NodeData: pointer): integer;
 | 
			
		||||
function CompareCodeTreeNodeExt(NodeData1, NodeData2: pointer): integer;
 | 
			
		||||
function CompareCodeTreeNodeExtWithPos(NodeData1, NodeData2: pointer): integer;
 | 
			
		||||
function CompareCodeTreeNodeExt(NodeData1, NodeData2: pointer): integer; // Txt
 | 
			
		||||
function CompareCodeTreeNodeExtWithPos(NodeData1, NodeData2: pointer): integer; // Position
 | 
			
		||||
function CompareCodeTreeNodeExtWithNodeStartPos(
 | 
			
		||||
  NodeData1, NodeData2: pointer): integer;
 | 
			
		||||
  NodeData1, NodeData2: pointer): integer; // Node.StartPos
 | 
			
		||||
function CompareCodeTreeNodeExtTxtAndPos(NodeData1, NodeData2: pointer): integer; // Txt, then Position
 | 
			
		||||
function CompareCodeTreeNodeExtWithNode(NodeData1, NodeData2: pointer): integer;
 | 
			
		||||
function ComparePointerWithCodeTreeNodeExtNode(p: Pointer;
 | 
			
		||||
                                               NodeExt: pointer): integer;
 | 
			
		||||
@ -497,9 +498,8 @@ function CompareTxtWithCodeTreeNodeExt(p: Pointer; NodeData: pointer
 | 
			
		||||
  ): integer;
 | 
			
		||||
var
 | 
			
		||||
  s: String;
 | 
			
		||||
  NodeExt: TCodeTreeNodeExtension;
 | 
			
		||||
  NodeExt: TCodeTreeNodeExtension absolute NodeData;
 | 
			
		||||
begin
 | 
			
		||||
  NodeExt:=TCodeTreeNodeExtension(NodeData);
 | 
			
		||||
  s:=PAnsistring(p)^;
 | 
			
		||||
  Result:=CompareTextIgnoringSpace(s,NodeExt.Txt,false);
 | 
			
		||||
  //debugln('CompareTxtWithCodeTreeNodeExt ',NodeExt.Txt,' ',s,' ',dbgs(Result));
 | 
			
		||||
@ -508,17 +508,17 @@ end;
 | 
			
		||||
function CompareIdentifierWithCodeTreeNodeExt(p: Pointer; NodeData: pointer
 | 
			
		||||
  ): integer;
 | 
			
		||||
var
 | 
			
		||||
  NodeExt: TCodeTreeNodeExtension;
 | 
			
		||||
  NodeExt: TCodeTreeNodeExtension absolute NodeData;
 | 
			
		||||
begin
 | 
			
		||||
  NodeExt:=TCodeTreeNodeExtension(NodeData);
 | 
			
		||||
  Result:=CompareIdentifierPtrs(p,Pointer(NodeExt.Txt));
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
function CompareCodeTreeNodeExt(NodeData1, NodeData2: pointer): integer;
 | 
			
		||||
var NodeExt1, NodeExt2: TCodeTreeNodeExtension;
 | 
			
		||||
var
 | 
			
		||||
  NodeExt1: TCodeTreeNodeExtension absolute NodeData1;
 | 
			
		||||
  NodeExt2: TCodeTreeNodeExtension absolute NodeData2;
 | 
			
		||||
begin
 | 
			
		||||
  NodeExt1:=TCodeTreeNodeExtension(NodeData1);
 | 
			
		||||
  NodeExt2:=TCodeTreeNodeExtension(NodeData2);
 | 
			
		||||
  Result:=CompareTextIgnoringSpace(NodeExt1.Txt,NodeExt2.Txt,false);
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
@ -549,6 +549,22 @@ begin
 | 
			
		||||
    Result:=0;
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
function CompareCodeTreeNodeExtTxtAndPos(NodeData1, NodeData2: pointer
 | 
			
		||||
  ): integer;
 | 
			
		||||
var
 | 
			
		||||
  NodeExt1: TCodeTreeNodeExtension absolute NodeData1;
 | 
			
		||||
  NodeExt2: TCodeTreeNodeExtension absolute NodeData2;
 | 
			
		||||
begin
 | 
			
		||||
  Result:=CompareTextIgnoringSpace(NodeExt1.Txt,NodeExt2.Txt,false);
 | 
			
		||||
  if Result<>0 then exit;
 | 
			
		||||
  if NodeExt1.Position<NodeExt2.Position then
 | 
			
		||||
    Result:=1
 | 
			
		||||
  else if NodeExt1.Position>NodeExt2.Position then
 | 
			
		||||
    Result:=-1
 | 
			
		||||
  else
 | 
			
		||||
    Result:=0;
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
function CompareCodeTreeNodeExtWithNode(NodeData1, NodeData2: pointer): integer;
 | 
			
		||||
var
 | 
			
		||||
  Node1: TCodeTreeNode;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								components/codetools/examples/addmethodassign.lpi
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								components/codetools/examples/addmethodassign.lpi
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<CONFIG>
 | 
			
		||||
  <ProjectOptions>
 | 
			
		||||
    <Version Value="9"/>
 | 
			
		||||
    <General>
 | 
			
		||||
      <Flags>
 | 
			
		||||
        <MainUnitHasCreateFormStatements Value="False"/>
 | 
			
		||||
        <MainUnitHasTitleStatement Value="False"/>
 | 
			
		||||
        <LRSInOutputDirectory Value="False"/>
 | 
			
		||||
      </Flags>
 | 
			
		||||
      <SessionStorage Value="InProjectDir"/>
 | 
			
		||||
      <MainUnit Value="0"/>
 | 
			
		||||
      <Title Value="addmethodassign"/>
 | 
			
		||||
    </General>
 | 
			
		||||
    <BuildModes Count="1">
 | 
			
		||||
      <Item1 Name="default" Default="True"/>
 | 
			
		||||
    </BuildModes>
 | 
			
		||||
    <PublishOptions>
 | 
			
		||||
      <Version Value="2"/>
 | 
			
		||||
      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
 | 
			
		||||
      <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
 | 
			
		||||
    </PublishOptions>
 | 
			
		||||
    <RunParams>
 | 
			
		||||
      <local>
 | 
			
		||||
        <FormatVersion Value="1"/>
 | 
			
		||||
        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
 | 
			
		||||
      </local>
 | 
			
		||||
    </RunParams>
 | 
			
		||||
    <RequiredPackages Count="1">
 | 
			
		||||
      <Item1>
 | 
			
		||||
        <PackageName Value="CodeTools"/>
 | 
			
		||||
      </Item1>
 | 
			
		||||
    </RequiredPackages>
 | 
			
		||||
    <Units Count="4">
 | 
			
		||||
      <Unit0>
 | 
			
		||||
        <Filename Value="addmethodassign.lpr"/>
 | 
			
		||||
        <IsPartOfProject Value="True"/>
 | 
			
		||||
        <UnitName Value="addmethodassign"/>
 | 
			
		||||
      </Unit0>
 | 
			
		||||
      <Unit1>
 | 
			
		||||
        <Filename Value="scanexamples/simpleunit1.pas"/>
 | 
			
		||||
        <IsPartOfProject Value="True"/>
 | 
			
		||||
        <UnitName Value="SimpleUnit1"/>
 | 
			
		||||
      </Unit1>
 | 
			
		||||
      <Unit2>
 | 
			
		||||
        <Filename Value="scanexamples/addeventexample.pas"/>
 | 
			
		||||
        <IsPartOfProject Value="True"/>
 | 
			
		||||
        <UnitName Value="AddEventExample"/>
 | 
			
		||||
      </Unit2>
 | 
			
		||||
      <Unit3>
 | 
			
		||||
        <Filename Value="scanexamples/assignexample1.pas"/>
 | 
			
		||||
        <IsPartOfProject Value="True"/>
 | 
			
		||||
        <UnitName Value="AssignExample1"/>
 | 
			
		||||
      </Unit3>
 | 
			
		||||
    </Units>
 | 
			
		||||
  </ProjectOptions>
 | 
			
		||||
  <CompilerOptions>
 | 
			
		||||
    <Version Value="10"/>
 | 
			
		||||
    <SearchPaths>
 | 
			
		||||
      <OtherUnitFiles Value="scanexamples"/>
 | 
			
		||||
    </SearchPaths>
 | 
			
		||||
    <Other>
 | 
			
		||||
      <CompilerPath Value="$(CompPath)"/>
 | 
			
		||||
    </Other>
 | 
			
		||||
  </CompilerOptions>
 | 
			
		||||
</CONFIG>
 | 
			
		||||
							
								
								
									
										104
									
								
								components/codetools/examples/addmethodassign.lpr
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								components/codetools/examples/addmethodassign.lpr
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,104 @@
 | 
			
		||||
{
 | 
			
		||||
 ***************************************************************************
 | 
			
		||||
 *                                                                         *
 | 
			
		||||
 *   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:
 | 
			
		||||
    Demonstrating, how to add a method Assign to a class.
 | 
			
		||||
}
 | 
			
		||||
program AddMethodAssign;
 | 
			
		||||
 | 
			
		||||
{$mode objfpc}{$H+}
 | 
			
		||||
 | 
			
		||||
uses
 | 
			
		||||
  Classes, SysUtils, CodeCache, CodeToolManager, FileProcs, AVL_Tree,
 | 
			
		||||
  BasicCodeTools, SourceChanger, CodeTree, AssignExample1;
 | 
			
		||||
  
 | 
			
		||||
var
 | 
			
		||||
  Filename: string;
 | 
			
		||||
  Code: TCodeBuffer;
 | 
			
		||||
  Tool: TCodeTool;
 | 
			
		||||
  AssignDeclNode: TCodeTreeNode;
 | 
			
		||||
  MemberNodeExts: TAVLTree;
 | 
			
		||||
  AssignBodyNode: TCodeTreeNode;
 | 
			
		||||
  AVLNode: TAVLTreeNode;
 | 
			
		||||
  NodeExt: TCodeTreeNodeExtension;
 | 
			
		||||
  NextAVLNode: TAVLTreeNode;
 | 
			
		||||
  ClassNode: TCodeTreeNode;
 | 
			
		||||
  AncestorClassNode: TCodeTreeNode;
 | 
			
		||||
begin
 | 
			
		||||
  // load the file
 | 
			
		||||
  Filename:=ExpandFileName(SetDirSeparators('scanexamples/assignexample1.pas'));
 | 
			
		||||
  Code:=CodeToolBoss.LoadFile(Filename,false,false);
 | 
			
		||||
  if Code=nil then
 | 
			
		||||
    raise Exception.Create('loading failed '+Filename);
 | 
			
		||||
 | 
			
		||||
  // parse the unit, check if in a class with an Assign method
 | 
			
		||||
  try
 | 
			
		||||
    MemberNodeExts:=nil;
 | 
			
		||||
    if not CodeToolBoss.FindAssignMethod(Code,3,18,Tool,
 | 
			
		||||
      ClassNode,AncestorClassNode,
 | 
			
		||||
      AssignDeclNode,MemberNodeExts,AssignBodyNode) then
 | 
			
		||||
      raise Exception.Create('parser error');
 | 
			
		||||
 | 
			
		||||
    debugln(['Assign declaration found: ',AssignDeclNode<>nil]);
 | 
			
		||||
    debugln(['Assign body found: ',AssignBodyNode<>nil]);
 | 
			
		||||
 | 
			
		||||
    // remove nodes which are written by a property
 | 
			
		||||
    if MemberNodeExts<>nil then begin
 | 
			
		||||
      AVLNode:=MemberNodeExts.FindLowest;
 | 
			
		||||
      while AVLNode<>nil do begin
 | 
			
		||||
        NextAVLNode:=MemberNodeExts.FindSuccessor(AVLNode);
 | 
			
		||||
        NodeExt:=TCodeTreeNodeExtension(AVLNode.Data);
 | 
			
		||||
        if NodeExt.Data<>nil then begin
 | 
			
		||||
          debugln(['skipping identifier ',NodeExt.Txt,' because it is written by a property']);
 | 
			
		||||
          MemberNodeExts.FreeAndDelete(AVLNode);
 | 
			
		||||
        end else begin
 | 
			
		||||
          debugln('assigning identifier ',NodeExt.Txt,' ...');
 | 
			
		||||
        end;
 | 
			
		||||
        AVLNode:=NextAVLNode;
 | 
			
		||||
      end;
 | 
			
		||||
    end;
 | 
			
		||||
    if (MemberNodeExts=nil) or (MemberNodeExts.Count=0) then begin
 | 
			
		||||
      debugln('no assignable members found');
 | 
			
		||||
      exit;
 | 
			
		||||
    end;
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    if AssignDeclNode=nil then begin
 | 
			
		||||
      if not Tool.AddAssignMethod(MemberNodeExts,'Assign','Source','TObject',
 | 
			
		||||
             true,false,
 | 
			
		||||
             CodeToolBoss.SourceChangeCache)
 | 
			
		||||
      then
 | 
			
		||||
        raise Exception.Create('AddAssignMethod failed');
 | 
			
		||||
    end else begin
 | 
			
		||||
      debugln(['there is already an Assign method']);
 | 
			
		||||
    end;
 | 
			
		||||
 | 
			
		||||
  finally
 | 
			
		||||
    DisposeAVLTree(MemberNodeExts);
 | 
			
		||||
  end;
 | 
			
		||||
  // write the new source:
 | 
			
		||||
  writeln('-----------------------------------');
 | 
			
		||||
  writeln('New source:');
 | 
			
		||||
  writeln(Code.Source);
 | 
			
		||||
  writeln('-----------------------------------');
 | 
			
		||||
end.
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,32 @@
 | 
			
		||||
unit AssignExample1;
 | 
			
		||||
 | 
			
		||||
{$mode objfpc}{$H+}
 | 
			
		||||
 | 
			
		||||
interface
 | 
			
		||||
 | 
			
		||||
uses
 | 
			
		||||
  Classes, sysutils;
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
 | 
			
		||||
  { TMyPersistent }
 | 
			
		||||
 | 
			
		||||
  TMyPersistent = class(TComponent)
 | 
			
		||||
  private
 | 
			
		||||
    FMyInt: integer;
 | 
			
		||||
  public
 | 
			
		||||
    procedure CopyFrom(Src: TMyPersistent);
 | 
			
		||||
    property MyInt: integer read FMyInt write FMyInt;
 | 
			
		||||
  end;
 | 
			
		||||
 | 
			
		||||
implementation
 | 
			
		||||
 | 
			
		||||
{ TMyPersistent }
 | 
			
		||||
 | 
			
		||||
procedure TMyPersistent.CopyFrom(Src: TMyPersistent);
 | 
			
		||||
begin
 | 
			
		||||
 | 
			
		||||
end;
 | 
			
		||||
 | 
			
		||||
end.
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user