diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs index a1e194657a..9852a5c8c4 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs @@ -18,12 +18,12 @@ using System; using System.Collections.Generic; -using System.ComponentModel.Design; +using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; +using CSharpBinding.Completion; using CSharpBinding.FormattingStrategy; using CSharpBinding.Refactoring; -using ICSharpCode.AvalonEdit.Highlighting; -using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.Core; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; @@ -43,6 +43,13 @@ namespace CSharpBinding this.container.AddService(typeof(CodeGenerator), new CSharpCodeGenerator()); this.container.AddService(typeof(System.CodeDom.Compiler.CodeDomProvider), new Microsoft.CSharp.CSharpCodeProvider()); } + + public override ICodeCompletionBinding CreateCompletionBinding(FileName fileName, TextLocation currentLocation, ICSharpCode.NRefactory.Editor.ITextSource fileContent) + { + if (fileName == null) + throw new ArgumentNullException("fileName"); + return new CSharpCompletionBinding(fileName, currentLocation, fileContent); + } } public class CSharpTextEditorExtension : ITextEditorExtension diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs index 153462befe..1b3828e211 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs @@ -20,10 +20,14 @@ using System; using System.Collections.Generic; using System.Linq; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.Completion; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Completion; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.CodeCompletion; @@ -32,6 +36,22 @@ namespace CSharpBinding.Completion { public class CSharpCompletionBinding : ICodeCompletionBinding { + FileName contextFileName; + TextLocation currentLocation; + ITextSource fileContent; + + public CSharpCompletionBinding() + : this(null, TextLocation.Empty, null) + { + } + + public CSharpCompletionBinding(FileName contextFileName, TextLocation currentLocation, ITextSource fileContent) + { + this.contextFileName = contextFileName; + this.currentLocation = currentLocation; + this.fileContent = fileContent; + } + public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { // We use HandleKeyPressed instead. @@ -52,36 +72,49 @@ namespace CSharpBinding.Completion bool ShowCompletion(ITextEditor editor, char completionChar, bool ctrlSpace) { - var completionContext = CSharpCompletionContext.Get(editor); + CSharpCompletionContext completionContext; + if (fileContent == null) { + completionContext = CSharpCompletionContext.Get(editor); + } else { + completionContext = CSharpCompletionContext.Get(editor, fileContent, currentLocation, contextFileName); + } if (completionContext == null) return false; + int caretOffset; + if (fileContent == null) { + caretOffset = editor.Caret.Offset; + currentLocation = editor.Caret.Location; + } else { + caretOffset = completionContext.Document.GetOffset(currentLocation); + } + var completionFactory = new CSharpCompletionDataFactory(completionContext, new CSharpResolver(completionContext.TypeResolveContextAtCaret)); + CSharpCompletionEngine cce = new CSharpCompletionEngine( - editor.Document, + completionContext.Document, completionContext.CompletionContextProvider, completionFactory, completionContext.ProjectContent, completionContext.TypeResolveContextAtCaret ); - cce.FormattingPolicy = FormattingOptionsFactory.CreateSharpDevelop(); - cce.EolMarker = DocumentUtilities.GetLineTerminator(editor.Document, editor.Caret.Line); - cce.IndentString = editor.Options.IndentationString; + cce.EolMarker = DocumentUtilities.GetLineTerminator(completionContext.Document, currentLocation.Line); + cce.IndentString = editor.Options.IndentationString; int startPos, triggerWordLength; IEnumerable completionData; if (ctrlSpace) { - if (!cce.TryGetCompletionWord(editor.Caret.Offset, out startPos, out triggerWordLength)) { - startPos = editor.Caret.Offset; + if (!cce.TryGetCompletionWord(caretOffset, out startPos, out triggerWordLength)) { + startPos = caretOffset; triggerWordLength = 0; } completionData = cce.GetCompletionData(startPos, true); completionData = completionData.Concat(cce.GetImportCompletionData(startPos)); } else { - startPos = editor.Caret.Offset; + startPos = caretOffset; if (char.IsLetterOrDigit (completionChar) || completionChar == '_') { - if (startPos > 1 && char.IsLetterOrDigit (editor.Document.GetCharAt (startPos - 2))) + if (startPos > 1 && char.IsLetterOrDigit (completionContext.Document.GetCharAt (startPos - 2))) return false; completionData = cce.GetCompletionData(startPos, false); startPos--; @@ -96,8 +129,8 @@ namespace CSharpBinding.Completion list.Items.AddRange(FilterAndAddTemplates(editor, completionData.Cast().ToList())); if (list.Items.Count > 0) { list.SortItems(); - list.PreselectionLength = editor.Caret.Offset - startPos; - list.PostselectionLength = Math.Max(0, startPos + triggerWordLength - editor.Caret.Offset); + list.PreselectionLength = caretOffset - startPos; + list.PostselectionLength = Math.Max(0, startPos + triggerWordLength - caretOffset); list.SuggestedItem = list.Items.FirstOrDefault(i => i.Text == cce.DefaultCompletionString); editor.ShowCompletionWindow(list); return true; @@ -106,13 +139,13 @@ namespace CSharpBinding.Completion if (!ctrlSpace) { // Method Insight var pce = new CSharpParameterCompletionEngine( - editor.Document, + completionContext.Document, completionContext.CompletionContextProvider, completionFactory, completionContext.ProjectContent, completionContext.TypeResolveContextAtCaret ); - var newInsight = pce.GetParameterDataProvider(editor.Caret.Offset, completionChar) as CSharpMethodInsight; + var newInsight = pce.GetParameterDataProvider(caretOffset, completionChar) as CSharpMethodInsight; if (newInsight != null && newInsight.items.Count > 0) { newInsight.UpdateHighlightedParameter(pce); newInsight.Show(); diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionContext.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionContext.cs index 6a838913de..7d45f6f541 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionContext.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionContext.cs @@ -18,6 +18,10 @@ using System; using System.Diagnostics; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.SharpDevelop.Project; using CSharpBinding.Parser; using ICSharpCode.NRefactory.CSharp.Completion; using ICSharpCode.NRefactory.CSharp.TypeSystem; @@ -30,6 +34,7 @@ namespace CSharpBinding.Completion sealed class CSharpCompletionContext { public readonly ITextEditor Editor; + public readonly IDocument Document; public readonly CSharpFullParseInformation ParseInformation; public readonly ICompilation Compilation; public readonly IProjectContent ProjectContent; @@ -51,21 +56,40 @@ namespace CSharpBinding.Completion if (projectContent == null) return null; - return new CSharpCompletionContext(editor, parseInfo, compilation, projectContent); + return new CSharpCompletionContext(editor, parseInfo, compilation, projectContent, editor.Document, editor.Caret.Location); } - private CSharpCompletionContext(ITextEditor editor, CSharpFullParseInformation parseInfo, ICompilation compilation, IProjectContent projectContent) + public static CSharpCompletionContext Get(ITextEditor editor, ITextSource fileContent, TextLocation currentLocation, FileName fileName) + { + IDocument document = new ReadOnlyDocument(fileContent); + + // Don't require the very latest parse information, an older cached version is OK. + var parseInfo = SD.ParserService.Parse(fileName, document) as CSharpFullParseInformation; + if (parseInfo == null) + return null; + + ICompilation compilation = SD.ParserService.GetCompilationForFile(fileName); + var projectContent = compilation.MainAssembly.UnresolvedAssembly as IProjectContent; + if (projectContent == null) + return null; + + return new CSharpCompletionContext(editor, parseInfo, compilation, projectContent, document, currentLocation); + } + + private CSharpCompletionContext(ITextEditor editor, CSharpFullParseInformation parseInfo, ICompilation compilation, IProjectContent projectContent, IDocument document, TextLocation caretLocation) { Debug.Assert(editor != null); Debug.Assert(parseInfo != null); Debug.Assert(compilation != null); Debug.Assert(projectContent != null); + Debug.Assert(document != null); this.Editor = editor; + this.Document = document; this.ParseInformation = parseInfo; this.Compilation = compilation; this.ProjectContent = projectContent; - this.TypeResolveContextAtCaret = parseInfo.UnresolvedFile.GetTypeResolveContext(compilation, editor.Caret.Location); - this.CompletionContextProvider = new DefaultCompletionContextProvider(editor.Document, parseInfo.UnresolvedFile); + this.TypeResolveContextAtCaret = parseInfo.UnresolvedFile.GetTypeResolveContext(compilation, caretLocation); + this.CompletionContextProvider = new DefaultCompletionContextProvider(document, parseInfo.UnresolvedFile); } } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj index ee8d5f9e33..b954bdac24 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj +++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj @@ -112,6 +112,7 @@ Code + diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/AutoCompleteTextBox.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/AutoCompleteTextBox.cs index 59734d05a7..5ed0b5a703 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/AutoCompleteTextBox.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/AutoCompleteTextBox.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -24,6 +25,13 @@ using System.Windows.Media; using System.Windows.Threading; using ICSharpCode.AvalonEdit; using ICSharpCode.AvalonEdit.Editing; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Completion; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.CodeCompletion; @@ -113,33 +121,14 @@ namespace Debugger.AddIn.Pads.Controls { StackFrame frame = WindowsDebugger.CurrentStackFrame; if (e.Text == "." && frame != null) - ShowDotCompletion(frame, this.editor.Text); + ShowDotCompletion(frame); } - private void ShowDotCompletion(StackFrame frame, string currentText) + void ShowDotCompletion(StackFrame frame) { - string language = ProjectService.CurrentProject == null ? "C#" : ProjectService.CurrentProject.Language; - #warning reimplement this! -// NRefactoryResolver resolver = new NRefactoryResolver(LanguageProperties.GetLanguage(language)); -// -// var seg = frame.NextStatement; -// -// var expressionFinder = ParserService.GetExpressionFinder(seg.Filename); -// var info = ParserService.GetParseInformation(seg.Filename); -// -// string text = ParserService.GetParseableFileContent(seg.Filename).Text; -// -// int currentOffset = this.editor.CaretOffset; -// -// var expr = expressionFinder.FindExpression(currentText, currentOffset); -// -// expr.Region = new DomRegion(seg.StartLine, seg.StartColumn, seg.EndLine, seg.EndColumn); -// -// var rr = resolver.Resolve(expr, info, text); -// -// if (rr != null) { -// editorAdapter.ShowCompletionWindow(new DotCodeCompletionItemProvider().GenerateCompletionListForResolveResult(rr, expr.Context)); -// } + var binding = DebuggerDotCompletion.PrepareDotCompletion(editor.Text.Substring(0, editor.CaretOffset), frame); + if (binding == null) return; + binding.HandleKeyPressed(editorAdapter, '.'); } public void FocusEditor() diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerDotCompletion.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerDotCompletion.cs new file mode 100644 index 0000000000..bf18d95f76 --- /dev/null +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerDotCompletion.cs @@ -0,0 +1,128 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// 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; +using System.Text; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; + +namespace Debugger.AddIn.Pads.Controls +{ + static class DebuggerDotCompletion + { + public static ICodeCompletionBinding PrepareDotCompletion(string expressionToComplete, StackFrame context) + { + var seq = context.NextStatement; + var fileName = new FileName(seq.Filename); + var lang = SD.LanguageService.GetLanguageByFileName(fileName); + var currentLocation = new TextLocation(seq.StartLine, seq.StartColumn); + if (lang == null) + return null; + string content = GeneratePartialClassContextStub(fileName, currentLocation, context); + const string caretPoint = "$__Caret_Point__$;"; + int caretOffset = content.IndexOf(caretPoint, StringComparison.Ordinal) + expressionToComplete.Length; + var doc = new ReadOnlyDocument(content.Replace(caretPoint, expressionToComplete)); + return lang.CreateCompletionBinding(fileName, doc.GetLocation(caretOffset), doc.CreateSnapshot()); + } + + static string GeneratePartialClassContextStub(FileName fileName, TextLocation currentLocation, StackFrame context) + { + var compilation = SD.ParserService.GetCompilationForFile(fileName); + var file = SD.ParserService.GetExistingUnresolvedFile(fileName); + if (compilation == null || file == null) + return ""; + var member = file.GetMember(currentLocation); + if (member == null) + return ""; + var builder = new TypeSystemAstBuilder(); + EntityDeclaration decl = builder.ConvertEntity(member.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly))); + decl.Name = "__DebuggerStub_" + decl.Name; + decl.Modifiers &= (Modifiers.Static); + switch (member.SymbolKind) { + case SymbolKind.Property: + break; + case SymbolKind.Indexer: + break; + case SymbolKind.Event: + break; + case SymbolKind.Method: + GenerateBodyFromContext(builder, context, (MethodDeclaration)decl); + break; + case SymbolKind.Operator: + break; + case SymbolKind.Constructor: + break; + case SymbolKind.Destructor: + break; + case SymbolKind.Accessor: + break; + default: + throw new ArgumentOutOfRangeException(); + } + return WrapInType(member.DeclaringTypeDefinition, decl.ToString()); + } + + static void GenerateBodyFromContext(TypeSystemAstBuilder builder, StackFrame context, MethodDeclaration methodDeclaration) + { + methodDeclaration.Body = new BlockStatement(); + foreach (var v in context.GetLocalVariables()) + methodDeclaration.Body.Statements.Add(new VariableDeclarationStatement(builder.ConvertType(v.Type), v.Name)); + methodDeclaration.Body.Statements.Add(new ExpressionStatement(new IdentifierExpression("$__Caret_Point__$"))); + } + + static string WrapInType(IUnresolvedTypeDefinition entity, string code) + { + if (entity == null) + return code; + code = WrapInType(entity.DeclaringTypeDefinition, GetHeader(entity) + code + "\r\n}"); + if (entity.DeclaringTypeDefinition == null) { + return "namespace " + entity.Namespace + " {\r\n" + code + "\r\n}"; + } + return code; + } + + static string GetHeader(IUnresolvedTypeDefinition entity) + { + StringBuilder builder = new StringBuilder(); + builder.Append("partial "); + switch (entity.Kind) { + case TypeKind.Class: + builder.Append("class "); + break; + case TypeKind.Interface: + builder.Append("interface "); + break; + case TypeKind.Struct: + builder.Append("struct "); + break; + default: + throw new NotSupportedException(); + } + builder.Append(entity.Name); + builder.AppendLine(" {"); + return builder.ToString(); + } + } +} + diff --git a/src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs b/src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs index 45695063d6..7a3b678371 100644 --- a/src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs +++ b/src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs @@ -18,7 +18,12 @@ using System; using System.ComponentModel.Design; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.SharpDevelop.Refactoring; namespace ICSharpCode.SharpDevelop @@ -70,6 +75,11 @@ namespace ICSharpCode.SharpDevelop } } + public virtual ICodeCompletionBinding CreateCompletionBinding(FileName fileName, TextLocation currentLocation, ITextSource fileContent) + { + throw new NotSupportedException(); + } + public object GetService(Type serviceType) { return container.GetService(serviceType); diff --git a/src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs b/src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs index 6eef839a3b..863e7508fe 100644 --- a/src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs +++ b/src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs @@ -17,7 +17,12 @@ // DEALINGS IN THE SOFTWARE. using System; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.SharpDevelop.Refactoring; namespace ICSharpCode.SharpDevelop @@ -55,5 +60,10 @@ namespace ICSharpCode.SharpDevelop System.CodeDom.Compiler.CodeDomProvider CodeDomProvider { get; } + + /// + /// Creates a completion binding which works with a fileName and a location as context. + /// + ICodeCompletionBinding CreateCompletionBinding(FileName fileName, TextLocation currentLocation, ITextSource fileContent); } }