diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index 60c5659f48..ffa35962e3 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -66,7 +66,9 @@ + + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs index 5ca9372617..1ef8be602f 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs @@ -4,15 +4,12 @@ using System; using System.Collections.Generic; using System.Linq; -using CSharpBinding.Parser; + using ICSharpCode.NRefactory.Completion; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Completion; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.CodeCompletion; -using ICSharpCode.SharpDevelop.Parser; namespace CSharpBinding.Completion { @@ -38,27 +35,17 @@ namespace CSharpBinding.Completion bool ShowCompletion(ITextEditor editor, char completionChar, bool ctrlSpace) { - // Don't require the very latest parse information, an older cached version is OK. - var parseInfo = SD.ParserService.GetCachedParseInformation(editor.FileName) as CSharpFullParseInformation; - if (parseInfo == null) { - parseInfo = SD.ParserService.Parse(editor.FileName, editor.Document) as CSharpFullParseInformation; - if (parseInfo == null) - return false; - } - ICompilation compilation = SD.ParserService.GetCompilationForFile(editor.FileName); - var projectContent = compilation.MainAssembly.UnresolvedAssembly as IProjectContent; - if (projectContent == null) + var completionContext = CSharpCompletionContext.Get(editor); + if (completionContext == null) return false; - var completionContextProvider = new DefaultCompletionContextProvider(editor.Document, parseInfo.ParsedFile); - var typeResolveContext = parseInfo.ParsedFile.GetTypeResolveContext(compilation, editor.Caret.Location); - var completionFactory = new CSharpCompletionDataFactory(typeResolveContext); + var completionFactory = new CSharpCompletionDataFactory(this, editor, completionContext.TypeResolveContextAtCaret); CSharpCompletionEngine cce = new CSharpCompletionEngine( editor.Document, - completionContextProvider, + completionContext.CompletionContextProvider, completionFactory, - projectContent, - typeResolveContext + completionContext.ProjectContent, + completionContext.TypeResolveContextAtCaret ); cce.FormattingPolicy = FormattingOptionsFactory.CreateSharpDevelop(); @@ -102,15 +89,15 @@ namespace CSharpBinding.Completion // Method Insight var pce = new CSharpParameterCompletionEngine( editor.Document, - completionContextProvider, + completionContext.CompletionContextProvider, completionFactory, - projectContent, - typeResolveContext + completionContext.ProjectContent, + completionContext.TypeResolveContextAtCaret ); - var provider = pce.GetParameterDataProvider(editor.Caret.Offset, completionChar) as CSharpParameterDataProvider; - if (provider != null && provider.items.Count > 0) { - var insightWindow = editor.ShowInsightWindow(provider.items); - insightWindow.StartOffset = provider.StartOffset; + var newInsight = pce.GetParameterDataProvider(editor.Caret.Offset, completionChar) as CSharpMethodInsight; + if (newInsight != null && newInsight.items.Count > 0) { + newInsight.UpdateHighlightedParameter(pce); + newInsight.Show(); return true; } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionContext.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionContext.cs new file mode 100644 index 0000000000..63da2d025e --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionContext.cs @@ -0,0 +1,56 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Diagnostics; +using CSharpBinding.Parser; +using ICSharpCode.NRefactory.CSharp.Completion; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Editor; + +namespace CSharpBinding.Completion +{ + sealed class CSharpCompletionContext + { + public readonly ITextEditor Editor; + public readonly CSharpFullParseInformation ParseInformation; + public readonly ICompilation Compilation; + public readonly IProjectContent ProjectContent; + public readonly CSharpTypeResolveContext TypeResolveContextAtCaret; + public readonly ICompletionContextProvider CompletionContextProvider; + + public static CSharpCompletionContext Get(ITextEditor editor) + { + // Don't require the very latest parse information, an older cached version is OK. + var parseInfo = SD.ParserService.GetCachedParseInformation(editor.FileName) as CSharpFullParseInformation; + if (parseInfo == null) { + parseInfo = SD.ParserService.Parse(editor.FileName, editor.Document) as CSharpFullParseInformation; + } + if (parseInfo == null) + return null; + + ICompilation compilation = SD.ParserService.GetCompilationForFile(editor.FileName); + var projectContent = compilation.MainAssembly.UnresolvedAssembly as IProjectContent; + if (projectContent == null) + return null; + + return new CSharpCompletionContext(editor, parseInfo, compilation, projectContent); + } + + private CSharpCompletionContext(ITextEditor editor, CSharpFullParseInformation parseInfo, ICompilation compilation, IProjectContent projectContent) + { + Debug.Assert(editor != null); + Debug.Assert(parseInfo != null); + Debug.Assert(compilation != null); + Debug.Assert(projectContent != null); + this.Editor = editor; + this.ParseInformation = parseInfo; + this.Compilation = compilation; + this.ProjectContent = projectContent; + this.TypeResolveContextAtCaret = parseInfo.ParsedFile.GetTypeResolveContext(compilation, editor.Caret.Location); + this.CompletionContextProvider = new DefaultCompletionContextProvider(editor.Document, parseInfo.ParsedFile); + } + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs index 48d4e76cf2..d8b5e12e6a 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs @@ -3,27 +3,36 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; + using ICSharpCode.NRefactory.Completion; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Completion; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Editor; namespace CSharpBinding.Completion { - class CSharpCompletionDataFactory : ICompletionDataFactory, IParameterCompletionDataFactory + sealed class CSharpCompletionDataFactory : ICompletionDataFactory, IParameterCompletionDataFactory { + readonly CSharpCompletionBinding binding; + readonly ITextEditor editor; readonly CSharpTypeResolveContext contextAtCaret; - public CSharpCompletionDataFactory(CSharpTypeResolveContext contextAtCaret) + public CSharpCompletionDataFactory(CSharpCompletionBinding binding, ITextEditor editor, CSharpTypeResolveContext contextAtCaret) { - if (contextAtCaret == null) - throw new ArgumentNullException("contextAtCaret"); + Debug.Assert(binding != null); + Debug.Assert(editor != null); + Debug.Assert(contextAtCaret != null); + this.binding = binding; + this.editor = editor; this.contextAtCaret = contextAtCaret; } + #region ICompletionDataFactory implementation public ICompletionData CreateEntityCompletionData(IUnresolvedEntity entity) { return new CompletionData(entity.Name) { @@ -81,7 +90,7 @@ namespace CSharpBinding.Completion public ICompletionData CreateVariableCompletionData(IVariable variable) { - return new CompletionData(variable.Name) { + return new CompletionData(variable.Name) { Image = ClassBrowserIconService.LocalVariable }; } @@ -115,33 +124,37 @@ namespace CSharpBinding.Completion { yield break; } + #endregion #region IParameterCompletionDataFactory implementation + IParameterDataProvider CreateMethodDataProvider(int startOffset, IEnumerable methods) + { + return new CSharpMethodInsight(binding, editor, startOffset, from m in methods where m != null select new CSharpInsightItem(m)); + } + IParameterDataProvider IParameterCompletionDataFactory.CreateConstructorProvider(int startOffset, IType type) { - IAmbience ambience = AmbienceService.GetCurrentAmbience(); - return new CSharpParameterDataProvider(startOffset, type.GetConstructors().Select(m => new CSharpInsightItem(m, ambience))); + return CreateMethodDataProvider(startOffset, type.GetConstructors()); } IParameterDataProvider IParameterCompletionDataFactory.CreateMethodDataProvider(int startOffset, IEnumerable methods) { - IAmbience ambience = AmbienceService.GetCurrentAmbience(); - return new CSharpParameterDataProvider(startOffset, methods.Select(m => new CSharpInsightItem(m, ambience))); + return CreateMethodDataProvider(startOffset, methods); } IParameterDataProvider IParameterCompletionDataFactory.CreateDelegateDataProvider(int startOffset, IType type) { - return new CSharpParameterDataProvider(startOffset, Enumerable.Empty()); + return CreateMethodDataProvider(startOffset, new[] { type.GetDelegateInvokeMethod() }); } IParameterDataProvider IParameterCompletionDataFactory.CreateIndexerParameterDataProvider(int startOffset, IType type, AstNode resolvedNode) { - throw new NotImplementedException(); + return CreateMethodDataProvider(startOffset, type.GetProperties(p => p.IsIndexer)); } IParameterDataProvider IParameterCompletionDataFactory.CreateTypeParameterDataProvider(int startOffset, IEnumerable types) { - throw new NotImplementedException(); + return null; } #endregion } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs index 57126a05f7..0aeaa92216 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs @@ -2,11 +2,14 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; -using System.Collections.Generic; -using System.Linq; -using ICSharpCode.NRefactory.Completion; +using System.IO; +using System.Text; +using System.Windows; +using System.Windows.Controls; + +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor.CodeCompletion; namespace CSharpBinding.Completion @@ -14,73 +17,84 @@ namespace CSharpBinding.Completion sealed class CSharpInsightItem : IInsightItem { public readonly IParameterizedMember Method; - readonly IAmbience ambience; - public CSharpInsightItem(IParameterizedMember method, IAmbience ambience) + public CSharpInsightItem(IParameterizedMember method) { this.Method = method; - this.ambience = ambience; } - string header; + TextBlock header; public object Header { get { if (header == null) { - ambience.ConversionFlags = ConversionFlags.StandardConversionFlags; - header = ambience.ConvertEntity(Method); + header = new TextBlock(); + GenerateHeader(); } return header; } } - public object Content { - get { return null; } - } - } - - sealed class CSharpParameterDataProvider : IParameterDataProvider - { - readonly int startOffset; - internal readonly IReadOnlyList items; - - public CSharpParameterDataProvider(int startOffset, IEnumerable items) - { - this.startOffset = startOffset; - this.items = items.ToList(); - } - - int IParameterDataProvider.Count { - get { return items.Count; } - } - - public int StartOffset { - get { return startOffset; } - } - - string IParameterDataProvider.GetHeading(int overload, string[] parameterDescription, int currentParameter) - { - throw new NotImplementedException(); - } + int highlightedParameterIndex = -1; - string IParameterDataProvider.GetDescription(int overload, int currentParameter) + public void HighlightParameter(int parameterIndex) { - throw new NotImplementedException(); + if (highlightedParameterIndex == parameterIndex) + return; + this.highlightedParameterIndex = parameterIndex; + if (header != null) + GenerateHeader(); } - string IParameterDataProvider.GetParameterDescription(int overload, int paramIndex) + void GenerateHeader() { - throw new NotImplementedException(); + CSharpAmbience ambience = new CSharpAmbience(); + ambience.ConversionFlags = ConversionFlags.StandardConversionFlags; + var stringBuilder = new StringBuilder(); + var formatter = new ParameterHighlightingOutputFormatter(stringBuilder, highlightedParameterIndex); + ambience.ConvertEntity(Method, formatter, FormattingOptionsFactory.CreateSharpDevelop()); + var inlineBuilder = new HighlightedInlineBuilder(stringBuilder.ToString()); + inlineBuilder.SetFontWeight(formatter.parameterStartOffset, formatter.parameterLength, FontWeights.Bold); + header.Inlines.Clear(); + header.Inlines.AddRange(inlineBuilder.CreateRuns()); } - int IParameterDataProvider.GetParameterCount(int overload) - { - throw new NotImplementedException(); + public object Content { + get { return null; } } - bool IParameterDataProvider.AllowParameterList(int overload) + sealed class ParameterHighlightingOutputFormatter : TextWriterOutputFormatter { - throw new NotImplementedException(); + StringBuilder b; + int highlightedParameterIndex; + int parameterIndex; + internal int parameterStartOffset; + internal int parameterLength; + + public ParameterHighlightingOutputFormatter(StringBuilder b, int highlightedParameterIndex) + : base(new StringWriter(b)) + { + this.b = b; + this.highlightedParameterIndex = highlightedParameterIndex; + } + + public override void StartNode(AstNode node) + { + if (parameterIndex == highlightedParameterIndex && node is ParameterDeclaration) { + parameterStartOffset = b.Length; + } + base.StartNode(node); + } + + public override void EndNode(AstNode node) + { + base.EndNode(node); + if (node is ParameterDeclaration) { + if (parameterIndex == highlightedParameterIndex) + parameterLength = b.Length - parameterStartOffset; + parameterIndex++; + } + } } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpMethodInsight.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpMethodInsight.cs new file mode 100644 index 0000000000..613e5b5a5a --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpMethodInsight.cs @@ -0,0 +1,110 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +using ICSharpCode.NRefactory.Completion; +using ICSharpCode.NRefactory.CSharp.Completion; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; + +namespace CSharpBinding.Completion +{ + sealed class CSharpMethodInsight : IParameterDataProvider + { + readonly int startOffset; + internal readonly IReadOnlyList items; + readonly CSharpCompletionBinding binding; + readonly ITextEditor editor; + IInsightWindow window; + + public CSharpMethodInsight(CSharpCompletionBinding binding, ITextEditor editor, int startOffset, IEnumerable items) + { + Debug.Assert(binding != null); + Debug.Assert(editor != null); + Debug.Assert(items != null); + this.binding = binding; + this.editor = editor; + this.startOffset = startOffset; + this.items = items.ToList(); + } + + public void Show() + { + window = editor.ShowInsightWindow(items); + window.StartOffset = startOffset; + // closing the window at the end of the parameter list is handled by the CaretPositionChanged event + window.EndOffset = editor.Document.TextLength; + window.CaretPositionChanged += window_CaretPositionChanged; + } + + void window_CaretPositionChanged(object sender, EventArgs e) + { + var completionContext = CSharpCompletionContext.Get(editor); + if (completionContext == null) { + window.Close(); + return; + } + var completionFactory = new CSharpCompletionDataFactory(binding, editor, completionContext.TypeResolveContextAtCaret); + var pce = new CSharpParameterCompletionEngine( + editor.Document, + completionContext.CompletionContextProvider, + completionFactory, + completionContext.ProjectContent, + completionContext.TypeResolveContextAtCaret + ); + UpdateHighlightedParameter(pce); + } + + public void UpdateHighlightedParameter(CSharpParameterCompletionEngine pce) + { + int parameterIndex = pce.GetCurrentParameterIndex(window != null ? window.StartOffset : startOffset, editor.Caret.Offset); + if (parameterIndex < 0 && window != null) { + window.Close(); + } else { + if (parameterIndex > 0) + parameterIndex--; // NR returns 1-based parameter index + foreach (var item in items) + item.HighlightParameter(parameterIndex); + } + } + + #region IParameterDataProvider implementation + int IParameterDataProvider.Count { + get { return items.Count; } + } + + int IParameterDataProvider.StartOffset { + get { return startOffset; } + } + + string IParameterDataProvider.GetHeading(int overload, string[] parameterDescription, int currentParameter) + { + throw new NotImplementedException(); + } + + string IParameterDataProvider.GetDescription(int overload, int currentParameter) + { + throw new NotImplementedException(); + } + + string IParameterDataProvider.GetParameterDescription(int overload, int paramIndex) + { + throw new NotImplementedException(); + } + + int IParameterDataProvider.GetParameterCount(int overload) + { + throw new NotImplementedException(); + } + + bool IParameterDataProvider.AllowParameterList(int overload) + { + throw new NotImplementedException(); + } + #endregion + } +} diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index bab5fa831b..318070e7c9 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -106,7 +106,6 @@ UserControl - diff --git a/src/Main/Base/Project/Src/Editor/CodeCompletion/IInsightWindowHandler.cs b/src/Main/Base/Project/Src/Editor/CodeCompletion/IInsightWindowHandler.cs deleted file mode 100644 index 6e2e26df48..0000000000 --- a/src/Main/Base/Project/Src/Editor/CodeCompletion/IInsightWindowHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; - -namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion -{ - public interface IInsightWindowHandler - { - void InitializeOpenedInsightWindow(ITextEditor editor, IInsightWindow insightWindow); - bool InsightRefreshOnComma(ITextEditor editor, char ch, out IInsightWindow insightWindow); - void HighlightParameter(IInsightWindow window, int index); - } -}