From e02c8ef2c83c7cbd175b50db267de87256ac57fd Mon Sep 17 00:00:00 2001 From: mattias Date: Mon, 22 Jul 2013 22:08:53 +0000 Subject: [PATCH] codetools: started ability to load code completion snippets from an xml files, thanks to Don Ziesig, patch #15922 git-svn-id: trunk@42177 - --- .gitattributes | 2 + .../codetools/codecompletiontemplater.pas | 432 ++++++++++++++++++ .../codetools/codecompletiontemplates.xml | 54 +++ components/codetools/codecompletiontool.pas | 251 +++++++--- components/codetools/codetoolmanager.pas | 40 +- components/codetools/codetools.lpk | 10 +- components/codetools/codetools.pas | 2 +- ide/codemacroprompt.lfm | 10 +- ide/codemacroprompt.pas | 21 +- ide/codetemplatesdlg.lfm | 56 ++- ide/codetemplatesdlg.pas | 6 +- ide/codetoolsoptions.pas | 49 +- .../codetools_classcompletion_options.lfm | 120 ++--- .../codetools_classcompletion_options.pas | 3 +- ide/frames/codetools_codecreation_options.lfm | 48 +- ide/frames/codetools_codecreation_options.pas | 54 ++- ide/lazarusidestrconsts.pas | 4 + ide/sourceeditor.lfm | 9 +- 18 files changed, 982 insertions(+), 189 deletions(-) create mode 100644 components/codetools/codecompletiontemplater.pas create mode 100644 components/codetools/codecompletiontemplates.xml diff --git a/.gitattributes b/.gitattributes index 868f79462f..56ed286f33 100644 --- a/.gitattributes +++ b/.gitattributes @@ -675,6 +675,8 @@ components/codetools/changedeclarationtool.pas svneol=native#text/plain components/codetools/codeatom.pas svneol=native#text/pascal components/codetools/codebeautifier.pas svneol=native#text/plain components/codetools/codecache.pas svneol=native#text/pascal +components/codetools/codecompletiontemplater.pas svneol=native#text/plain +components/codetools/codecompletiontemplates.xml svneol=native#text/plain components/codetools/codecompletiontool.pas svneol=native#text/pascal components/codetools/codegraph.pas svneol=native#text/plain components/codetools/codeindex.pas svneol=native#text/plain diff --git a/components/codetools/codecompletiontemplater.pas b/components/codetools/codecompletiontemplater.pas new file mode 100644 index 0000000000..2a2609a1c1 --- /dev/null +++ b/components/codetools/codecompletiontemplater.pas @@ -0,0 +1,432 @@ +{ + *************************************************************************** + * * + * 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: Donald Ziesig + + Abstract + + TTemplateExpander provides xml templates to replace the hard-coded + pascal snippets used by TCodeCompletionCodeTool. + + The xml file fragment for the Setter method: + + + * + * + * + + * + * + * + + + produces pascal: + + procedure TMyClass.SetMyVar(AValue: MyType); + begin + if AValue=MyVar then Exit; + MyVar:=AValue; + end; + +=============================================================================== + + The following xml tags are implemented: + + ... generates pascal code if string value of + the argument named "SomeBool" is "true". + + generates pascal code if string value of + the argument named "SomeBool" is not "true" + + ... must immediately follow or . + generates pascal code if the negation of + the previous tag is true. + + ... generates pascal code for zero or more + values of the string argument "SomeVar". + The values are encoded as a single string + of the form "Arg0?Arg1?...ArgN?" (Yeah, I + know. See the comments below about a hack.) + + is replaced by the appropriate Indent string. + +
is replaced by the appropriate LineEnd string. + + is replaced by the string element of ArgVal + which corresponds to the string element of + ArgName. + +} +unit CodeCompletionTemplater; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, laz2_DOM, laz2_XMLRead, LazFileUtils, LazUTF8Classes, + CodeCache, FileProcs; + +type + + { TTemplateExpander } + + TTemplateExpander = class + private + FCode: TCodeBuffer; + fCodeChangeStep: Integer; + procedure LoadCode; + procedure SetCode(AValue: TCodeBuffer); + protected + XMLDoc : TXMLDocument; + Root : TDOMNode; + + function ArgCount(Args: String): Integer; + function ArgN(Args: String; Index : Integer) : String; + + function ExpandTemplate(Template: TDOMNode; + LineEnd, Indent : String; + ArgName: array of String; + ArgVal: array of const; + CountIndex: Integer = -1): String; + + function FindTemplate(TemplateName: String): TDOMNode; + public + constructor Create; + destructor Destroy; override; + + function Expand(TemplateName : String; + LineEnd, Indent : String; + ArgName : array of String; + ArgVal : array of const): String; + + function TemplateExists(TemplateName: String): Boolean; + + property Doc: TXMLDocument read XMLDoc; + property Code: TCodeBuffer read FCode write SetCode; + procedure ReloadCode; + end; + +var + Expander : TTemplateExpander; // will be set by CodeToolBoss + +implementation + +{ TTemplateExpander } + +// +// ArgCount and ArgN are part of a hack to overcome the fact that +// "array of const" can not contain another array. +// +// instead of a convenient ['a',...,['x1,'x2'],...,'z'] we must use something +// like ['a',...,'x1?x2?',...'z'] (which is what I chose just for simplicity) +// + +function TTemplateExpander.ArgCount(Args: String): Integer; +var + I : Integer; +begin + Result := 0; + for I := 1 to Length(Args) do + if Args[I] = '?' then + Inc(Result); +end; + +function TTemplateExpander.ArgN(Args: String; Index: Integer): String; +var + I : Integer; + P : Integer; + S : String; +begin + S := Args; + for I := 0 to pred(Index) do + begin + P := Pos('?',S); + S := Copy(S,P+1,65535); + end; + P := Pos('?',S); + Result := Copy(S,1,P-1); +end; + +constructor TTemplateExpander.Create; +begin + fCodeChangeStep:=CTInvalidChangeStamp; +end; + +destructor TTemplateExpander.Destroy; +begin + FCode:=nil; + FreeAndNil(XMLDoc); +end; + +function TTemplateExpander.Expand(TemplateName: String; LineEnd, + Indent: String; ArgName: array of String; ArgVal: array of const): String; +var + Template : TDOMNode; +begin + Template := FindTemplate(TemplateName); + if Template = nil then + raise Exception.Create('Template "' + TemplateName + '" not found in TemplateExpander.'); + Result := ExpandTemplate(Template, #13#10, ' ', ArgName, ArgVal); +end; + +function TTemplateExpander.ExpandTemplate(Template: TDOMNode; LineEnd, + Indent: String; ArgName: array of String; ArgVal: array of const; + CountIndex : Integer): String; + +// Sequential search of ArgName array to return corresponding element +// of the ArgVal array (appropriately processed if it simulates being +// an array itself. +function GetArgValue(Name : String; Index : Integer = -1): String; +var + I : Integer; + S : String; +begin + for I := 0 to pred(Length(ArgName)) do + if ArgName[I] = Name then + begin + S := AnsiString(ArgVal[I].VAnsiString); + if (Index < 0) or (Pos('?',S) = 0) then + Result := S + else + Result := ArgN(S, Index); + exit; + end; + raise Exception.Create('ExpandTemplate could not find Argument named "' + Name + '"'); +end; + +function GetBoolArgValue(Name : String; Index : Integer = -1): Boolean; +var + I : Integer; +begin + for I := 0 to pred(Length(ArgName)) do + if ArgName[I] = Name then + begin + Result := ArgVal[I].VBoolean; + exit; + end; + raise Exception.Create('ExpandTemplate could not find Argument named "' + Name + '"'); +end; + +function GetNodeValue(Node : TDOMNode; Required : Boolean = True): String; +var + Len : Integer; +begin + Result := ''; + Len := Node.Attributes.Length; + if Required then + if Len = 0 then + raise Exception.Create('Missing attribute tag for node "' + Node.NodeName + '"'); + if Len > 0 then + Result := Node.Attributes.Item[0].NodeValue; +end; + +var + Node : TDOMNode; + N : String; + S : String; + Name : String; + R : String; // for debugger + PrevNode : TDOMNode; + CommentFlag : Boolean; + CountArgs : String; + NArgs : Integer; + I : Integer; +begin + R := ''; + PrevNode := nil; + Node := Template.FirstChild; + while Node <> nil do + begin + N := Node.NodeName; + S := Node.NodeValue; + CommentFlag := False; +// plain text in the xml file is copied directly to the output (almost). + if N = '#text' then + begin + if Pos(#10, S) = 1 then // Hack to work around XML parser that leaves + S := Copy(S,2,65535); // A new-line when text appears in first + R := R + S; // column of the XML file. + end +// indent the text using the string argument Indent + else if N = 'indent' then + begin + Name := GetNodeValue(Node, False); + if Name = '' then + R := R + Indent + else + R := R + GetArgValue(Name); + end +// add the line break using the string argument LineEnd + else if N = 'br' then + R := R + LineEnd +// process the xml 'if' tag + else if (N = 'if') then + begin + Name := GetNodeValue(Node); //Node.Attributes.Item[0].NodeValue; + if GetBoolArgValue(Name) then + R := R + ExpandTemplate(Node, LineEnd,Indent,ArgName,ArgVal); + end +// process the xml 'ifnot' tag + else if (N = 'ifnot') then + begin + Name := GetNodeValue(Node); //Node.Attributes.Item[0].NodeValue; + if not GetBoolArgValue(Name) then + R := R + ExpandTemplate(Node, LineEnd,Indent,ArgName,ArgVal); + end +// process the xml 'else' tag. This is sneaky. The else tag must (almost) +// immediately follow the closing of either the 'if' or 'ifnot' tags. (The +// exception allows comments to intervene) +// +// The original implementation used separate 'else' and 'elsenot' tags, but +// the xml file got so confusing at times that it was better to add some +// nasty looking code here to make the xml neater. + else if N = 'else' then + begin + if PrevNode = nil then + raise Exception.Create('Expander: "else" without "if" or "ifnot"'); + if PrevNode.NodeName = 'if' then + begin + Name := GetNodeValue(PrevNode); //PrevNode.Attributes.Item[0].NodeValue; + if GetBoolArgValue(Name) then + R := R + ExpandTemplate(Node, LineEnd,Indent,ArgName,ArgVal); + end + else if PrevNode.NodeName = 'ifnot' then + begin + Name := GetNodeValue(PrevNode); //PrevNode.Attributes.Item[0].NodeValue; + if not GetBoolArgValue(Name) then + R := R + ExpandTemplate(Node, LineEnd,Indent,ArgName,ArgVal); + end + else + raise Exception.Create('Expander: mis-placed "else" following ' + PrevNode.NodeName); + + end +// process the xml 'count' tag. This implements multiple lines to be generated +// from array or list data in the pascal code. This was originally needed to +// implement the 'AssignMethod' template. + else if N = 'count' then + begin + Name := GetNodeValue(Node); //Node.Attributes.Item[0].NodeValue; + CountArgs := GetArgValue(Name); + NArgs := ArgCount(CountArgs); + for I := 0 to pred(Nargs) do + R := R + ExpandTemplate(Node, LineEnd,Indent,ArgName,ArgVal, I); + end +// process all other xml tags (less comment) as requests for the pascal variable +// specified by the tag name: e.g., will look for an argument name +// of ClassName in the ArgNames array and get the corresponding value from the +// ArgVals array; + else if N <> '#comment' then + R := R + GetArgValue(N, CountIndex) +{$IFDEF DebugTemplate } + else + begin + R := R + '{ ' + Node.NodeValue + ' }'; + CommentFlag := True; + end; +{$ELSE DebugTemplate} + else + CommentFlag := True; +{$ENDIF DebugTemplate} +// ignore the comment nodes in subsequent processing. + if not CommentFlag then PrevNode := Node; + Node := Node.NextSibling; + end; + Result := R; +end; + +function TTemplateExpander.FindTemplate(TemplateName : String): TDOMNode; +var + N : String; +begin + if not Assigned(Root) then + begin + Result := nil; + exit; + end; + if Root.NodeName <> 'templates' then + raise Exception.Create('Root node of codetools TemplateExpander = "' + Root.NodeName + '", "templates" expected.'); + +// Sequential search of list of templates. + Result := Root.FirstChild; + while Result <> nil do + begin + N := Result.NodeName; + if N <> '#comment' then // ignores first level comments + begin + if N <> 'template' then + raise Exception.Create('template node of codetools TemplateExpander = "' + N + '", "template" expected.'); + if Result.Attributes.Item[0].NodeValue = TemplateName then + break; + end; + Result := Result.NextSibling; + end; +end; + +procedure TTemplateExpander.LoadCode; +var + ms: TMemoryStream; +begin + if Code=nil then begin + fCodeChangeStep:=CTInvalidChangeStamp; + exit; + end; + fCodeChangeStep:=Code.ChangeStep; + Root:=nil; + FreeAndNil(XMLDoc); + ms:=TMemoryStream.Create; + try + Code.SaveToStream(ms); + ms.Position:=0; + ReadXMLFile(XMLDoc, ms); + Root := XMLDoc.DocumentElement; + finally + ms.Free; + end; +end; + +procedure TTemplateExpander.SetCode(AValue: TCodeBuffer); +begin + if FCode=AValue then Exit; + FCode:=AValue; + LoadCode; +end; + +function TTemplateExpander.TemplateExists(TemplateName: String): Boolean; +begin + ReloadCode; + Result := FindTemplate(TemplateName) <> nil; +end; + +procedure TTemplateExpander.ReloadCode; +begin + if Code=nil then exit; + if Code.ChangeStep=fCodeChangeStep then exit; + LoadCode; +end; + +end. + diff --git a/components/codetools/codecompletiontemplates.xml b/components/codetools/codecompletiontemplates.xml new file mode 100644 index 0000000000..69fafb488a --- /dev/null +++ b/components/codetools/codecompletiontemplates.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + diff --git a/components/codetools/codecompletiontool.pas b/components/codetools/codecompletiontool.pas index dafc61388f..b0eee27610 100644 --- a/components/codetools/codecompletiontool.pas +++ b/components/codetools/codecompletiontool.pas @@ -77,6 +77,7 @@ interface {off $DEFINE VerboseCompleteMethod} {off $DEFINE VerboseCompleteLocalVarAssign} {off $DEFINE VerboseCompleteEventAssign} +{$DEFINE UseXMLTemplates} uses {$IFDEF MEM_CHECK} @@ -85,7 +86,7 @@ uses Classes, SysUtils, FileProcs, CodeToolsStrConsts, CodeTree, CodeAtom, CodeCache, CustomCodeTool, PascalParserTool, MethodJumpTool, FindDeclarationTool, KeywordFuncLists, CodeToolsStructs, BasicCodeTools, - LinkScanner, SourceChanger, CodeGraph, AVL_Tree; + LinkScanner, SourceChanger, CodeGraph, AVL_Tree, codecompletiontemplater; type TNewClassPart = (ncpPrivateProcs, ncpPrivateVars, @@ -137,6 +138,7 @@ type fNewMainUsesSectionUnits: TAVLTree; // tree of AnsiString procedure AddNewPropertyAccessMethodsToClassProcs(ClassProcs: TAVLTree; const TheClassName: string); + procedure SetSetPropertyVariablename(AValue: string); function UpdateProcBodySignature(ProcBodyNodes: TAVLTree; const BodyNodeExt: TCodeTreeNodeExtension; ProcAttrCopyDefToBody: TProcHeadAttributes; var ProcsCopied: boolean; @@ -363,7 +365,7 @@ type public // Options property SetPropertyVariablename: string read FSetPropertyVariablename - write FSetPropertyVariablename; + write SetSetPropertyVariablename; property CompleteProperties: boolean read FCompleteProperties write FCompleteProperties; property AddInheritedCodeToOverrideMethod: boolean @@ -456,6 +458,12 @@ begin FSourceChangeCache.MainScanner:=Scanner; end; +procedure TCodeCompletionCodeTool.SetSetPropertyVariablename(aValue: string); +begin + if FSetPropertyVariablename=aValue then Exit; + FSetPropertyVariablename:=aValue; +end; + function TCodeCompletionCodeTool.OnTopLvlIdentifierFound( Params: TFindDeclarationParams; const FoundContext: TFindContext ): TIdentifierFoundResult; @@ -1067,8 +1075,17 @@ begin +'invalid target for a var'); end; - InsertTxt:=VariableName+':'+VariableType+';'; +{$IFDEF UseXMLTemplates} + if ( Expander <> nil ) and Expander.TemplateExists('PrettyColon') then + begin + InsertTxt:=VariableName+Expander.Expand('PrettyColon','','',[],[])+VariableType+';'; + end + else +{$ENDIF} + begin + InsertTxt:=VariableName+':'+VariableType+';'; //DebugLn(['TCodeCompletionCodeTool.AddLocalVariable C InsertTxt="',InsertTxt,'" ParentNode=',ParentNode.DescAsString,' HeaderNode=',HeaderNode.DescAsString,' OtherSectionNode=',OtherSectionNode.DescAsString,' VarSectionNode=',VarSectionNode.DescAsString,' CursorNode=',CursorNode.DescAsString]); + end; if (VarSectionNode<>nil) then begin // there is already a var section @@ -2641,6 +2658,9 @@ var Params: TFindDeclarationParams; ParamName: String; // create param list without brackets + {$IFDEF UseXMLTemplates} + Colon : String; + {$ENDIF} begin Result:=''; CleanList:=''; @@ -2684,8 +2704,20 @@ begin Result:=Result+';'; CleanList:=CleanList+';'; end; - Result:=Result+ParamName+':'+ParamType; - CleanList:=CleanList+ParamType; + {$IFDEF UseXMLTemplates} + if assigned( Expander ) and Expander.TemplateExists('PrettyColon') then + begin + Colon := Expander.Expand('PrettyColon', '','', // Doesn't use linebreak or indentation + [], [] ); + Result:=Result+ParamName+Colon+ParamType; + CleanList:=CleanList+Colon+ParamType; + end + else + {$ENDIF UseXMLTemplates} + begin + Result:=Result+ParamName+':'+ParamType; + CleanList:=CleanList+':'+ParamType; + end; // next MoveCursorToCleanPos(ExprEndPos); ReadNextAtom; @@ -2806,7 +2838,20 @@ const // prepend 'procedure' keyword if IsFunction then - ProcCode:='function '+ProcCode+':'+FuncType+';' + begin + {$IFDEF UseXMLTemplates} + if ( Expander <> nil ) and Expander.TemplateExists('PrettyColon') then + begin + ProcCode:= 'function '+ProcCode+ + Expander.Expand('PrettyColon','','',[],[]) + +FuncType+';'; + end + else + {$ENDIF} + begin + ProcCode:='function '+ProcCode+':'+FuncType+';'; + end; + end else ProcCode:='procedure '+ProcCode+';'; CleanProcHead:=CleanProcHead+';'; @@ -2998,6 +3043,15 @@ begin CodeCompleteSrcChgCache:=SourceChangeCache; // check if variable already exists if not VarExistsInCodeCompleteClass(UpperCaseStr(VarName)) then begin + {$IFDEF UseXMLTemplates} + if ( Expander <> nil ) and Expander.TemplateExists('PrettyColon') then + begin + AddClassInsertion(UpperCaseStr(VarName), + VarName+Expander.Expand('PrettyColon','','',[],[])+VarType+';',VarName,ncpPublishedVars); + + end + else + {$ENDIF} AddClassInsertion(UpperCaseStr(VarName), VarName+':'+VarType+';',VarName,ncpPublishedVars); if not InsertAllNewClassParts then @@ -5803,6 +5857,9 @@ var SrcVar: String; i: Integer; Beauty: TBeautifyCodeOptions; + {$IFDEF UseXMLTemplates} + NodeExtsStr: String; + {$ENDIF} begin Result:=false; NewPos:=CleanCodeXYPosition; @@ -5812,67 +5869,114 @@ begin Beauty:=SourceChanger.BeautifyCodeOptions; aClassName:=ExtractClassName(ClassNode,false); CleanDef:=ProcName+'('+ParamType+');'; - Def:='procedure '+ProcName+'('+ParamName+':'+ParamType+');'; - if OverrideMod then Def:=Def+'override;'; + {$IFDEF UseXMLTemplates} + if assigned( Expander ) and Expander.TemplateExists('AssignMethodDef') then + begin + Def := Expander.Expand('AssignMethodDef', '','', // Doesn't use linebreak or indentation + ['ProcName', 'ParamName', 'ParamType', 'Override' ], + [ ProcName, ParamName, ParamType, OverrideMod ] ); + end else + {$ENDIF UseXMLTemplates} + begin + Def:='procedure '+ProcName+'('+ParamName+':'+ParamType+');'; + if OverrideMod then Def:=Def+'override;'; + end; SrcVar:=ParamName; // create the proc header SameType:=CompareIdentifiers(PChar(aClassName),PChar(ParamType))=0; e:=SourceChanger.BeautifyCodeOptions.LineEnd; Indent:=0; IndentStep:=SourceChanger.BeautifyCodeOptions.Indent; - ProcBody:='procedure '+aClassName+'.'+ProcName+'('+ParamName+':'+ParamType+');'+e; - if not SameType then begin - // add local variable - SrcVar:=LocalVarName; - if SrcVar='' then - SrcVar:='aSource'; - if CompareIdentifiers(PChar(SrcVar),PChar(ParamName))=0 then begin - if CompareIdentifiers(PChar(SrcVar),'aSource')=0 then - SrcVar:='aSrc' - else + {$IFDEF UseXMLTemplates} + if assigned(Expander) and Expander.TemplateExists('AssignMethod') then begin + if not SameType then begin + // add local variable + SrcVar:=LocalVarName; + if SrcVar='' then SrcVar:='aSource'; + if CompareIdentifiers(PChar(SrcVar),PChar(ParamName))=0 then begin + if CompareIdentifiers(PChar(SrcVar),'aSource')=0 then + SrcVar:='aSrc' + else + SrcVar:='aSource'; + end; + end; + // add assignments + NodeExtsStr := ''; + if MemberNodeExts<>nil then begin + for i:=0 to MemberNodeExts.Count-1 do + begin + NodeExt:=TCodeTreeNodeExtension(MemberNodeExts[i]); + NodeExtsStr := NodeExtsStr + NodeExt.Txt + '?'; + end; + end; + ProcBody := Expander.Expand( 'AssignMethod',e,GetIndentStr(Indent), + ['ClassName', 'ProcName', 'ParamName', 'ParamType', + 'SameType', 'SrcVar', 'Inherited0', 'Inherited1', + 'NodeExt' ], + [ aClassName, ProcName, ParamName, ParamType, + SameType, SrcVar, + CallInherited and (not CallInheritedOnlyInElse), + CallInherited and CallInheritedOnlyInElse, + NodeExtsStr ] ); + end + else + {$ENDIF UseXMLTemplates} + begin + ProcBody:='procedure '+aClassName+'.'+ProcName+'('+ParamName+':'+ParamType+');'+e; + if not SameType then begin + // add local variable + SrcVar:=LocalVarName; + if SrcVar='' then + SrcVar:='aSource'; + if CompareIdentifiers(PChar(SrcVar),PChar(ParamName))=0 then begin + if CompareIdentifiers(PChar(SrcVar),'aSource')=0 then + SrcVar:='aSrc' + else + SrcVar:='aSource'; + end; + ProcBody:=ProcBody+'var'+e + +Beauty.GetIndentStr(Indent+IndentStep)+SrcVar+':'+aClassName+';'+e; end; - ProcBody:=ProcBody+'var'+e - +Beauty.GetIndentStr(Indent+IndentStep)+SrcVar+':'+aClassName+';'+e; - end; - ProcBody:=ProcBody+'begin'+e; - inc(Indent,IndentStep); - - // call inherited - if CallInherited and (not CallInheritedOnlyInElse) then - ProcBody:=ProcBody - +Beauty.GetIndentStr(Indent)+'inherited '+ProcName+'('+ParamName+');'+e; - - if not SameType then begin - // add a parameter check to the new procedure - ProcBody:=ProcBody - +Beauty.GetIndentStr(Indent)+'if '+ParamName+' is '+aClassName+' then'+e - +Beauty.GetIndentStr(Indent)+'begin'+e; + ProcBody:=ProcBody+'begin'+e; inc(Indent,IndentStep); - ProcBody:=ProcBody+Beauty.GetIndentStr(Indent)+SrcVar+':='+aClassName+'('+ParamName+');'+e; - end; - // add assignments - if MemberNodeExts<>nil then begin - for i:=0 to MemberNodeExts.Count-1 do begin - NodeExt:=TCodeTreeNodeExtension(MemberNodeExts[i]); - // add assignment - ProcBody:=ProcBody+Beauty.GetIndentStr(Indent)+NodeExt.Txt+':='+SrcVar+'.'+NodeExt.Txt+';'+e; - end; - end; + // call inherited + if CallInherited and (not CallInheritedOnlyInElse) then + ProcBody:=ProcBody + +Beauty.GetIndentStr(Indent)+'inherited '+ProcName+'('+ParamName+');'+e; - if not SameType then begin - // close if block - dec(Indent,IndentStep); - if CallInherited and CallInheritedOnlyInElse then begin - ProcBody:=ProcBody+Beauty.GetIndentStr(Indent)+'end else'+e - +Beauty.GetIndentStr(Indent+IndentStep)+'inherited '+ProcName+'('+ParamName+');'+e; - end else begin - ProcBody:=ProcBody+Beauty.GetIndentStr(Indent)+'end;'+e + if not SameType then begin + // add a parameter check to the new procedure + ProcBody:=ProcBody + +Beauty.GetIndentStr(Indent)+'if '+ParamName+' is '+aClassName+' then'+e + +Beauty.GetIndentStr(Indent)+'begin'+e; + inc(Indent,IndentStep); + ProcBody:=ProcBody+Beauty.GetIndentStr(Indent)+SrcVar+':='+aClassName+'('+ParamName+');'+e; end; + + // add assignments + if MemberNodeExts<>nil then begin + for i:=0 to MemberNodeExts.Count-1 do begin + NodeExt:=TCodeTreeNodeExtension(MemberNodeExts[i]); + // add assignment + ProcBody:=ProcBody+Beauty.GetIndentStr(Indent)+NodeExt.Txt+':='+SrcVar+'.'+NodeExt.Txt+';'+e; + end; + end; + + if not SameType then begin + // close if block + dec(Indent,IndentStep); + if CallInherited and CallInheritedOnlyInElse then begin + ProcBody:=ProcBody+Beauty.GetIndentStr(Indent)+'end else'+e + +Beauty.GetIndentStr(Indent+IndentStep)+'inherited '+ProcName+'('+ParamName+');'+e; + end else begin + ProcBody:=ProcBody+Beauty.GetIndentStr(Indent)+'end;'+e + end; + end; + // close procedure body + ProcBody:=ProcBody+'end;'; end; - // close procedure body - ProcBody:=ProcBody+'end;'; if not InitClassCompletion(ClassNode,SourceChanger) then exit; ProcBody:=SourceChanger.BeautifyCodeOptions.BeautifyStatement(ProcBody,0); @@ -6851,19 +6955,33 @@ var end; } - ProcBody:= - 'procedure ' - +ExtractClassName(PropNode.Parent.Parent,false)+'.'+AccessParam - +'('+SetPropertyVariablename+':'+PropType+');' - +BeautifyCodeOpts.LineEnd - +'begin'+BeautifyCodeOpts.LineEnd - +BeautifyCodeOpts.GetIndentStr(BeautifyCodeOpts.Indent) - +'if '+VariableName+'='+SetPropertyVariablename+' then Exit;' + {$IFDEF UseXMLTemplates} + if assigned(Expander) and Expander.TemplateExists('SetterMethod') then + begin + debugln(['CompleteWriteSpecifier ', 'USING template for SetterMethod']); + ProcBody := Expander.Expand( 'SetterMethod', + BeautifyCodeOpts.LineEnd, + GetIndentStr(BeautifyCodeOpts.Indent), + ['ClassName', 'AccessParam','PropVarName', 'PropType','VarName'], + [ExtractClassName(PropNode.Parent.Parent,false), AccessParam, SetPropertyVariablename, PropType, VariableName] ); + end + else + {$ENDIF} + begin + ProcBody:= + 'procedure ' + +ExtractClassName(PropNode.Parent.Parent,false)+'.'+AccessParam + +'('+SetPropertyVariablename+':'+PropType+');' +BeautifyCodeOpts.LineEnd - +BeautifyCodeOpts.GetIndentStr(BeautifyCodeOpts.Indent) - +VariableName+':='+SetPropertyVariablename+';' - +BeautifyCodeOpts.LineEnd - +'end;'; + +'begin'+BeautifyCodeOpts.LineEnd + +BeautifyCodeOpts.GetIndentStr(BeautifyCodeOpts.Indent) + +'if '+VariableName+'='+SetPropertyVariablename+' then Exit;' + +BeautifyCodeOpts.LineEnd + +BeautifyCodeOpts.GetIndentStr(BeautifyCodeOpts.Indent) + +VariableName+':='+SetPropertyVariablename+';' + +BeautifyCodeOpts.LineEnd + +'end;'; + end; if IsClassProp then ProcBody:='class '+ProcBody; end; @@ -8921,6 +9039,5 @@ begin FAddInheritedCodeToOverrideMethod:=true; end; - end. diff --git a/components/codetools/codetoolmanager.pas b/components/codetools/codetoolmanager.pas index 502e8c94d8..3bbe4424a5 100644 --- a/components/codetools/codetoolmanager.pas +++ b/components/codetools/codetoolmanager.pas @@ -46,7 +46,7 @@ uses EventCodeTool, CodeTree, CodeAtom, SourceChanger, DefineTemplates, CodeCache, ExprEval, LinkScanner, KeywordFuncLists, FindOverloads, CodeBeautifier, FindDeclarationCache, DirectoryCacher, AVL_Tree, - PPUCodeTools, LFMTrees, DirectivesTree, + PPUCodeTools, LFMTrees, DirectivesTree, codecompletiontemplater, PascalParserTool, CodeToolsConfig, CustomCodeTool, FindDeclarationTool, IdentCompletionTool, StdCodeTools, ResourceCodeTool, CodeToolsStructs, CTUnitGraph, CodeTemplatesTool, ExtractProcTool; @@ -88,6 +88,7 @@ type FCatchExceptions: boolean; FChangeStep: integer; FCheckFilesOnDisk: boolean; + FCodeCompletionTemplateFileName: String; FCodeNodeTreeChangeStep: integer; FCompleteProperties: boolean; FCurCodeTool: TCodeTool; // current codetool @@ -141,8 +142,10 @@ type procedure SetAbortable(const AValue: boolean); procedure SetAddInheritedCodeToOverrideMethod(const AValue: boolean); procedure SetCheckFilesOnDisk(NewValue: boolean); + procedure SetCodeCompletionTemplateFileName(AValue: String); procedure SetCompleteProperties(const AValue: boolean); procedure SetIndentSize(NewValue: integer); + procedure SetSetPropertyVariablename(AValue: string); procedure SetTabWidth(const AValue: integer); procedure SetUseTabs(AValue: boolean); procedure SetVisibleEditorLines(NewValue: integer); @@ -279,7 +282,7 @@ type property IndentSize: integer read FIndentSize write SetIndentSize; property JumpCentered: boolean read FJumpCentered write SetJumpCentered; property SetPropertyVariablename: string - read FSetPropertyVariablename write FSetPropertyVariablename; + read FSetPropertyVariablename write SetSetPropertyVariablename; property VisibleEditorLines: integer read FVisibleEditorLines write SetVisibleEditorLines; property TabWidth: integer read FTabWidth write SetTabWidth; @@ -290,6 +293,10 @@ type read FAddInheritedCodeToOverrideMethod write SetAddInheritedCodeToOverrideMethod; + // code completion templates + property CodeCompletionTemplateFileName : String read FCodeCompletionTemplateFileName + write SetCodeCompletionTemplateFileName; + // source changing procedure BeginUpdate; function EndUpdate: boolean; @@ -5431,6 +5438,29 @@ begin FCurCodeTool.CheckFilesOnDisk:=NewValue; end; +procedure TCodeToolManager.SetCodeCompletionTemplateFileName(AValue: String); +var + OldValue: String; + Code: TCodeBuffer; +begin + AValue:=CleanAndExpandFilename(AValue); + if FCodeCompletionTemplateFileName=AValue then Exit; + OldValue:=FCodeCompletionTemplateFileName; + FCodeCompletionTemplateFileName:=AValue; + if CompareFilenames(FCodeCompletionTemplateFileName,OldValue)=0 then exit; + if (FCodeCompletionTemplateFileName<>'') then + Code:=LoadFile(FCodeCompletionTemplateFileName,true,false) + else + Code:=nil; + if Code<>nil then begin + if Expander=nil then + Expander:=TTemplateExpander.Create; + Expander.Code:=Code; + end else begin + FreeAndNil(Expander); + end; +end; + procedure TCodeToolManager.SetCompleteProperties(const AValue: boolean); begin if CompleteProperties=AValue then exit; @@ -5479,6 +5509,12 @@ begin FCurCodeTool.JumpCentered:=NewValue; end; +procedure TCodeToolManager.SetSetPropertyVariablename(aValue: string); +begin + if FSetPropertyVariablename=aValue then Exit; + FSetPropertyVariablename:=aValue; +end; + procedure TCodeToolManager.SetCursorBeyondEOL(NewValue: boolean); begin if NewValue=FCursorBeyondEOL then exit; diff --git a/components/codetools/codetools.lpk b/components/codetools/codetools.lpk index db217b61fa..72de191247 100644 --- a/components/codetools/codetools.lpk +++ b/components/codetools/codetools.lpk @@ -1,4 +1,4 @@ - + @@ -29,7 +29,7 @@ - + @@ -269,8 +269,12 @@ - + + + + + diff --git a/components/codetools/codetools.pas b/components/codetools/codetools.pas index 31a2bea479..54df29a56b 100644 --- a/components/codetools/codetools.pas +++ b/components/codetools/codetools.pas @@ -19,7 +19,7 @@ uses SourceChanger, SourceLog, StdCodeTools, OtherIdentifierTree, CodeToolsCfgScript, CTXMLFixFragment, CTUnitGraph, ChangeDeclarationTool, CodeToolsFPCMsgs, UnitDictionary, ctloadlaz, CTUnitGroupGraph, - LazarusPackageIntf; + CodeCompletionTemplater, LazarusPackageIntf; implementation diff --git a/ide/codemacroprompt.lfm b/ide/codemacroprompt.lfm index fb7a45b411..22c584156c 100644 --- a/ide/codemacroprompt.lfm +++ b/ide/codemacroprompt.lfm @@ -1,12 +1,10 @@ object CodeMacroPromptDlg: TCodeMacroPromptDlg - Caption = 'CodeMacroPromptDlg' - ClientHeight = 300 - ClientWidth = 400 - PixelsPerInch = 112 - HorzScrollBar.Page = 399 - VertScrollBar.Page = 299 Left = 290 Height = 300 Top = 163 Width = 400 + HorzScrollBar.Page = 399 + VertScrollBar.Page = 299 + Caption = 'CodeMacroPromptDlg' + LCLVersion = '1.1' end diff --git a/ide/codemacroprompt.pas b/ide/codemacroprompt.pas index ced39bc902..1d9592a81b 100644 --- a/ide/codemacroprompt.pas +++ b/ide/codemacroprompt.pas @@ -32,7 +32,7 @@ interface uses Classes, SysUtils, LCLProc, Forms, Controls, Graphics, Dialogs, - BasicCodeTools, + BasicCodeTools, CodeToolManager, SynEditAutoComplete, SynPluginTemplateEdit, SynPluginSyncronizedEditBase, SynEdit, MacroIntf, LazIDEIntf, SrcEditorIntf; @@ -114,6 +114,10 @@ implementation {$R *.lfm} + + + + const MaxLevel = 10; // prevent cycling @@ -463,12 +467,12 @@ var Pattern: String; LineText: String; Parser: TLazTemplateParser; + CodeToolBossOriginalIndent : Integer; // So I don't break anything else (hopefully) begin Result:=false; //debugln('ExecuteCodeTemplate ',dbgsName(SrcEdit),' ',dbgsName(SrcEdit.EditorControl)); AEditor:=SrcEdit.EditorControl as TSynEdit; Pattern:=TemplateValue; - Parser := TLazTemplateParser.Create(Pattern); AEditor.BeginUpdate; try @@ -498,8 +502,17 @@ begin Parser.EnableMacros := Attributes.IndexOfName(CodeTemplateEnableMacros)>=0; Parser.KeepSubIndent := Attributes.IndexOfName(CodeTemplateKeepSubIndent)>=0; Parser.Indent := BaseIndent; - LazarusIDE.SaveSourceEditorChangesToCodeCache(nil); - if not Parser.SubstituteCodeMacros(SrcEdit) then exit; + CodeToolBossOriginalIndent := CodeToolBoss.IndentSize; + if Parser.KeepSubIndent then + CodeToolBoss.IndentSize := BaseIndent // Use additional indentation + else + CodeToolBoss.IndentSize := 0; // Use current indentation + try + LazarusIDE.SaveSourceEditorChangesToCodeCache(nil); + if not Parser.SubstituteCodeMacros(SrcEdit) then exit; + finally + CodeToolBoss.IndentSize := CodeToolBossOriginalIndent; + end; s:=AEditor.Lines[p.y-1]; if TokenStartX>length(s) then diff --git a/ide/codetemplatesdlg.lfm b/ide/codetemplatesdlg.lfm index b838a14d0c..9311c0c0fa 100644 --- a/ide/codetemplatesdlg.lfm +++ b/ide/codetemplatesdlg.lfm @@ -1,7 +1,7 @@ object CodeTemplateDialog: TCodeTemplateDialog Left = 332 Height = 542 - Top = 224 + Top = 174 Width = 497 ActiveControl = FilenameEdit BorderIcons = [biSystemMenu] @@ -11,7 +11,7 @@ object CodeTemplateDialog: TCodeTemplateDialog OnClose = FormClose OnCreate = FormCreate Position = poScreenCenter - LCLVersion = '0.9.31' + LCLVersion = '1.1' object FilenameGroupBox: TGroupBox Left = 6 Height = 51 @@ -20,13 +20,13 @@ object CodeTemplateDialog: TCodeTemplateDialog Align = alTop BorderSpacing.Around = 6 Caption = 'FilenameGroupBox' - ClientHeight = 33 + ClientHeight = 31 ClientWidth = 481 TabOrder = 0 object FilenameEdit: TEdit AnchorSideRight.Control = FilenameButton Left = 9 - Height = 21 + Height = 26 Top = 4 Width = 435 Anchors = [akTop, akLeft, akRight] @@ -41,7 +41,7 @@ object CodeTemplateDialog: TCodeTemplateDialog AnchorSideBottom.Side = asrBottom Left = 449 Height = 23 - Top = 2 + Top = 7 Width = 26 Anchors = [akRight, akBottom] BorderSpacing.Right = 6 @@ -58,7 +58,7 @@ object CodeTemplateDialog: TCodeTemplateDialog Align = alTop BorderSpacing.Around = 6 Caption = 'TemplatesGroupBox' - ClientHeight = 149 + ClientHeight = 147 ClientWidth = 481 TabOrder = 1 object TemplateListBox: TListBox @@ -68,7 +68,7 @@ object CodeTemplateDialog: TCodeTemplateDialog AnchorSideBottom.Control = TemplatesGroupBox AnchorSideBottom.Side = asrBottom Left = 6 - Height = 137 + Height = 135 Top = 6 Width = 315 Anchors = [akTop, akLeft, akRight, akBottom] @@ -78,7 +78,9 @@ object CodeTemplateDialog: TCodeTemplateDialog BorderSpacing.Bottom = 6 ItemHeight = 0 OnSelectionChange = TemplateListBoxSelectionChange + ScrollWidth = 311 TabOrder = 0 + TopIndex = -1 end object AddButton: TButton AnchorSideTop.Control = TemplateListBox @@ -132,20 +134,20 @@ object CodeTemplateDialog: TCodeTemplateDialog AnchorSideRight.Control = Owner AnchorSideBottom.Control = FilenameGroupBox Left = 6 - Height = 268 + Height = 262 Top = 236 Width = 485 Align = alClient BorderSpacing.Around = 6 Caption = 'EditTemplateGroupBox' - ClientHeight = 250 + ClientHeight = 242 ClientWidth = 481 TabOrder = 2 object UseMacrosCheckBox: TCheckBox Left = 6 - Height = 17 + Height = 22 Top = 6 - Width = 117 + Width = 171 BorderSpacing.Bottom = 7 Caption = 'UseMacrosCheckBox' ParentShowHint = False @@ -158,10 +160,10 @@ object CodeTemplateDialog: TCodeTemplateDialog AnchorSideTop.Control = UseMacrosCheckBox AnchorSideTop.Side = asrCenter AnchorSideRight.Side = asrBottom - Left = 149 - Height = 23 + Left = 203 + Height = 28 Top = 3 - Width = 116 + Width = 144 AutoSize = True BorderSpacing.Left = 20 BorderSpacing.Around = 6 @@ -177,8 +179,8 @@ object CodeTemplateDialog: TCodeTemplateDialog AnchorSideBottom.Control = EditTemplateGroupBox AnchorSideBottom.Side = asrBottom Left = 6 - Height = 146 - Top = 98 + Height = 137 + Top = 99 Width = 469 BorderSpacing.Around = 6 Anchors = [akTop, akLeft, akRight, akBottom] @@ -604,6 +606,12 @@ object CodeTemplateDialog: TCodeTemplateDialog 'TemplateSynEdit' ) VisibleSpecialChars = [vscSpace, vscTabAtLast] + SelectedColor.BackPriority = 50 + SelectedColor.ForePriority = 50 + SelectedColor.FramePriority = 50 + SelectedColor.BoldPriority = 50 + SelectedColor.ItalicPriority = 50 + SelectedColor.UnderlinePriority = 50 BracketHighlightStyle = sbhsBoth BracketMatchColor.Background = clNone BracketMatchColor.Foreground = clNone @@ -639,6 +647,8 @@ object CodeTemplateDialog: TCodeTemplateDialog object TSynGutterSeparator Width = 2 MouseActions = <> + MarkupInfo.Background = clWhite + MarkupInfo.Foreground = clGray end object TSynGutterCodeFolding MouseActions = < @@ -700,8 +710,8 @@ object CodeTemplateDialog: TCodeTemplateDialog AnchorSideRight.Control = EditTemplateGroupBox AnchorSideRight.Side = asrBottom Left = 6 - Height = 39 - Top = 53 + Height = 30 + Top = 63 Width = 469 Anchors = [akTop, akLeft, akRight] AutoFill = True @@ -725,9 +735,9 @@ object CodeTemplateDialog: TCodeTemplateDialog AnchorSideTop.Control = UseMacrosCheckBox AnchorSideTop.Side = asrBottom Left = 6 - Height = 17 - Top = 30 - Width = 139 + Height = 22 + Top = 35 + Width = 202 BorderSpacing.Top = 6 Caption = 'KeepSubIndentCheckBox' ParentShowHint = False @@ -737,8 +747,8 @@ object CodeTemplateDialog: TCodeTemplateDialog end object ButtonPanel: TButtonPanel Left = 6 - Height = 26 - Top = 510 + Height = 32 + Top = 504 Width = 485 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True diff --git a/ide/codetemplatesdlg.pas b/ide/codetemplatesdlg.pas index 522bfe4261..f1fabede44 100644 --- a/ide/codetemplatesdlg.pas +++ b/ide/codetemplatesdlg.pas @@ -502,6 +502,7 @@ var CaretXY: TPoint; p: integer; i: Integer; + Indent : String; begin List:=TStringList.Create; try @@ -529,9 +530,10 @@ begin exit; end; + Indent := StringOfChar(' ',CodeToolBoss.IndentSize); Value:=''; for i:=0 to List.Count-1 do - Value:=Value+List[i]+': ;'+LineEnding; + Value:=Value+ Indent + List[i]+': ;'+LineEnding; finally List.Free; end; @@ -1230,7 +1232,7 @@ begin TemplateSynEdit.Lines.BeginUpdate; TemplateSynEdit.Lines.Clear; - //debugln('TCodeTemplateDialog.ShowCurCodeTemplate A a=',dbgs(a)); + // debugln('TCodeTemplateDialog.ShowCurCodeTemplate A a=',dbgs(a)); if a >= 0 then begin EditTemplateGroupBox.Caption:=dbgstr(SynAutoComplete.Completions[a]) diff --git a/ide/codetoolsoptions.pas b/ide/codetoolsoptions.pas index 48e29228d0..250e28e3e1 100644 --- a/ide/codetoolsoptions.pas +++ b/ide/codetoolsoptions.pas @@ -38,10 +38,12 @@ uses Classes, SysUtils, LazConf, FileUtil, Laz2_XMLCfg, lazutf8classes, LResources, Forms, Controls, Buttons, LclProc, ExtCtrls, Dialogs, CodeToolManager, DefineTemplates, SourceChanger, SynEdit, - IDEOptionsIntf, IDEOptionDefs, LazarusIDEStrConsts, IDEProcs; + IDEOptionsIntf, MacroIntf, IDEOptionDefs, LazarusIDEStrConsts, IDEProcs; const DefaultIndentationFilename = 'laz_indentation.pas'; // in directory GetPrimaryConfigPath + DefaultCodeCompletionFilename = + '$(LazarusDir)'+PathDelim+'components'+PathDelim+'codetools'+PathDelim+'codecompletiontemplates.xml'; type @@ -106,7 +108,12 @@ type fIndentationFilename: String; FIndentContextSensitive: boolean; + // code completion templates + FCodeCompletionTemplateFileName : String; + + procedure SetCodeCompletionTemplateFileName(aValue: String); procedure SetFilename(const AValue: string); + procedure SetSetPropertyVariablename(aValue: string); public class function GetGroupCaption:string; override; class function GetInstance: TAbstractIDEOptions; override; @@ -194,7 +201,7 @@ type property PrivateVariablePrefix: string read FPrivateVariablePrefix write FPrivateVariablePrefix; property SetPropertyVariablename: string - read FSetPropertyVariablename write FSetPropertyVariablename; + read FSetPropertyVariablename write SetSetPropertyVariablename; property UsesInsertPolicy: TUsesInsertPolicy read FUsesInsertPolicy write FUsesInsertPolicy; @@ -221,6 +228,10 @@ type read fIndentationFileName write fIndentationFileName; property IndentContextSensitive: boolean read FIndentContextSensitive write FIndentContextSensitive; + + // code completion templates + property CodeCompletionTemplateFileName : String read FCodeCompletionTemplateFileName + write SetCodeCompletionTemplateFileName; end; var @@ -482,6 +493,11 @@ begin FIndentContextSensitive := XMLConfig.GetValue('CodeToolsOptions/Indentation/ContextSensitive',true); + // code completion templates + FCodeCompletionTemplateFileName := + XMLConfig.GetValue('CodeToolsOptions/CodeCompletionTemplate/FileName' + , DefaultCodeCompletionFilename); + XMLConfig.Free; except on E: Exception do begin @@ -619,6 +635,10 @@ begin XMLConfig.SetDeleteValue('CodeToolsOptions/Indentation/ContextSensitive' , FIndentContextSensitive, true); + // code completion templates + XMLConfig.SetDeleteValue('CodeToolsOptions/CodeCompletionTemplate/FileName' + , FCodeCompletionTemplateFileName, DefaultCodeCompletionFilename); + XMLConfig.Flush; XMLConfig.Free; except @@ -628,6 +648,13 @@ begin end; end; +procedure TCodeToolsOptions.SetCodeCompletionTemplateFileName(aValue: String); +begin + aValue:=TrimFilename(aValue); + if FCodeCompletionTemplateFileName=aValue then Exit; + FCodeCompletionTemplateFileName:=aValue; +end; + procedure TCodeToolsOptions.SetFilename(const AValue: string); begin FFilename:=AValue; @@ -641,11 +668,18 @@ begin GetPrimaryConfigPath+'/'+DefaultCodeToolsOptsFile); CopySecondaryConfigFile(DefaultCodeToolsOptsFile); if (not FileExistsCached(ConfFileName)) then begin + debugln('Looking for code tools config file: "' + ConfFileName + '"'); debugln(UTF8ToConsole(lisCompilerNOTECodetoolsConfigFileNotFoundUsingDefaults)); end; FFilename:=ConfFilename; end; +procedure TCodeToolsOptions.SetSetPropertyVariablename(aValue: string); +begin + if FSetPropertyVariablename=aValue then Exit; + FSetPropertyVariablename:=aValue; +end; + procedure TCodeToolsOptions.Assign(Source: TPersistent); var CodeToolsOpts: TCodeToolsOptions absolute Source; @@ -770,6 +804,9 @@ begin fIndentationFilename:= TrimFilename(GetPrimaryConfigPath+PathDelim+DefaultIndentationFilename); FIndentContextSensitive:=true; + + // code completion templates + fCodeCompletionTemplateFileName := DefaultCodeCompletionFilename; end; procedure TCodeToolsOptions.ClearGlobalDefineTemplates; @@ -882,6 +919,7 @@ procedure TCodeToolsOptions.AssignTo(Dest: TPersistent); var Boss: TCodeToolManager absolute Dest; Beauty: TBeautifyCodeOptions absolute Dest; + aFilename: String; begin if Dest is TCodeToolManager then begin @@ -895,6 +933,13 @@ begin // CreateCode - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AssignTo(Boss.SourceChangeCache.BeautifyCodeOptions); Boss.SetPropertyVariablename:=SetPropertyVariablename; + // + aFilename:=CodeCompletionTemplateFileName; + IDEMacros.SubstituteMacros(aFilename); + aFilename:=TrimFilename(aFilename); + if (aFilename<>'') and not FilenameIsAbsolute(aFilename) then + aFilename:=TrimFilename(AppendPathDelim(GetPrimaryConfigPath)+aFilename); + Boss.CodeCompletionTemplateFileName:=aFilename; end else if Dest is TBeautifyCodeOptions then diff --git a/ide/frames/codetools_classcompletion_options.lfm b/ide/frames/codetools_classcompletion_options.lfm index 4402f2f57a..dda49a1312 100644 --- a/ide/frames/codetools_classcompletion_options.lfm +++ b/ide/frames/codetools_classcompletion_options.lfm @@ -1,10 +1,10 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFrame Left = 0 - Height = 455 + Height = 431 Top = 0 - Width = 572 - ClientHeight = 455 - ClientWidth = 572 + Width = 575 + ClientHeight = 431 + ClientWidth = 575 TabOrder = 0 Visible = False DesignLeft = 336 @@ -41,7 +41,7 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra Left = 256 Height = 100 Top = 0 - Width = 316 + Width = 319 Anchors = [akTop, akLeft, akRight] AutoFill = True AutoSize = True @@ -63,9 +63,9 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra AnchorSideTop.Control = ClassPartInsertPolicyRadioGroup AnchorSideTop.Side = asrBottom Left = 6 - Height = 18 + Height = 24 Top = 106 - Width = 249 + Width = 227 BorderSpacing.Around = 6 Caption = 'MixMethodsAndPropertiesCheckBox' TabOrder = 2 @@ -75,9 +75,9 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra AnchorSideTop.Control = UpdateAllMethodSignaturesCheckBox AnchorSideTop.Side = asrBottom Left = 6 - Height = 18 - Top = 154 - Width = 228 + Height = 24 + Top = 166 + Width = 204 BorderSpacing.Around = 6 Caption = 'ClassHeaderCommentsCheckBox' TabOrder = 3 @@ -90,23 +90,23 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra AnchorSideRight.Side = asrBottom AnchorSideBottom.Side = asrBottom Left = 0 - Height = 168 - Top = 202 - Width = 572 + Height = 184 + Top = 226 + Width = 575 Anchors = [akTop, akLeft, akRight] AutoSize = True BorderSpacing.Top = 6 Caption = 'PropertyCompletionGroupBox' - ClientHeight = 146 - ClientWidth = 564 + ClientHeight = 167 + ClientWidth = 571 TabOrder = 4 object PropertyCompletionCheckBox: TCheckBox AnchorSideLeft.Control = PropertyCompletionGroupBox AnchorSideTop.Control = PropertyCompletionGroupBox Left = 6 - Height = 18 + Height = 24 Top = 6 - Width = 208 + Width = 192 BorderSpacing.Around = 6 Caption = 'PropertyCompletionCheckBox' TabOrder = 0 @@ -118,24 +118,24 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra AnchorSideRight.Control = PropertyCompletionGroupBox AnchorSideRight.Side = asrBottom Left = 6 - Height = 110 - Top = 30 - Width = 552 + Height = 125 + Top = 36 + Width = 559 Anchors = [akTop, akLeft, akRight] AutoSize = True BorderSpacing.Around = 6 BevelOuter = bvNone ChildSizing.Layout = cclTopToBottomThenLeftToRight ChildSizing.ControlsPerLine = 5 - ClientHeight = 110 - ClientWidth = 552 + ClientHeight = 125 + ClientWidth = 559 TabOrder = 1 object SetPropertyVariablenameLabel: TLabel AnchorSideTop.Side = asrCenter Left = 11 - Height = 16 - Top = 3 - Width = 191 + Height = 15 + Top = 5 + Width = 171 BorderSpacing.Right = 6 BorderSpacing.CellAlignHorizontal = ccaRightBottom BorderSpacing.CellAlignVertical = ccaCenter @@ -144,10 +144,10 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra end object PrivateVariablePrefixLabel: TLabel AnchorSideTop.Side = asrCenter - Left = 39 - Height = 16 - Top = 25 - Width = 163 + Left = 36 + Height = 15 + Top = 30 + Width = 146 BorderSpacing.Right = 6 BorderSpacing.CellAlignHorizontal = ccaRightBottom BorderSpacing.CellAlignVertical = ccaCenter @@ -157,9 +157,9 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra object PropertyStoredIdentPostfixLabel: TLabel AnchorSideTop.Side = asrCenter Left = 0 - Height = 16 - Top = 47 - Width = 202 + Height = 15 + Top = 55 + Width = 182 BorderSpacing.Right = 6 BorderSpacing.CellAlignHorizontal = ccaRightBottom BorderSpacing.CellAlignVertical = ccaCenter @@ -168,10 +168,10 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra end object PropertyWriteIdentPrefixLabel: TLabel AnchorSideTop.Side = asrCenter - Left = 15 - Height = 16 - Top = 69 - Width = 187 + Left = 11 + Height = 15 + Top = 80 + Width = 171 BorderSpacing.Right = 6 BorderSpacing.CellAlignHorizontal = ccaRightBottom BorderSpacing.CellAlignVertical = ccaCenter @@ -180,10 +180,10 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra end object PropertyReadIdentPrefixLabel: TLabel AnchorSideTop.Side = asrCenter - Left = 17 - Height = 16 - Top = 91 - Width = 185 + Left = 14 + Height = 15 + Top = 105 + Width = 168 BorderSpacing.Right = 6 BorderSpacing.CellAlignHorizontal = ccaRightBottom BorderSpacing.CellAlignVertical = ccaCenter @@ -193,8 +193,8 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra object SetPropertyVariablenameEdit: TEdit AnchorSideTop.Side = asrBottom AnchorSideRight.Side = asrBottom - Left = 208 - Height = 22 + Left = 188 + Height = 25 Top = 0 Width = 80 TabOrder = 0 @@ -203,9 +203,9 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra object PrivateVariablePrefixEdit: TEdit AnchorSideTop.Side = asrBottom AnchorSideRight.Side = asrBottom - Left = 208 - Height = 22 - Top = 22 + Left = 188 + Height = 25 + Top = 25 Width = 80 TabOrder = 1 Text = 'PrivateVariablePrefixEdit' @@ -213,9 +213,9 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra object PropertyStoredIdentPostfixEdit: TEdit AnchorSideTop.Side = asrBottom AnchorSideRight.Side = asrBottom - Left = 208 - Height = 22 - Top = 44 + Left = 188 + Height = 25 + Top = 50 Width = 80 TabOrder = 2 Text = 'PropertyStoredIdentPostfixEdit' @@ -223,9 +223,9 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra object PropertyWriteIdentPrefixEdit: TEdit AnchorSideTop.Side = asrBottom AnchorSideRight.Side = asrBottom - Left = 208 - Height = 22 - Top = 66 + Left = 188 + Height = 25 + Top = 75 Width = 80 TabOrder = 3 Text = 'PropertyWriteIdentPrefixEdit' @@ -233,9 +233,9 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra object PropertyReadIdentPrefixEdit: TEdit AnchorSideTop.Side = asrBottom AnchorSideRight.Side = asrBottom - Left = 208 - Height = 22 - Top = 88 + Left = 188 + Height = 25 + Top = 100 Width = 80 TabOrder = 4 Text = 'PropertyReadIdentPrefixEdit' @@ -247,9 +247,9 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra AnchorSideTop.Control = ClassHeaderCommentsCheckBox AnchorSideTop.Side = asrBottom Left = 6 - Height = 18 - Top = 178 - Width = 283 + Height = 24 + Top = 196 + Width = 251 BorderSpacing.Around = 6 Caption = 'ClassImplementationCommentsCheckBox' TabOrder = 5 @@ -259,9 +259,9 @@ object CodetoolsClassCompletionOptionsFrame: TCodetoolsClassCompletionOptionsFra AnchorSideTop.Control = MixMethodsAndPropertiesCheckBox AnchorSideTop.Side = asrBottom Left = 6 - Height = 18 - Top = 130 - Width = 257 + Height = 24 + Top = 136 + Width = 235 BorderSpacing.Around = 6 Caption = 'UpdateAllMethodSignaturesCheckBox' TabOrder = 6 diff --git a/ide/frames/codetools_classcompletion_options.pas b/ide/frames/codetools_classcompletion_options.pas index 78325e8f54..f03b9b72ab 100644 --- a/ide/frames/codetools_classcompletion_options.pas +++ b/ide/frames/codetools_classcompletion_options.pas @@ -25,7 +25,7 @@ unit codetools_classcompletion_options; interface uses - Classes, SysUtils, FileUtil, Forms, ExtCtrls, StdCtrls, + Classes, SysUtils, FileUtil, Forms, ExtCtrls, StdCtrls, Buttons, Dialogs, SourceChanger, CodeToolsOptions, LazarusIDEStrConsts, IDEOptionsIntf; type @@ -53,7 +53,6 @@ type SetPropertyVariablenameLabel: TLabel; UpdateAllMethodSignaturesCheckBox: TCheckBox; private - { private declarations } public function GetTitle: String; override; procedure Setup(ADialog: TAbstractOptionsEditorDialog); override; diff --git a/ide/frames/codetools_codecreation_options.lfm b/ide/frames/codetools_codecreation_options.lfm index 40369b70f9..886fad09f2 100644 --- a/ide/frames/codetools_codecreation_options.lfm +++ b/ide/frames/codetools_codecreation_options.lfm @@ -39,7 +39,7 @@ object CodetoolsCodeCreationOptionsFrame: TCodetoolsCodeCreationOptionsFrame Left = 6 Height = 24 Top = 112 - Width = 261 + Width = 217 BorderSpacing.Around = 6 Caption = 'ForwardProcsKeepOrderCheckBox' TabOrder = 1 @@ -78,7 +78,7 @@ object CodetoolsCodeCreationOptionsFrame: TCodetoolsCodeCreationOptionsFrame Left = 6 Height = 24 Top = 254 - Width = 279 + Width = 231 BorderSpacing.Left = 6 BorderSpacing.Top = 6 Caption = 'UpdateMultiProcSignaturesCheckBox' @@ -91,9 +91,51 @@ object CodetoolsCodeCreationOptionsFrame: TCodetoolsCodeCreationOptionsFrame Left = 6 Height = 24 Top = 278 - Width = 315 + Width = 261 BorderSpacing.Left = 6 Caption = 'UpdateOtherProcSignaturesCaseCheckBox' TabOrder = 4 end + object TemplateFileLabel: TLabel + AnchorSideLeft.Control = UpdateOtherProcSignaturesCaseCheckBox + AnchorSideTop.Control = TemplateFileEdit + AnchorSideTop.Side = asrCenter + Left = 6 + Height = 15 + Top = 307 + Width = 100 + Caption = 'TemplateFileLabel' + ParentColor = False + end + object TemplateFileEdit: TEdit + AnchorSideLeft.Control = TemplateFileLabel + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = UpdateOtherProcSignaturesCaseCheckBox + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = TemplateFileBrowseButton + Left = 106 + Height = 25 + Top = 302 + Width = 439 + Anchors = [akTop, akLeft, akRight] + TabOrder = 5 + Text = 'TemplateFileEdit' + end + object TemplateFileBrowseButton: TButton + AnchorSideTop.Control = TemplateFileEdit + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = TemplateFileEdit + AnchorSideBottom.Side = asrBottom + Left = 545 + Height = 25 + Top = 302 + Width = 21 + Anchors = [akTop, akRight, akBottom] + AutoSize = True + BorderSpacing.Right = 6 + Caption = '...' + OnClick = TemplateFileBrowseButtonClick + TabOrder = 6 + end end diff --git a/ide/frames/codetools_codecreation_options.pas b/ide/frames/codetools_codecreation_options.pas index 627405c6f1..4a97e45af3 100644 --- a/ide/frames/codetools_codecreation_options.pas +++ b/ide/frames/codetools_codecreation_options.pas @@ -25,8 +25,9 @@ unit codetools_codecreation_options; interface uses - Classes, SysUtils, FileUtil, Forms, ExtCtrls, StdCtrls, - SourceChanger, CodeToolsOptions, LazarusIDEStrConsts, IDEOptionsIntf; + Classes, SysUtils, FileUtil, Forms, ExtCtrls, StdCtrls, Dialogs, + SourceChanger, CodeToolsOptions, LazarusIDEStrConsts, IDEOptionsIntf, + IDEDialogs; type @@ -35,9 +36,13 @@ type TCodetoolsCodeCreationOptionsFrame = class(TAbstractIDEOptionsEditor) ForwardProcsInsertPolicyRadioGroup: TRadioGroup; ForwardProcsKeepOrderCheckBox: TCheckBox; + TemplateFileBrowseButton: TButton; + TemplateFileEdit: TEdit; + TemplateFileLabel: TLabel; UpdateMultiProcSignaturesCheckBox: TCheckBox; UpdateOtherProcSignaturesCaseCheckBox: TCheckBox; UsesInsertPolicyRadioGroup: TRadioGroup; + procedure TemplateFileBrowseButtonClick(Sender: TObject); private public function GetTitle: String; override; @@ -53,6 +58,26 @@ implementation { TCodetoolsCodeCreationOptionsFrame } +procedure TCodetoolsCodeCreationOptionsFrame.TemplateFileBrowseButtonClick( + Sender: TObject); +var + OpenDialog: TOpenDialog; +begin + OpenDialog:=TOpenDialog.Create(nil); + try + InitIDEFileDialog(OpenDialog); + OpenDialog.Title:=lisChooseAFileWithCodeToolsTemplates; + OpenDialog.Options:=OpenDialog.Options+[ofFileMustExist]; + OpenDialog.Filter:=lisCodetoolsTemplateFile+' (*.xml)|*.xml|'+dlgAllFiles+ + '|'+GetAllFilesMask; + if OpenDialog.Execute then + TemplateFileEdit.Text:=OpenDialog.FileName; + finally + StoreIDEFileDialog(OpenDialog); + OpenDialog.Free; + end; +end; + function TCodetoolsCodeCreationOptionsFrame.GetTitle: String; begin Result := dlgCodeCreation; @@ -73,10 +98,6 @@ begin end; ForwardProcsKeepOrderCheckBox.Caption:=dlgForwardProcsKeepOrder; - UpdateMultiProcSignaturesCheckBox.Caption:= - lisCTOUpdateMultipleProcedureSignatures; - UpdateOtherProcSignaturesCaseCheckBox.Caption:= - lisUpdateOtherProcedureSignaturesWhenOnlyLetterCaseHa; with UsesInsertPolicyRadioGroup do begin Caption:=lisNewUnitsAreAddedToUsesSections; @@ -90,6 +111,13 @@ begin EndUpdate; end; end; + + UpdateMultiProcSignaturesCheckBox.Caption:= + lisCTOUpdateMultipleProcedureSignatures; + UpdateOtherProcSignaturesCaseCheckBox.Caption:= + lisUpdateOtherProcedureSignaturesWhenOnlyLetterCaseHa; + + TemplateFileLabel.Caption:=lisTemplateFile; end; procedure TCodetoolsCodeCreationOptionsFrame.ReadSettings( @@ -106,8 +134,6 @@ begin end; ForwardProcsKeepOrderCheckBox.Checked := KeepForwardProcOrder; - UpdateMultiProcSignaturesCheckBox.Checked:=UpdateMultiProcSignatures; - UpdateOtherProcSignaturesCaseCheckBox.Checked:=UpdateOtherProcSignaturesCase; case UsesInsertPolicy of uipFirst: UsesInsertPolicyRadioGroup.ItemIndex:=0; @@ -118,6 +144,11 @@ begin //uipAlphabetically: UsesInsertPolicyRadioGroup.ItemIndex:=4; end; + + UpdateMultiProcSignaturesCheckBox.Checked:=UpdateMultiProcSignatures; + UpdateOtherProcSignaturesCaseCheckBox.Checked:=UpdateOtherProcSignaturesCase; + + TemplateFileEdit.Text:=CodeCompletionTemplateFileName; end; end; @@ -133,8 +164,6 @@ begin end; KeepForwardProcOrder := ForwardProcsKeepOrderCheckBox.Checked; - UpdateMultiProcSignatures:=UpdateMultiProcSignaturesCheckBox.Checked; - UpdateOtherProcSignaturesCase:=UpdateOtherProcSignaturesCaseCheckBox.Checked; case UsesInsertPolicyRadioGroup.ItemIndex of 0: UsesInsertPolicy:=uipFirst; @@ -143,6 +172,11 @@ begin 3: UsesInsertPolicy:=uipLast; else UsesInsertPolicy:=uipAlphabetically; end; + + UpdateMultiProcSignatures:=UpdateMultiProcSignaturesCheckBox.Checked; + UpdateOtherProcSignaturesCase:=UpdateOtherProcSignaturesCaseCheckBox.Checked; + + CodeCompletionTemplateFileName:=TemplateFileEdit.Text; end; end; diff --git a/ide/lazarusidestrconsts.pas b/ide/lazarusidestrconsts.pas index 4068b06f66..1e05d5d9d5 100644 --- a/ide/lazarusidestrconsts.pas +++ b/ide/lazarusidestrconsts.pas @@ -1346,6 +1346,7 @@ resourcestring dlgDelTemplate = 'Delete template '; dlgChsCodeTempl = 'Choose code template file (*.dci)'; dlgAllFiles = 'All files'; + lisCodetoolsTemplateFile = 'Codetools template file'; lisExecutable = 'Executable'; lisEditorFileTypes = 'Editor file types'; lisPkgMgrNew = 'new'; @@ -1994,6 +1995,8 @@ resourcestring dlgCOCfgCmpMessages = 'Messages'; lisChooseAnFPCMessageFile = 'Choose an FPC message file'; lisFPCMessageFile = 'FPC message file'; + lisChooseAFileWithCodeToolsTemplates = 'Choose a file with CodeTools ' + +'templates'; dlgCOOther = 'Other'; dlgCOInherited = 'Inherited'; dlgCOCompilerCommands = 'Compiler Commands'; @@ -5796,6 +5799,7 @@ resourcestring +'signatures'; lisUpdateOtherProcedureSignaturesWhenOnlyLetterCaseHa = 'Update other ' +'procedure signatures when only letter case has changed'; + lisTemplateFile = 'Template file'; implementation diff --git a/ide/sourceeditor.lfm b/ide/sourceeditor.lfm index e8324f96c6..ade39eed9f 100644 --- a/ide/sourceeditor.lfm +++ b/ide/sourceeditor.lfm @@ -1,4 +1,4 @@ -inherited SourceNotebook: TSourceNotebook +object SourceNotebook: TSourceNotebook Left = 533 Height = 300 Top = 374 @@ -8,10 +8,11 @@ inherited SourceNotebook: TSourceNotebook ClientHeight = 300 ClientWidth = 400 OnMouseUp = FormMouseUp - object StatusBar: TStatusBar[0] + LCLVersion = '1.1' + object StatusBar: TStatusBar Left = 0 - Height = 23 - Top = 277 + Height = 21 + Top = 279 Width = 400 Panels = < item