// CSharp Editor Example with Code Completion // Copyright (c) 2006, 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.Collections; using System.Collections.Generic; using System.Windows.Forms; using ICSharpCode.TextEditor; using ICSharpCode.TextEditor.Gui.CompletionWindow; using Dom = ICSharpCode.SharpDevelop.Dom; using NRefactoryResolver = ICSharpCode.SharpDevelop.Dom.NRefactoryResolver.NRefactoryResolver; namespace HeuristicLab.CodeEditor { class CodeCompletionProvider : ICompletionDataProvider { CodeEditor codeEditor; public CodeCompletionProvider(CodeEditor codeEditor) { this.codeEditor = codeEditor; } public ImageList ImageList { get { return codeEditor.imageList1; } } public string PreSelection { get { return null; } } public int DefaultIndex { get { return -1; } } public CompletionDataProviderKeyResult ProcessKey(char key) { if (char.IsLetterOrDigit(key) || key == '_') { return CompletionDataProviderKeyResult.NormalKey; } else { // key triggers insertion of selected items return CompletionDataProviderKeyResult.InsertionKey; } } /// /// Called when entry should be inserted. Forward to the insertion action of the completion data. /// public bool InsertAction(ICompletionData data, TextArea textArea, int insertionOffset, char key) { textArea.Caret.Position = textArea.Document.OffsetToPosition(insertionOffset); return data.InsertAction(textArea, key); } public ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped) { // We can return code-completion items like this: //return new ICompletionData[] { // new DefaultCompletionData("Text", "Description", 1) //}; NRefactoryResolver resolver = new NRefactoryResolver(codeEditor.projectContent.Language); Dom.ResolveResult rr = resolver.Resolve(FindExpression(textArea), codeEditor.parseInformation, textArea.MotherTextEditorControl.Text); List resultList = new List(); if (rr != null) { try { ArrayList completionData = rr.GetCompletionData(codeEditor.projectContent); if (completionData != null) { AddCompletionData(resultList, completionData); } } catch (NullReferenceException) { } } return resultList.ToArray(); } /// /// Find the expression the cursor is at. /// Also determines the context (using statement, "new"-expression etc.) the /// cursor is at. /// Dom.ExpressionResult FindExpression(TextArea textArea) { Dom.IExpressionFinder finder; finder = new Dom.CSharp.CSharpExpressionFinder(codeEditor.parseInformation); Dom.ExpressionResult expression = finder.FindExpression(textArea.Document.TextContent, textArea.Caret.Offset); if (expression.Region.IsEmpty) { expression.Region = new Dom.DomRegion(textArea.Caret.Line + 1, textArea.Caret.Column + 1); } return expression; } void AddCompletionData(List resultList, ArrayList completionData) { // used to store the method 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) { if (obj is string) { // namespace names are returned as string resultList.Add(new DefaultCompletionData((string)obj, "namespace " + obj, 5)); } else if (obj is Dom.IClass) { Dom.IClass c = (Dom.IClass)obj; 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; } // Group results by name and add "(x Overloads)" to the // description if there are multiple results with the same name. CodeCompletionData data; if (nameDictionary.TryGetValue(m.Name, out data)) { data.AddOverload(m); } else { nameDictionary[m.Name] = data = new CodeCompletionData(m); resultList.Add(data); } } else { // Current ICSharpCode.SharpDevelop.Dom should never return anything else throw new NotSupportedException(); } } } } }