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(); + } + } +}