diff options
author | Mike Krüger <mkrueger@novell.com> | 2011-05-17 17:36:46 +0400 |
---|---|---|
committer | Mike Krüger <mkrueger@novell.com> | 2011-05-17 17:36:46 +0400 |
commit | 7b2f670351021970022eda95632292de634b5bd7 (patch) | |
tree | 2733e96641b95c74b2121420ce2056db7d6759e2 /main | |
parent | 3f525e0f841bc315baec05f02dec21134224d875 (diff) |
Fixed 'Bug 693860 - Refactoring and templates do not honour formatting
rules'.
Diffstat (limited to 'main')
12 files changed, 194 insertions, 55 deletions
diff --git a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/CSharpTokenNode.cs b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/CSharpTokenNode.cs index a776f259f5..8ca4618b8a 100644 --- a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/CSharpTokenNode.cs +++ b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/CSharpTokenNode.cs @@ -27,7 +27,7 @@ using System; namespace ICSharpCode.NRefactory.CSharp { - public class CSharpTokenNode : AstNode + public class CSharpTokenNode : AstNode, IRelocationable { public static new readonly CSharpTokenNode Null = new NullCSharpTokenNode (); class NullCSharpTokenNode : CSharpTokenNode @@ -80,6 +80,13 @@ namespace ICSharpCode.NRefactory.CSharp this.tokenLength = tokenLength; } + #region IRelocationable implementation + void IRelocationable.SetStartLocation (AstLocation startLocation) + { + this.startLocation = startLocation; + } + #endregion + public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data) { return visitor.VisitCSharpTokenNode (this, data); diff --git a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Expressions/EmptyExpression.cs b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Expressions/EmptyExpression.cs index b057acbdf6..e73e451660 100644 --- a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Expressions/EmptyExpression.cs +++ b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Expressions/EmptyExpression.cs @@ -30,7 +30,7 @@ namespace ICSharpCode.NRefactory.CSharp /// <summary> /// Type<[EMPTY]> /// </summary> - public class EmptyExpression : Expression + public class EmptyExpression : Expression, IRelocationable { AstLocation location; @@ -39,7 +39,14 @@ namespace ICSharpCode.NRefactory.CSharp return location; } } - + + #region IRelocationable implementation + void IRelocationable.SetStartLocation (AstLocation startLocation) + { + this.location = startLocation; + } + #endregion + public override AstLocation EndLocation { get { return location; diff --git a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Comment.cs b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Comment.cs index 4f757b2640..d347af7a9a 100644 --- a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Comment.cs +++ b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Comment.cs @@ -32,7 +32,7 @@ namespace ICSharpCode.NRefactory.CSharp Documentation } - public class Comment : AstNode + public class Comment : AstNode, IRelocationable { public override NodeType NodeType { get { @@ -81,6 +81,15 @@ namespace ICSharpCode.NRefactory.CSharp this.startLocation = startLocation; this.endLocation = endLocation; } + + #region IRelocationable implementation + void IRelocationable.SetStartLocation (AstLocation startLocation) + { + int lineDelta = startLocation.Line - this.startLocation.Line; + endLocation = new AstLocation (endLocation.Line + lineDelta, lineDelta != 0 ? endLocation.Column : endLocation.Column + startLocation.Column - this.startLocation.Column); + this.startLocation = startLocation; + } + #endregion public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data) { diff --git a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/IRelocationable.cs b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/IRelocationable.cs new file mode 100644 index 0000000000..54a239d5d4 --- /dev/null +++ b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/IRelocationable.cs @@ -0,0 +1,35 @@ +// +// IRelocationable.cs +// +// Author: +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + public interface IRelocationable + { + void SetStartLocation (AstLocation startLocation); + } +} + diff --git a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs index 70f1ddca48..64ece2245a 100644 --- a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs +++ b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs @@ -28,7 +28,7 @@ using System; namespace ICSharpCode.NRefactory.CSharp { - public class Identifier : AstNode + public class Identifier : AstNode, IRelocationable { public static readonly new Identifier Null = new NullIdentifier (); class NullIdentifier : Identifier @@ -79,7 +79,15 @@ namespace ICSharpCode.NRefactory.CSharp get { return startLocation; } + + } + + #region IRelocationable implementation + void IRelocationable.SetStartLocation (AstLocation startLocation) + { + this.startLocation = startLocation; } + #endregion public override AstLocation EndLocation { get { diff --git a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs index 3ee53863e5..a0ff3c68cf 100644 --- a/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs +++ b/main/contrib/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.NRefactory.CSharp /// <summary> /// ; /// </summary> - public class EmptyStatement : Statement + public class EmptyStatement : Statement, IRelocationable { public AstLocation Location { get; @@ -48,6 +48,13 @@ namespace ICSharpCode.NRefactory.CSharp } } + #region IRelocationable implementation + void IRelocationable.SetStartLocation (AstLocation startLocation) + { + this.Location = startLocation; + } + #endregion + public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data) { return visitor.VisitEmptyStatement (this, data); diff --git a/main/contrib/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs b/main/contrib/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs index 8c8b8466e3..09a7c05015 100644 --- a/main/contrib/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs +++ b/main/contrib/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs @@ -2929,22 +2929,32 @@ namespace ICSharpCode.NRefactory.CSharp } } - public CompilationUnit Parse (TextReader reader) + public CompilationUnit Parse (TextReader reader, int line = 0) { // TODO: can we optimize this to avoid the text->stream->text roundtrip? using (MemoryStream stream = new MemoryStream ()) { - StreamWriter w = new StreamWriter(stream, Encoding.UTF8); + StreamWriter w = new StreamWriter (stream, Encoding.UTF8); char[] buffer = new char[2048]; int read; while ((read = reader.ReadBlock(buffer, 0, buffer.Length)) > 0) - w.Write(buffer, 0, read); - w.Flush(); // we can't close the StreamWriter because that would also close the MemoryStream + w.Write (buffer, 0, read); + w.Flush (); // we can't close the StreamWriter because that would also close the MemoryStream stream.Position = 0; - return Parse(stream); + return Parse (stream, line); + } + } + + public static void AdjustLineLocations (AstNode node, int line) + { + if (node is IRelocationable) { + ((IRelocationable)node).SetStartLocation (new AstLocation (node.StartLocation.Line + line, node.StartLocation.Column)); + } + foreach (var child in node.Children) { + AdjustLineLocations (child, line); } } - public CompilationUnit Parse (Stream stream) + public CompilationUnit Parse (Stream stream, int line) { lock (CompilerCallableEntryPoint.parseLock) { CompilerCompilationUnit top = CompilerCallableEntryPoint.ParseFile (new string[] { "-v", "-unsafe"}, stream, "parsed.cs", errorReportPrinter); @@ -2953,24 +2963,33 @@ namespace ICSharpCode.NRefactory.CSharp CSharpParser.ConversionVisitor conversionVisitor = new ConversionVisitor (top.LocationsBag); top.UsingsBag.Global.Accept (conversionVisitor); InsertComments (top, conversionVisitor); + if (line != 0) + AdjustLineLocations (conversionVisitor.Unit, line); return conversionVisitor.Unit; } } + + public CompilationUnit Parse (Stream stream) + { + return Parse (stream, 1); + } - public IEnumerable<AttributedNode> ParseTypeMembers(TextReader reader) + public IEnumerable<AttributedNode> ParseTypeMembers (TextReader reader, int lineModifier = 0) { - string code = "unsafe partial class MyClass { " + reader.ReadToEnd() + "}"; - var cu = Parse(new StringReader(code)); - var td = cu.Children.FirstOrDefault() as TypeDeclaration; + string code = "unsafe partial class MyClass { " + Environment.NewLine + reader.ReadToEnd () + "}"; + var cu = Parse (new StringReader (code), -1 + lineModifier); + if (cu == null) + return Enumerable.Empty<AttributedNode> (); + var td = cu.Children.FirstOrDefault () as TypeDeclaration; if (td != null) return td.Members; return Enumerable.Empty<AttributedNode> (); } - public IEnumerable<Statement> ParseStatements(TextReader reader) + public IEnumerable<Statement> ParseStatements(TextReader reader, int lineModifier = 0) { - string code = "void M() { " + reader.ReadToEnd() + "}"; - var members = ParseTypeMembers(new StringReader(code)); + string code = "void M() { " + Environment.NewLine + reader.ReadToEnd() + "}"; + var members = ParseTypeMembers(new StringReader(code), lineModifier - 1); var method = members.FirstOrDefault() as MethodDeclaration; if (method != null && method.Body != null) return method.Body.Statements; @@ -2990,7 +3009,7 @@ namespace ICSharpCode.NRefactory.CSharp public AstNode ParseExpression(TextReader reader) { - var es = ParseStatements(new StringReader("tmp = " + reader.ReadToEnd() + ";")).FirstOrDefault() as ExpressionStatement; + var es = ParseStatements(new StringReader("tmp = " + Environment.NewLine + reader.ReadToEnd() + ";"), -1).FirstOrDefault() as ExpressionStatement; if (es != null) { AssignmentExpression ae = es.Expression as AssignmentExpression; if (ae != null) diff --git a/main/contrib/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/main/contrib/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 89c1e443eb..ec25b2127f 100644 --- a/main/contrib/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/main/contrib/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -357,6 +357,7 @@ <Compile Include="CSharp\Formatter\CSharpFormattingOptions.cs" /> <Compile Include="CSharp\Ast\Expressions\AnonymousTypeCreateExpression.cs" /> <Compile Include="CSharp\Ast\Expressions\UndocumentedExpression.cs" /> + <Compile Include="CSharp\Ast\IRelocationable.cs" /> </ItemGroup> <ItemGroup> <Folder Include="CSharp\" /> diff --git a/main/contrib/ICSharpCode.NRefactory/Makefile.am b/main/contrib/ICSharpCode.NRefactory/Makefile.am index ade046eb13..1168b8b90b 100644 --- a/main/contrib/ICSharpCode.NRefactory/Makefile.am +++ b/main/contrib/ICSharpCode.NRefactory/Makefile.am @@ -73,6 +73,7 @@ FILES = \ CSharp/Ast/GeneralScope/UsingDeclaration.cs \ CSharp/Ast/IAstVisitor.cs \ CSharp/Ast/Identifier.cs \ + CSharp/Ast/IRelocationable.cs \ CSharp/Ast/MemberType.cs \ CSharp/Ast/Modifiers.cs \ CSharp/Ast/NodeType.cs \ diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormatter.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormatter.cs index 57bbc2ac0c..3149404d73 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormatter.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormatter.cs @@ -94,8 +94,11 @@ namespace MonoDevelop.CSharp.Formatting TextEditorData data, int startOffset, int endOffset) { var parser = new CSharpParser (); - var compilationUnit = parser.Parse (data); - + var compilationUnit = parser.ParseSnippet (data); + if (compilationUnit == null) { + Console.WriteLine ("couldn't parse : " + data.Text); + return; + } var policy = policyParent.Get<CSharpFormattingPolicy> (mimeTypeChain); var adapter = new TextEditorDataAdapter (data); @@ -104,9 +107,7 @@ namespace MonoDevelop.CSharp.Formatting }; compilationUnit.AcceptVisitor (formattingVisitor, null); - var changes = new List<ICSharpCode.NRefactory.Change> (); - changes.AddRange (formattingVisitor.Changes. Where (c => (startOffset <= c.Offset && c.Offset < endOffset))); @@ -162,7 +163,7 @@ namespace MonoDevelop.CSharp.Formatting end += c.InsertedText.Length; } - /* System.Console.WriteLine ("-----"); + /* System.Console.WriteLine ("-----"); System.Console.WriteLine (data.Text.Replace (" ", "^").Replace ("\t", "->")); System.Console.WriteLine ("-----");*/ string result = data.GetTextBetween (startOffset, Math.Min (data.Length, end)); diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Resolver/HelperMethods.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Resolver/HelperMethods.cs index f75f2dd157..dd50236a8f 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Resolver/HelperMethods.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Resolver/HelperMethods.cs @@ -49,6 +49,7 @@ using ICSharpCode.OldNRefactory; using MonoDevelop.CSharp.Parser; using MonoDevelop.CSharp.Completion; using Mono.TextEditor; +using ICSharpCode.NRefactory.CSharp; namespace MonoDevelop.CSharp { @@ -71,5 +72,25 @@ namespace MonoDevelop.CSharp return parser.Parse (stream); } } + + public static AstNode ParseSnippet (this ICSharpCode.NRefactory.CSharp.CSharpParser parser, TextEditorData data) + { + using (var stream = new StreamReader (data.OpenStream ())) { + var result = parser.ParseExpression (stream); + if (!parser.HasErrors) + return result; + } + parser.ErrorPrinter.Reset (); + using (var stream = new StreamReader (data.OpenStream ())) { + var result = parser.ParseStatements (stream); + if (!parser.HasErrors) + return result.First (); + } + parser.ErrorPrinter.Reset (); + + using (var stream = data.OpenStream ()) { + return parser.Parse (stream); + } + } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs index 5ac5fc4bb6..e17cc730d5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs @@ -37,6 +37,7 @@ using MonoDevelop.Projects.Dom; using MonoDevelop.Projects.Dom.Parser; using Mono.TextEditor; using Mono.TextEditor.PopupWindow; +using MonoDevelop.Ide.CodeFormatting; namespace MonoDevelop.Ide.CodeTemplates { @@ -145,15 +146,6 @@ namespace MonoDevelop.Ide.CodeTemplates return start; } -// static string GetLeadingWhiteSpace (MonoDevelop.Ide.Gui.TextEditor editor, int lineNr) -// { -// string lineText = editor.GetLineText (lineNr); -// int index = 0; -// while (index < lineText.Length && Char.IsWhiteSpace (lineText[index])) -// index++; -// return index > 0 ? lineText.Substring (0, index) : ""; -// } - static Regex variableRegEx = new Regex ("\\$([^$]*)\\$", RegexOptions.Compiled); public List<string> ParseVariables (string code) @@ -210,10 +202,9 @@ namespace MonoDevelop.Ide.CodeTemplates StringBuilder sb = new StringBuilder (); int lastOffset = 0; string code = context.Document.Editor.FormatString (context.InsertPosition, context.TemplateCode); - result.TextLinks = new List<TextLink> (); foreach (Match match in variableRegEx.Matches (code)) { - string name = match.Groups[1].Value; + string name = match.Groups [1].Value; sb.Append (code.Substring (lastOffset, match.Index - lastOffset)); lastOffset = match.Index + match.Length; if (string.IsNullOrEmpty (name)) { // $$ is interpreted as $ @@ -232,30 +223,30 @@ namespace MonoDevelop.Ide.CodeTemplates TextLink link = result.TextLinks.Find (l => l.Name == name); bool isNew = link == null; if (isNew) { - link = new TextLink (name); - if (!string.IsNullOrEmpty (variableDecarations[name].ToolTip)) - link.Tooltip = GettextCatalog.GetString (variableDecarations[name].ToolTip); - link.Values = new CodeTemplateListDataProvider (variableDecarations[name].Values); - if (!string.IsNullOrEmpty (variableDecarations[name].Function)) { - link.Values = expansion.RunFunction (context, null, variableDecarations[name].Function); + link = new TextLink (name); + if (!string.IsNullOrEmpty (variableDecarations [name].ToolTip)) + link.Tooltip = GettextCatalog.GetString (variableDecarations [name].ToolTip); + link.Values = new CodeTemplateListDataProvider (variableDecarations [name].Values); + if (!string.IsNullOrEmpty (variableDecarations [name].Function)) { + link.Values = expansion.RunFunction (context, null, variableDecarations [name].Function); } result.TextLinks.Add (link); } - link.IsEditable = variableDecarations[name].IsEditable; - link.IsIdentifier = variableDecarations[name].IsIdentifier; - if (!string.IsNullOrEmpty (variableDecarations[name].Function)) { - IListDataProvider<string> functionResult = expansion.RunFunction (context, null, variableDecarations[name].Function); + link.IsEditable = variableDecarations [name].IsEditable; + link.IsIdentifier = variableDecarations [name].IsIdentifier; + if (!string.IsNullOrEmpty (variableDecarations [name].Function)) { + IListDataProvider<string > functionResult = expansion.RunFunction (context, null, variableDecarations [name].Function); if (functionResult != null && functionResult.Count > 0) { - string s = (string)functionResult[functionResult.Count - 1]; + string s = (string)functionResult [functionResult.Count - 1]; if (s == null) { if (variableDecarations.ContainsKey (name)) - s = variableDecarations[name].Default; + s = variableDecarations [name].Default; } if (s != null) { link.AddLink (new Segment (sb.Length, s.Length)); if (isNew) { link.GetStringFunc = delegate (Func<string, string> callback) { - return expansion.RunFunction (context, callback, variableDecarations[name].Function); + return expansion.RunFunction (context, callback, variableDecarations [name].Function); }; } sb.Append (s); @@ -263,12 +254,36 @@ namespace MonoDevelop.Ide.CodeTemplates } else { AddDefaultValue (sb, link, name); } - } else { + } else { AddDefaultValue (sb, link, name); } } sb.Append (code.Substring (lastOffset, code.Length - lastOffset)); - result.Code = sb.ToString (); + + // format & indent template code + TextEditorData data = new TextEditorData (); + data.Text = sb.ToString (); + data.Document.TextReplaced += delegate(object sender, ReplaceEventArgs e) { + int delta = -e.Count + (e.Value != null ? e.Value.Length : 0); + foreach (var link in result.TextLinks) { + foreach (var segment in link.Links) { + if (segment.Offset > e.Offset) { + segment.Offset += delta; + } + } + } + if (result.CaretEndOffset > e.Offset) + result.CaretEndOffset += delta; + }; + + var formatter = CodeFormatterService.GetFormatter (context.Document.Editor.Document.MimeType); + if (formatter != null && context.Document.HasProject) { + formatter.OnTheFlyFormat (context.Document.Project.Policies, data, 0, data.Length); + } + + IndentCode (data, context.LineIndent); + result.Code = data.Text; + data.Dispose (); return result; } @@ -301,7 +316,15 @@ namespace MonoDevelop.Ide.CodeTemplates } return result.ToString (); } - + + static void IndentCode (TextEditorData data, string lineIndent) + { + for (int i = 1; i < data.LineCount; i++) { + var line = data.GetLine (i + 1); + if (line.EditableLength > 0) + data.Insert (line.Offset, lineIndent); + } + } string GetIndent (StringBuilder sb) { @@ -373,7 +396,7 @@ namespace MonoDevelop.Ide.CodeTemplates ParsedDocument = doc, InsertPosition = data.Caret.Location, LineIndent = data.Document.GetLineIndent (data.Caret.Line), - TemplateCode = IndentCode (Code, document.Editor.EolMarker, data.Document.GetLineIndent (data.Caret.Line)) + TemplateCode = Code }; if (data.IsSomethingSelected) { @@ -405,7 +428,7 @@ namespace MonoDevelop.Ide.CodeTemplates document.Editor.Caret.Offset = offset + template.Code.Length; } - if (PropertyService.Get ("OnTheFlyFormatting", false)) { +/* if (PropertyService.Get ("OnTheFlyFormatting", false)) { string mt = DesktopService.GetMimeTypeForUri (document.FileName); var formatter = MonoDevelop.Ide.CodeFormatting.CodeFormatterService.GetFormatter (mt); if (formatter != null && formatter.SupportsOnTheFlyFormatting) { @@ -414,7 +437,7 @@ namespace MonoDevelop.Ide.CodeTemplates document.Editor, offset, offset + length); document.Editor.Document.EndAtomicUndo (); } - } + }*/ return template; } |