From f537811abc2c179cc19e97147069aceb085aca5b Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 8 Aug 2007 19:55:33 +0000 Subject: [PATCH 1/4] Fixed bug introduced in rev. 2634: When creating a new project, ${USER} ${DATE} in the standard header etc. was not replaced with values. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/2.1@2648 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Src/Internal/Templates/Project/ProjectDescriptor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs b/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs index 4875c3593f..9b5fc1c017 100644 --- a/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs +++ b/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs @@ -357,6 +357,7 @@ namespace ICSharpCode.SharpDevelop.Internal.Templates // Textual content StreamWriter sr = new StreamWriter(File.Create(fileName), ParserService.DefaultFileEncoding); string fileContent = StringParser.Parse(file.Content, new string[,] { {"ProjectName", projectCreateInformation.ProjectName}, {"FileName", fileName}}); + fileContent = StringParser.Parse(fileContent); if (SharpDevelopTextEditorProperties.IndentationString != "\t") { fileContent = fileContent.Replace("\t", SharpDevelopTextEditorProperties.IndentationString); } From 3acc3cbb3e9872ce6e1aca707767256ae24fe289 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 27 Aug 2007 12:51:27 +0000 Subject: [PATCH 2/4] Improved CSharpCodeCompletion sample: add tool tip support, show only one entry for overloaded methods git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/2.1@2673 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../CSharpCodeCompletion.csproj | 2 + .../CodeCompletionData.cs | 112 ++++++++++++++ .../CodeCompletionProvider.cs | 66 ++------ samples/CSharpCodeCompletion/MainForm.cs | 3 +- .../CSharpCodeCompletion/ToolTipProvider.cs | 146 ++++++++++++++++++ 5 files changed, 275 insertions(+), 54 deletions(-) create mode 100644 samples/CSharpCodeCompletion/CodeCompletionData.cs create mode 100644 samples/CSharpCodeCompletion/ToolTipProvider.cs diff --git a/samples/CSharpCodeCompletion/CSharpCodeCompletion.csproj b/samples/CSharpCodeCompletion/CSharpCodeCompletion.csproj index 351c176a15..50afac8c4d 100644 --- a/samples/CSharpCodeCompletion/CSharpCodeCompletion.csproj +++ b/samples/CSharpCodeCompletion/CSharpCodeCompletion.csproj @@ -51,6 +51,7 @@ + MainForm.cs @@ -62,6 +63,7 @@ + \ No newline at end of file diff --git a/samples/CSharpCodeCompletion/CodeCompletionData.cs b/samples/CSharpCodeCompletion/CodeCompletionData.cs new file mode 100644 index 0000000000..aab2d15f65 --- /dev/null +++ b/samples/CSharpCodeCompletion/CodeCompletionData.cs @@ -0,0 +1,112 @@ +/* + * Erstellt mit SharpDevelop. + * Benutzer: grunwald + * Datum: 27.08.2007 + * Zeit: 14:25 + * + * Sie können diese Vorlage unter Extras > Optionen > Codeerstellung > Standardheader ändern. + */ + +using System; +using ICSharpCode.TextEditor.Gui.CompletionWindow; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.CSharp; + +namespace CSharpEditor +{ + /// + /// Represents an item in the code completion window. + /// + class CodeCompletionData : DefaultCompletionData, ICompletionData + { + IMember member; + IClass c; + + public CodeCompletionData(IMember member) + : base(member.Name, null, GetMemberImageIndex(member)) + { + this.member = member; + } + + public CodeCompletionData(IClass c) + : base(c.Name, null, GetClassImageIndex(c)) + { + this.c = c; + } + + int overloads = 0; + + public void AddOverload() + { + overloads++; + } + + static int GetMemberImageIndex(IMember member) + { + // Missing: different icons for private/public member + if (member is IMethod) + return 1; + if (member is IProperty) + return 2; + if (member is IField) + return 3; + if (member is IEvent) + return 6; + return 3; + } + + static int GetClassImageIndex(IClass c) + { + switch (c.ClassType) { + case ClassType.Enum: + return 4; + default: + return 0; + } + } + + string description; + + // DefaultCompletionData.Description is not virtual, but we can reimplement + // the interface to get the same effect as overriding. + string ICompletionData.Description { + get { + if (description == null) { + IDecoration entity = (IDecoration)member ?? c; + description = GetCSharpText(entity); + if (overloads > 1) { + description += " (+" + overloads + " overloads)"; + } + description += Environment.NewLine + XmlDocumentationToText(entity.Documentation); + } + return description; + } + } + + /// + /// Converts a member to text. + /// Returns the declaration of the member as C# code, e.g. + /// "public void MemberName(string parameter)" + /// + static string GetCSharpText(IDecoration entity) + { + if (entity is IMethod) + return CSharpAmbience.Instance.Convert(entity as IMethod); + if (entity is IProperty) + return CSharpAmbience.Instance.Convert(entity as IProperty); + if (entity is IEvent) + return CSharpAmbience.Instance.Convert(entity as IEvent); + if (entity is IField) + return CSharpAmbience.Instance.Convert(entity as IField); + if (entity is IClass) + return CSharpAmbience.Instance.Convert(entity as IClass); + // unknown entity: + return entity.ToString(); + } + + public static string XmlDocumentationToText(string xmlDoc) + { + return xmlDoc; + } + } +} diff --git a/samples/CSharpCodeCompletion/CodeCompletionProvider.cs b/samples/CSharpCodeCompletion/CodeCompletionProvider.cs index 54f5a6b19e..b7c62ad7a9 100644 --- a/samples/CSharpCodeCompletion/CodeCompletionProvider.cs +++ b/samples/CSharpCodeCompletion/CodeCompletionProvider.cs @@ -122,6 +122,9 @@ namespace CSharpEditor void AddCompletionData(List resultList, ArrayList completionData) { + // used to store method the names for grouping overloads + Dictionary nameDictionary = new Dictionary(); + // Add the completion data as returned by SharpDevelop.Dom to the // list for the text editor foreach (object obj in completionData) { @@ -130,71 +133,28 @@ namespace CSharpEditor resultList.Add(new DefaultCompletionData((string)obj, "namespace " + obj, 5)); } else if (obj is Dom.IClass) { Dom.IClass c = (Dom.IClass)obj; - if (c.ClassType == Dom.ClassType.Enum) { - resultList.Add(new DefaultCompletionData(c.Name, - GetDescription(c), - 4)); - } else { // missing: struct, delegate etc. - resultList.Add(new DefaultCompletionData(c.Name, - GetDescription(c), - 0)); - } + resultList.Add(new CodeCompletionData(c)); } else if (obj is Dom.IMember) { Dom.IMember m = (Dom.IMember)obj; if (m is Dom.IMethod && ((m as Dom.IMethod).IsConstructor)) { // Skip constructors continue; } - // TODO: Group results by name and add "(x Overloads)" to the + // Group results by name and add "(x Overloads)" to the // description if there are multiple results with the same name. - resultList.Add(new DefaultCompletionData(m.Name, - GetDescription(m), - GetMemberImageIndex(m))); + + CodeCompletionData data; + if (nameDictionary.TryGetValue(m.Name, out data)) { + data.AddOverload(); + } else { + nameDictionary[m.Name] = data = new CodeCompletionData(m); + resultList.Add(data); + } } else { // Current ICSharpCode.SharpDevelop.Dom should never return anything else throw new NotSupportedException(); } } } - - /// - /// Converts a member to text. - /// Returns the declaration of the member as C# code, e.g. - /// "public void MemberName(string parameter)" - /// - string GetDescription(Dom.IDecoration entity) - { - return GetCSharpText(entity) + Environment.NewLine + entity.Documentation; - } - - string GetCSharpText(Dom.IDecoration entity) - { - if (entity is Dom.IMethod) - return Dom.CSharp.CSharpAmbience.Instance.Convert(entity as Dom.IMethod); - if (entity is Dom.IProperty) - return Dom.CSharp.CSharpAmbience.Instance.Convert(entity as Dom.IProperty); - if (entity is Dom.IEvent) - return Dom.CSharp.CSharpAmbience.Instance.Convert(entity as Dom.IEvent); - if (entity is Dom.IField) - return Dom.CSharp.CSharpAmbience.Instance.Convert(entity as Dom.IField); - if (entity is Dom.IClass) - return Dom.CSharp.CSharpAmbience.Instance.Convert(entity as Dom.IClass); - // unknown entity: - return entity.ToString(); - } - - int GetMemberImageIndex(Dom.IMember member) - { - // Missing: different icons for private/public member - if (member is Dom.IMethod) - return 1; - if (member is Dom.IProperty) - return 2; - if (member is Dom.IField) - return 3; - if (member is Dom.IEvent) - return 6; - return 3; - } } } diff --git a/samples/CSharpCodeCompletion/MainForm.cs b/samples/CSharpCodeCompletion/MainForm.cs index 7278c4f8b2..8d7605f47c 100644 --- a/samples/CSharpCodeCompletion/MainForm.cs +++ b/samples/CSharpCodeCompletion/MainForm.cs @@ -80,8 +80,9 @@ class MainClass "; textEditorControl1.SetHighlighting("C#"); textEditorControl1.ShowEOLMarkers = false; - CodeCompletionKeyHandler.Attach(this, textEditorControl1); HostCallbackImplementation.Register(this); + CodeCompletionKeyHandler.Attach(this, textEditorControl1); + ToolTipProvider.Attach(this, textEditorControl1); pcRegistry = new Dom.ProjectContentRegistry(); // Default .NET 2.0 registry diff --git a/samples/CSharpCodeCompletion/ToolTipProvider.cs b/samples/CSharpCodeCompletion/ToolTipProvider.cs new file mode 100644 index 0000000000..61fa8f004e --- /dev/null +++ b/samples/CSharpCodeCompletion/ToolTipProvider.cs @@ -0,0 +1,146 @@ +// CSharp Editor Example with Code Completion +// Copyright (c) 2007, Daniel Grunwald +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// - Neither the name of the ICSharpCode team nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &AS IS& AND ANY EXPRESS +// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Text; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.CSharp; +using TextEditor = ICSharpCode.TextEditor; +using NRefactoryResolver = ICSharpCode.SharpDevelop.Dom.NRefactoryResolver.NRefactoryResolver; + +namespace CSharpEditor +{ + sealed class ToolTipProvider + { + MainForm mainForm; + TextEditor.TextEditorControl editor; + CSharpExpressionFinder expressionFinder = new CSharpExpressionFinder(MainForm.DummyFileName); + + private ToolTipProvider(MainForm mainForm, TextEditor.TextEditorControl editor) + { + this.mainForm = mainForm; + this.editor = editor; + } + + public static void Attach(MainForm mainForm, TextEditor.TextEditorControl editor) + { + ToolTipProvider tp = new ToolTipProvider(mainForm, editor); + editor.ActiveTextAreaControl.TextArea.ToolTipRequest += tp.OnToolTipRequest; + } + + void OnToolTipRequest(object sender, TextEditor.ToolTipRequestEventArgs e) + { + if (e.InDocument && !e.ToolTipShown) { + // SD2 requires subtracting 1 from the offset, this is fixed in the expression + // finder in SD3. + ExpressionResult expression = expressionFinder.FindFullExpression( + editor.Text, + editor.Document.PositionToOffset(e.LogicalPosition) - 1); + + TextEditor.TextArea textArea = editor.ActiveTextAreaControl.TextArea; + NRefactoryResolver resolver = new NRefactoryResolver(mainForm.myProjectContent, mainForm.myProjectContent.Language); + ResolveResult rr = resolver.Resolve(expression, + textArea.Caret.Line, + textArea.Caret.Column, + MainForm.DummyFileName, + textArea.MotherTextEditorControl.Text); + string toolTipText = GetText(rr); + if (toolTipText != null) { + e.ShowToolTip(toolTipText); + } + } + } + + static string GetText(ResolveResult result) + { + if (result == null) { + return null; + } + if (result is MixedResolveResult) + return GetText(((MixedResolveResult)result).PrimaryResult); + IAmbience ambience = new CSharpAmbience(); + ambience.ConversionFlags = ConversionFlags.StandardConversionFlags | ConversionFlags.ShowAccessibility; + if (result is MemberResolveResult) { + return GetMemberText(ambience, ((MemberResolveResult)result).ResolvedMember); + } else if (result is LocalResolveResult) { + LocalResolveResult rr = (LocalResolveResult)result; + ambience.ConversionFlags = ConversionFlags.UseFullyQualifiedNames + | ConversionFlags.ShowReturnType + | ConversionFlags.QualifiedNamesOnlyForReturnTypes; + StringBuilder b = new StringBuilder(); + if (rr.IsParameter) + b.Append("parameter "); + else + b.Append("local variable "); + b.Append(ambience.Convert(rr.Field)); + return b.ToString(); + } else if (result is NamespaceResolveResult) { + return "namespace " + ((NamespaceResolveResult)result).Name; + } else if (result is TypeResolveResult) { + IClass c = ((TypeResolveResult)result).ResolvedClass; + if (c != null) + return GetMemberText(ambience, c); + else + return ambience.Convert(result.ResolvedType); + } else if (result is MethodResolveResult) { + MethodResolveResult mrr = result as MethodResolveResult; + IMethod m = mrr.GetMethodIfSingleOverload(); + if (m != null) + return GetMemberText(ambience, m); + else + return "Overload of " + ambience.Convert(mrr.ContainingType) + "." + mrr.Name; + } else { + return null; + } + } + + static string GetMemberText(IAmbience ambience, IDecoration member) + { + StringBuilder text = new StringBuilder(); + if (member is IField) { + text.Append(ambience.Convert(member as IField)); + } else if (member is IProperty) { + text.Append(ambience.Convert(member as IProperty)); + } else if (member is IEvent) { + text.Append(ambience.Convert(member as IEvent)); + } else if (member is IMethod) { + text.Append(ambience.Convert(member as IMethod)); + } else if (member is IClass) { + text.Append(ambience.Convert(member as IClass)); + } else { + text.Append("unknown member "); + text.Append(member.ToString()); + } + string documentation = member.Documentation; + if (documentation != null && documentation.Length > 0) { + text.Append('\n'); + text.Append(CodeCompletionData.XmlDocumentationToText(documentation)); + } + return text.ToString(); + } + } +} From 22775ffe8e0e80eebdd0284b5caf570c1cae8bbf Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 28 Aug 2007 10:30:44 +0000 Subject: [PATCH 3/4] Fixed some off-by-one bugs in the CSharpCodeCompletion example (caused by the different line counting in the parser and the text editor). git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/2.1@2674 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../CSharpCodeCompletion/CodeCompletionProvider.cs | 4 ++-- samples/CSharpCodeCompletion/MainForm.cs | 11 +++++++---- samples/CSharpCodeCompletion/ToolTipProvider.cs | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/samples/CSharpCodeCompletion/CodeCompletionProvider.cs b/samples/CSharpCodeCompletion/CodeCompletionProvider.cs index b7c62ad7a9..859834569a 100644 --- a/samples/CSharpCodeCompletion/CodeCompletionProvider.cs +++ b/samples/CSharpCodeCompletion/CodeCompletionProvider.cs @@ -94,8 +94,8 @@ namespace CSharpEditor NRefactoryResolver resolver = new NRefactoryResolver(mainForm.myProjectContent, mainForm.myProjectContent.Language); Dom.ResolveResult rr = resolver.Resolve(FindExpression(textArea), - textArea.Caret.Line, - textArea.Caret.Column, + textArea.Caret.Line + 1, + textArea.Caret.Column + 1, fileName, textArea.MotherTextEditorControl.Text); List resultList = new List(); diff --git a/samples/CSharpCodeCompletion/MainForm.cs b/samples/CSharpCodeCompletion/MainForm.cs index 8d7605f47c..85ca039abe 100644 --- a/samples/CSharpCodeCompletion/MainForm.cs +++ b/samples/CSharpCodeCompletion/MainForm.cs @@ -80,6 +80,7 @@ class MainClass "; textEditorControl1.SetHighlighting("C#"); textEditorControl1.ShowEOLMarkers = false; + textEditorControl1.ShowInvalidLines = false; HostCallbackImplementation.Register(this); CodeCompletionKeyHandler.Attach(this, textEditorControl1); ToolTipProvider.Attach(this, textEditorControl1); @@ -118,11 +119,13 @@ class MainClass "System", "System.Data", "System.Drawing", "System.Xml", "System.Windows.Forms" }; foreach (string assemblyName in referencedAssemblies) { - { // block for anonymous method - string assemblyNameCopy = assemblyName; - BeginInvoke(new MethodInvoker(delegate { parserThreadLabel.Text = "Loading " + assemblyNameCopy + "..."; })); + string assemblyNameCopy = assemblyName; // copy for anonymous method + BeginInvoke(new MethodInvoker(delegate { parserThreadLabel.Text = "Loading " + assemblyNameCopy + "..."; })); + Dom.IProjectContent referenceProjectContent = pcRegistry.GetProjectContentForReference(assemblyName, assemblyName); + myProjectContent.AddReferencedContent(referenceProjectContent); + if (referenceProjectContent is Dom.ReflectionProjectContent) { + (referenceProjectContent as Dom.ReflectionProjectContent).InitializeReferences(); } - myProjectContent.AddReferencedContent(pcRegistry.GetProjectContentForReference(assemblyName, assemblyName)); } BeginInvoke(new MethodInvoker(delegate { parserThreadLabel.Text = "Ready"; })); diff --git a/samples/CSharpCodeCompletion/ToolTipProvider.cs b/samples/CSharpCodeCompletion/ToolTipProvider.cs index 61fa8f004e..a206c1ebf5 100644 --- a/samples/CSharpCodeCompletion/ToolTipProvider.cs +++ b/samples/CSharpCodeCompletion/ToolTipProvider.cs @@ -64,8 +64,8 @@ namespace CSharpEditor TextEditor.TextArea textArea = editor.ActiveTextAreaControl.TextArea; NRefactoryResolver resolver = new NRefactoryResolver(mainForm.myProjectContent, mainForm.myProjectContent.Language); ResolveResult rr = resolver.Resolve(expression, - textArea.Caret.Line, - textArea.Caret.Column, + e.LogicalPosition.Y + 1, + e.LogicalPosition.X + 1, MainForm.DummyFileName, textArea.MotherTextEditorControl.Text); string toolTipText = GetText(rr); From c440f7c2a1e186bce6724951dcf9fe3be3155b96 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 28 Aug 2007 11:03:31 +0000 Subject: [PATCH 4/4] CSharpCodeCompletionSample: show xml documentation git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/2.1@2675 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../CodeCompletionData.cs | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/samples/CSharpCodeCompletion/CodeCompletionData.cs b/samples/CSharpCodeCompletion/CodeCompletionData.cs index aab2d15f65..8b954e9613 100644 --- a/samples/CSharpCodeCompletion/CodeCompletionData.cs +++ b/samples/CSharpCodeCompletion/CodeCompletionData.cs @@ -8,9 +8,13 @@ */ using System; -using ICSharpCode.TextEditor.Gui.CompletionWindow; +using System.IO; +using System.Text; +using System.Xml; + using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom.CSharp; +using ICSharpCode.TextEditor.Gui.CompletionWindow; namespace CSharpEditor { @@ -106,7 +110,54 @@ namespace CSharpEditor public static string XmlDocumentationToText(string xmlDoc) { - return xmlDoc; + System.Diagnostics.Debug.WriteLine(xmlDoc); + StringBuilder b = new StringBuilder(); + try { + using (XmlTextReader reader = new XmlTextReader(new StringReader("" + xmlDoc + ""))) { + reader.XmlResolver = null; + while (reader.Read()) { + switch (reader.NodeType) { + case XmlNodeType.Text: + b.Append(reader.Value); + break; + case XmlNodeType.Element: + switch (reader.Name) { + case "filterpriority": + reader.Skip(); + break; + case "returns": + b.AppendLine(); + b.Append("Returns: "); + break; + case "param": + b.AppendLine(); + b.Append(reader.GetAttribute("name") + ": "); + break; + case "remarks": + b.AppendLine(); + b.Append("Remarks: "); + break; + case "see": + if (reader.IsEmptyElement) { + b.Append(reader.GetAttribute("cref")); + } else { + reader.MoveToContent(); + if (reader.HasValue) { + b.Append(reader.Value); + } else { + b.Append(reader.GetAttribute("cref")); + } + } + break; + } + break; + } + } + } + return b.ToString(); + } catch (XmlException) { + return xmlDoc; + } } } }