diff --git a/src/EditorFeatures/CSharp/QuickInfo/SemanticQuickInfoProvider.cs b/src/EditorFeatures/CSharp/QuickInfo/SemanticQuickInfoProvider.cs deleted file mode 100644 index fe2c5598dec20..0000000000000 --- a/src/EditorFeatures/CSharp/QuickInfo/SemanticQuickInfoProvider.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.ComponentModel.Composition; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Projection; - -namespace Microsoft.CodeAnalysis.Editor.CSharp.QuickInfo -{ - [ExportQuickInfoProvider(PredefinedQuickInfoProviderNames.Semantic, LanguageNames.CSharp)] - internal class SemanticQuickInfoProvider : AbstractSemanticQuickInfoProvider - { - protected override bool ShouldCheckPreviousToken(SyntaxToken token) - { - return !token.Parent.IsKind(SyntaxKind.XmlCrefAttribute); - } - } -} diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 4b341dd05708f..f37764f644af7 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -7,13 +7,9 @@ using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Editor.CSharp.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.QuickInfo; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Projection; +using Microsoft.CodeAnalysis.QuickInfo; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -22,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.QuickInfo { public class SemanticQuickInfoSourceTests : AbstractSemanticQuickInfoSourceTests { - private async Task TestWithOptionsAsync(CSharpParseOptions options, string markup, params Action[] expectedResults) + private async Task TestWithOptionsAsync(CSharpParseOptions options, string markup, params Action[] expectedResults) { using (var workspace = TestWorkspace.CreateCSharp(markup, options)) { @@ -30,16 +26,16 @@ private async Task TestWithOptionsAsync(CSharpParseOptions options, string marku } } - private async Task TestWithOptionsAsync(TestWorkspace workspace, params Action[] expectedResults) + private async Task TestWithOptionsAsync(TestWorkspace workspace, params Action[] expectedResults) { var testDocument = workspace.DocumentWithCursor; var position = testDocument.CursorPosition.GetValueOrDefault(); var documentId = workspace.GetDocumentId(testDocument); var document = workspace.CurrentSolution.GetDocument(documentId); - var provider = new SemanticQuickInfoProvider(); + var service = QuickInfoService.GetService(document); - await TestWithOptionsAsync(document, provider, position, expectedResults); + await TestWithOptionsAsync(document, service, position, expectedResults); // speculative semantic model if (await CanUseSpeculativeSemanticModelAsync(document, position)) @@ -52,34 +48,30 @@ private async Task TestWithOptionsAsync(TestWorkspace workspace, params Action[] expectedResults) + private async Task TestWithOptionsAsync(Document document, QuickInfoService service, int position, Action[] expectedResults) { - var state = await provider.GetItemAsync(document, position, cancellationToken: CancellationToken.None); - if (state != null) - { - WaitForDocumentationComment(state.Content); - } + var info = await service.GetQuickInfoAsync(document, position, cancellationToken: CancellationToken.None); if (expectedResults.Length == 0) { - Assert.Null(state); + Assert.Null(info); } else { - Assert.NotNull(state); + Assert.NotNull(info); foreach (var expected in expectedResults) { - expected(state.Content); + expected(info); } } } - private async Task VerifyWithMscorlib45Async(string markup, Action[] expectedResults) + private async Task VerifyWithMscorlib45Async(string markup, Action[] expectedResults) { var xmlString = string.Format(@" @@ -96,37 +88,33 @@ private async Task VerifyWithMscorlib45Async(string markup, Action[] exp var documentId = workspace.Documents.Where(d => d.Name == "SourceDocument").Single().Id; var document = workspace.CurrentSolution.GetDocument(documentId); - var provider = new SemanticQuickInfoProvider(); + var service = QuickInfoService.GetService(document); - var state = await provider.GetItemAsync(document, position, cancellationToken: CancellationToken.None); - if (state != null) - { - WaitForDocumentationComment(state.Content); - } + var info = await service.GetQuickInfoAsync(document, position, cancellationToken: CancellationToken.None); if (expectedResults.Length == 0) { - Assert.Null(state); + Assert.Null(info); } else { - Assert.NotNull(state); + Assert.NotNull(info); foreach (var expected in expectedResults) { - expected(state.Content); + expected(info); } } } } - protected override async Task TestAsync(string markup, params Action[] expectedResults) + protected override async Task TestAsync(string markup, params Action[] expectedResults) { await TestWithOptionsAsync(Options.Regular, markup, expectedResults); await TestWithOptionsAsync(Options.Script, markup, expectedResults); } - protected async Task TestWithUsingsAsync(string markup, params Action[] expectedResults) + private async Task TestWithUsingsAsync(string markup, params Action[] expectedResults) { var markupWithUsings = @"using System; @@ -137,13 +125,13 @@ protected async Task TestWithUsingsAsync(string markup, params Action[] await TestAsync(markupWithUsings, expectedResults); } - protected Task TestInClassAsync(string markup, params Action[] expectedResults) + private Task TestInClassAsync(string markup, params Action[] expectedResults) { var markupInClass = "class C { " + markup + " }"; return TestWithUsingsAsync(markupInClass, expectedResults); } - protected Task TestInMethodAsync(string markup, params Action[] expectedResults) + private Task TestInMethodAsync(string markup, params Action[] expectedResults) { var markupInMethod = "class C { void M() { " + markup + " } }"; return TestWithUsingsAsync(markupInMethod, expectedResults); @@ -153,7 +141,7 @@ private async Task TestWithReferenceAsync(string sourceCode, string referencedCode, string sourceLanguage, string referencedLanguage, - params Action[] expectedResults) + params Action[] expectedResults) { await TestWithMetadataReferenceHelperAsync(sourceCode, referencedCode, sourceLanguage, referencedLanguage, expectedResults); await TestWithProjectReferenceHelperAsync(sourceCode, referencedCode, sourceLanguage, referencedLanguage, expectedResults); @@ -170,7 +158,7 @@ private async Task TestWithMetadataReferenceHelperAsync( string referencedCode, string sourceLanguage, string referencedLanguage, - params Action[] expectedResults) + params Action[] expectedResults) { var xmlString = string.Format(@" @@ -195,7 +183,7 @@ private async Task TestWithProjectReferenceHelperAsync( string referencedCode, string sourceLanguage, string referencedLanguage, - params Action[] expectedResults) + params Action[] expectedResults) { var xmlString = string.Format(@" @@ -221,7 +209,7 @@ private async Task TestInSameProjectHelperAsync( string sourceCode, string referencedCode, string sourceLanguage, - params Action[] expectedResults) + params Action[] expectedResults) { var xmlString = string.Format(@" @@ -238,7 +226,7 @@ private async Task TestInSameProjectHelperAsync( await VerifyWithReferenceWorkerAsync(xmlString, expectedResults); } - private async Task VerifyWithReferenceWorkerAsync(string xmlString, params Action[] expectedResults) + private async Task VerifyWithReferenceWorkerAsync(string xmlString, params Action[] expectedResults) { using (var workspace = TestWorkspace.Create(xmlString)) { @@ -246,25 +234,21 @@ private async Task VerifyWithReferenceWorkerAsync(string xmlString, params Actio var documentId = workspace.Documents.First(d => d.Name == "SourceDocument").Id; var document = workspace.CurrentSolution.GetDocument(documentId); - var provider = new SemanticQuickInfoProvider(); + var service = QuickInfoService.GetService(document); - var state = await provider.GetItemAsync(document, position, cancellationToken: CancellationToken.None); - if (state != null) - { - WaitForDocumentationComment(state.Content); - } + var info = await service.GetQuickInfoAsync(document, position, cancellationToken: CancellationToken.None); if (expectedResults.Length == 0) { - Assert.Null(state); + Assert.Null(info); } else { - Assert.NotNull(state); + Assert.NotNull(info); foreach (var expected in expectedResults) { - expected(state.Content); + expected(info); } } } diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SyntacticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SyntacticQuickInfoSourceTests.cs index 5c2893dec31e5..438d26c9928b5 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SyntacticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SyntacticQuickInfoSourceTests.cs @@ -4,15 +4,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Editor.CSharp.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.CSharp.QuickInfo; using Microsoft.CodeAnalysis.Editor.UnitTests.QuickInfo; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Projection; +using Microsoft.CodeAnalysis.QuickInfo; using Roslyn.Test.Utilities; using Xunit; @@ -263,9 +258,9 @@ await TestInMethodAndScriptAsync( {"); } - private IQuickInfoProvider CreateProvider(TestWorkspace workspace) + private QuickInfoProvider CreateProvider(TestWorkspace workspace) { - return new SyntacticQuickInfoProvider(); + return new CSharpSyntacticQuickInfoProvider(); } protected override async Task AssertNoContentAsync( @@ -274,7 +269,7 @@ protected override async Task AssertNoContentAsync( int position) { var provider = CreateProvider(workspace); - Assert.Null(await provider.GetItemAsync(document, position, CancellationToken.None)); + Assert.Null(await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, CancellationToken.None))); } protected override async Task AssertContentIsAsync( @@ -285,14 +280,15 @@ protected override async Task AssertContentIsAsync( string expectedDocumentationComment = null) { var provider = CreateProvider(workspace); - var state = await provider.GetItemAsync(document, position, cancellationToken: CancellationToken.None); - Assert.NotNull(state); - - var hostingControlFactory = workspace.GetService(); - - var viewHostingControl = (ViewHostingControl)hostingControlFactory.CreateElement(state.Content); - var actualContent = viewHostingControl.GetText_TestOnly(); - Assert.Equal(expectedContent, actualContent); + var info = await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, CancellationToken.None)); + Assert.NotNull(info); + + Assert.NotEqual(0, info.RelatedSpans.Length); + var tabSize = document.Project.Solution.Workspace.Options.GetOption(Microsoft.CodeAnalysis.Formatting.FormattingOptions.TabSize, document.Project.Language); + var text = await document.GetTextAsync(); + var spans = IndentationHelper.GetSpansWithAlignedIndentation(text, info.RelatedSpans, tabSize); + var actualText = string.Concat(spans.Select(s => text.GetSubText(s).ToString())); + Assert.Equal(expectedContent, actualText); } protected override Task TestInMethodAsync(string code, string expectedContent, string expectedDocumentationComment = null) diff --git a/src/EditorFeatures/Core.Wpf/Completion/Presentation/CustomCommitCompletion.cs b/src/EditorFeatures/Core.Wpf/Completion/Presentation/CustomCommitCompletion.cs index ddea192c2df19..da93eb0b7a087 100644 --- a/src/EditorFeatures/Core.Wpf/Completion/Presentation/CustomCommitCompletion.cs +++ b/src/EditorFeatures/Core.Wpf/Completion/Presentation/CustomCommitCompletion.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Wpf; +using Microsoft.CodeAnalysis.Tags; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.Language.Intellisense; @@ -31,7 +32,7 @@ public CustomCommitCompletion( // extra allocation is avoided. _completionPresenterSession = completionPresenterSession; this.CompletionItem = completionItem; - _imageMoniker = ImageMonikers.GetImageMoniker(CompletionItem.Tags); + _imageMoniker = ImageMonikers.GetFirstImageMoniker(CompletionItem.Tags); } public void Commit() @@ -74,7 +75,7 @@ public override IEnumerable AttributeIcons { get { - if (this.CompletionItem.Tags.Contains(CompletionTags.Warning)) + if (this.CompletionItem.Tags.Contains(WellKnownTags.Warning)) { return new[] { new CompletionIcon2(Glyph.CompletionWarning.GetImageMoniker(), s_glyphCompletionWarning, s_glyphCompletionWarning) }; } diff --git a/src/EditorFeatures/Core.Wpf/Completion/Presentation/ImageMonikers.cs b/src/EditorFeatures/Core.Wpf/Completion/Presentation/ImageMonikers.cs index 219bc6992ee08..02d8269218d8f 100644 --- a/src/EditorFeatures/Core.Wpf/Completion/Presentation/ImageMonikers.cs +++ b/src/EditorFeatures/Core.Wpf/Completion/Presentation/ImageMonikers.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Wpf; using Microsoft.VisualStudio.Imaging.Interop; @@ -9,9 +8,9 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.P { internal static class ImageMonikers { - public static ImageMoniker GetImageMoniker(ImmutableArray tags) + public static ImageMoniker GetFirstImageMoniker(ImmutableArray tags) { - return tags.GetGlyph().GetImageMoniker(); + return tags.GetFirstGlyph().GetImageMoniker(); } } } diff --git a/src/EditorFeatures/Core.Wpf/Completion/Presentation/RoslynIntellisenseFilter.cs b/src/EditorFeatures/Core.Wpf/Completion/Presentation/RoslynIntellisenseFilter.cs index 30dbb9de33787..de2cc9b987fda 100644 --- a/src/EditorFeatures/Core.Wpf/Completion/Presentation/RoslynIntellisenseFilter.cs +++ b/src/EditorFeatures/Core.Wpf/Completion/Presentation/RoslynIntellisenseFilter.cs @@ -10,7 +10,7 @@ internal class IntellisenseFilter2 : IntellisenseFilter public IntellisenseFilter2( RoslynCompletionSet completionSet, CompletionItemFilter filter) - : base(ImageMonikers.GetImageMoniker(filter.Tags), GetToolTip(filter), + : base(ImageMonikers.GetFirstImageMoniker(filter.Tags), GetToolTip(filter), filter.AccessKey.ToString(), automationText: filter.Tags[0]) { _completionSet = completionSet; diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ClassifiableDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ClassifiableDeferredContentConverter.cs deleted file mode 100644 index 1a5c28fe5bea7..0000000000000 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ClassifiableDeferredContentConverter.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Text.Classification; - -namespace Microsoft.CodeAnalysis.Editor.QuickInfo -{ - [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] - [QuickInfoConverterMetadata(typeof(ClassifiableDeferredContent))] - class ClassifiableDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter - { - private readonly ClassificationTypeMap _typeMap; - private readonly IClassificationFormatMapService _classificationFormatMapService; - - [ImportingConstructor] - public ClassifiableDeferredContentConverter( - ClassificationTypeMap typeMap, - IClassificationFormatMapService classificationFormatMapService) - { - _typeMap = typeMap; - _classificationFormatMapService = classificationFormatMapService; - } - - public FrameworkElement CreateFrameworkElement(IDeferredQuickInfoContent deferredContent, DeferredContentFrameworkElementFactory factory) - { - var classifiableContent = (ClassifiableDeferredContent)deferredContent; - var formatMap = _classificationFormatMapService.GetClassificationFormatMap("tooltip"); - var classifiedTextBlock = classifiableContent.ClassifiableContent.ToTextBlock(formatMap, _typeMap); - - if (classifiedTextBlock.Inlines.Count == 0) - { - classifiedTextBlock.Visibility = Visibility.Collapsed; - } - - return classifiedTextBlock; - } - - public Type GetApplicableType() - { - return typeof(ClassifiableDeferredContent); - } - } -} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/DocumentationCommentDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/DocumentationCommentDeferredContentConverter.cs deleted file mode 100644 index 2f7a6dbf84443..0000000000000 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/DocumentationCommentDeferredContentConverter.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Text.Classification; - -namespace Microsoft.CodeAnalysis.Editor.QuickInfo.Converters -{ - [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] - [QuickInfoConverterMetadata(typeof(DocumentationCommentDeferredContent))] - internal sealed class DocumentationCommentDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter - { - private readonly ClassificationTypeMap _typeMap; - private readonly IClassificationFormatMapService _classificationFormatMapService; - - [ImportingConstructor] - public DocumentationCommentDeferredContentConverter(ClassificationTypeMap typeMap, IClassificationFormatMapService classificationFormatMapService) - { - _typeMap = typeMap; - _classificationFormatMapService = classificationFormatMapService; - } - - public FrameworkElement CreateFrameworkElement(IDeferredQuickInfoContent deferredContent, DeferredContentFrameworkElementFactory factory) - { - var documentationCommentContent = (DocumentationCommentDeferredContent)deferredContent; - - var documentationTextBlock = new TextBlock() - { - TextWrapping = TextWrapping.Wrap - }; - - var formatMap = _classificationFormatMapService.GetClassificationFormatMap("tooltip"); - documentationTextBlock.SetDefaultTextProperties(formatMap); - - // If we have already computed the symbol documentation by now, update - - UpdateDocumentationTextBlock(documentationCommentContent, documentationTextBlock); - return documentationTextBlock; - } - - private void UpdateDocumentationTextBlock(DocumentationCommentDeferredContent deferredContent, TextBlock documentationTextBlock) - { - if (!string.IsNullOrEmpty(deferredContent.DocumentationComment)) - { - documentationTextBlock.Text = deferredContent.DocumentationComment; - } - else - { - documentationTextBlock.Text = string.Empty; - documentationTextBlock.Visibility = Visibility.Collapsed; - } - } - - public Type GetApplicableType() - { - return typeof(DocumentationCommentDeferredContent); - } - } -} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/IDeferredQuickInfoContentToFrameworkElementConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/IDeferredQuickInfoContentToFrameworkElementConverter.cs deleted file mode 100644 index b46fcbf88b43e..0000000000000 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/IDeferredQuickInfoContentToFrameworkElementConverter.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; - -namespace Microsoft.CodeAnalysis.Editor.QuickInfo -{ - interface IDeferredQuickInfoContentToFrameworkElementConverter - { - Type GetApplicableType(); - FrameworkElement CreateFrameworkElement(IDeferredQuickInfoContent deferredContent, DeferredContentFrameworkElementFactory factory); - } -} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ProjectionBufferDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ProjectionBufferDeferredContentConverter.cs deleted file mode 100644 index 70b75866815e9..0000000000000 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ProjectionBufferDeferredContentConverter.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.ComponentModel.Composition; -using System.Windows; -using System.Windows.Media; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Projection; -using Microsoft.VisualStudio.Utilities; - -namespace Microsoft.CodeAnalysis.Editor.QuickInfo -{ - [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] - [QuickInfoConverterMetadata(typeof(ProjectionBufferDeferredContent))] - class ProjectionBufferDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter - { - private readonly IProjectionBufferFactoryService _projectionBufferFactoryService; - private readonly IEditorOptionsFactoryService _editorOptionsFactoryService; - private readonly ITextEditorFactoryService _textEditorFactoryService; - - [ImportingConstructor] - public ProjectionBufferDeferredContentConverter( - IProjectionBufferFactoryService projectionBufferFactoryService, - IEditorOptionsFactoryService editorOptionsFactoryService, - ITextEditorFactoryService textEditorFactoryService) - { - _projectionBufferFactoryService = projectionBufferFactoryService; - _editorOptionsFactoryService = editorOptionsFactoryService; - _textEditorFactoryService = textEditorFactoryService; - } - - public FrameworkElement CreateFrameworkElement(IDeferredQuickInfoContent deferredContent, DeferredContentFrameworkElementFactory factory) - { - var projectionBufferDeferredContent = (ProjectionBufferDeferredContent)deferredContent; - return new ViewHostingControl(buffer => CreateView(projectionBufferDeferredContent, buffer), () => CreateBuffer(projectionBufferDeferredContent)); - } - - private IWpfTextView CreateView(ProjectionBufferDeferredContent deferredContent, ITextBuffer buffer) - { - var view = _textEditorFactoryService.CreateTextView( - buffer, deferredContent.RoleSet ?? _textEditorFactoryService.NoRoles); - - view.SizeToFit(); - view.Background = Brushes.Transparent; - - // Zoom out a bit to shrink the text. - view.ZoomLevel *= 0.75; - - return view; - } - - private IProjectionBuffer CreateBuffer(ProjectionBufferDeferredContent deferredContent) - { - return _projectionBufferFactoryService.CreateProjectionBufferWithoutIndentation( - _editorOptionsFactoryService.GlobalOptions, deferredContent.ContentType, deferredContent.Span); - } - - public Type GetApplicableType() - { - return typeof(ProjectionBufferDeferredContent); - } - } -} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoConverterMetadataAttribute.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoConverterMetadataAttribute.cs deleted file mode 100644 index 1537f9bf4c730..0000000000000 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoConverterMetadataAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.ComponentModel.Composition; - -namespace Microsoft.CodeAnalysis.Editor.QuickInfo -{ - [MetadataAttribute] - internal sealed class QuickInfoConverterMetadataAttribute : Attribute - { - public QuickInfoConverterMetadataAttribute(Type deferredType) - { - DeferredTypeFullName = deferredType.FullName; - } - - public string DeferredTypeFullName { get; } - } -} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoDisplayDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoDisplayDeferredContentConverter.cs deleted file mode 100644 index 68350c4da6d39..0000000000000 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoDisplayDeferredContentConverter.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; - -namespace Microsoft.CodeAnalysis.Editor.QuickInfo -{ - [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] - [QuickInfoConverterMetadata(typeof(QuickInfoDisplayDeferredContent))] - class QuickInfoDisplayDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter - { - public FrameworkElement CreateFrameworkElement(IDeferredQuickInfoContent deferredContent, DeferredContentFrameworkElementFactory factory) - { - var quickInfoDisplay = (QuickInfoDisplayDeferredContent)deferredContent; - FrameworkElement warningGlyphElement = null; - if (quickInfoDisplay.WarningGlyph != null) - { - warningGlyphElement = factory.CreateElement(quickInfoDisplay.WarningGlyph); - } - - FrameworkElement symbolGlyphElement = null; - if (quickInfoDisplay.SymbolGlyph != null) - { - symbolGlyphElement = factory.CreateElement(quickInfoDisplay.SymbolGlyph); - } - - return new QuickInfoDisplayPanel( - symbolGlyphElement, - warningGlyphElement, - factory.CreateElement(quickInfoDisplay.MainDescription), - factory.CreateElement(quickInfoDisplay.Documentation), - factory.CreateElement(quickInfoDisplay.TypeParameterMap), - factory.CreateElement(quickInfoDisplay.AnonymousTypes), - factory.CreateElement(quickInfoDisplay.UsageText), - factory.CreateElement(quickInfoDisplay.ExceptionText)); - } - - public Type GetApplicableType() - { - return typeof(QuickInfoDisplayDeferredContent); - } - } -} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/SymbolGlyphDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/SymbolGlyphDeferredContentConverter.cs deleted file mode 100644 index cad17e81ce528..0000000000000 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/SymbolGlyphDeferredContentConverter.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.ComponentModel.Composition; -using System.Windows; -using System.Windows.Data; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Wpf; -using Microsoft.VisualStudio.Imaging; -using Microsoft.VisualStudio.PlatformUI; - -namespace Microsoft.CodeAnalysis.Editor.QuickInfo -{ - [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] - [QuickInfoConverterMetadata(typeof(SymbolGlyphDeferredContent))] - class SymbolGlyphDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter - { - public FrameworkElement CreateFrameworkElement(IDeferredQuickInfoContent deferredContent, DeferredContentFrameworkElementFactory factory) - { - var symbolDeferredContent = (SymbolGlyphDeferredContent)deferredContent; - - var image = new CrispImage - { - Moniker = symbolDeferredContent.Glyph.GetImageMoniker(), - }; - - // Inform the ImageService of the background color so that images have the correct background. - var binding = new Binding("Background") - { - Converter = new BrushToColorConverter(), - RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(QuickInfoDisplayPanel), 1) - }; - - image.SetBinding(ImageThemingUtilities.ImageBackgroundColorProperty, binding); - return image; - } - - public Type GetApplicableType() - { - return typeof(SymbolGlyphDeferredContent); - } - } -} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/DeferredContentFrameworkElementFactory.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/DeferredContentFrameworkElementFactory.cs deleted file mode 100644 index d0a3a2ef0c4bc..0000000000000 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/DeferredContentFrameworkElementFactory.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using System.Windows; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Editor.QuickInfo -{ - [Export] - internal class DeferredContentFrameworkElementFactory - { - private readonly Dictionary> _convertersByTypeFullName - = new Dictionary>(); - private readonly IEnumerable> _convertersWithoutMetadata; - - [ImportingConstructor] - public DeferredContentFrameworkElementFactory( - [ImportMany] IEnumerable> converters, - [ImportMany] IEnumerable> convertersWithoutMetadata) - { - _convertersByTypeFullName = converters - .Where(i => !string.IsNullOrEmpty(i.Metadata.DeferredTypeFullName)) - .ToDictionary( - lazy => lazy.Metadata.DeferredTypeFullName, - lazy => (Lazy)lazy); - - _convertersWithoutMetadata = convertersWithoutMetadata; - } - - internal FrameworkElement CreateElement(IDeferredQuickInfoContent deferredContent) - { - var deferredContentFullName = deferredContent.GetType().FullName; - Lazy converter; - - if (!_convertersByTypeFullName.TryGetValue(deferredContentFullName, out converter)) - { - // The content must be of a type we didn't have MEF deferred metadata for. Realize the - // ones without MEF metadata, forcing everything to load. - foreach (var converterWithoutMetadata in _convertersWithoutMetadata) - { - _convertersByTypeFullName[converterWithoutMetadata.Value.GetApplicableType().FullName] = - new Lazy(() => converterWithoutMetadata.Value); - } - - Contract.ThrowIfFalse(_convertersByTypeFullName.TryGetValue(deferredContentFullName, out converter)); - } - - return converter.Value.CreateFrameworkElement(deferredContent, this); - } - - internal class QuickInfoConverterMetadata - { - public QuickInfoConverterMetadata(IDictionary data) - { - DeferredTypeFullName = (string)data.GetValueOrDefault(nameof(QuickInfoConverterMetadataAttribute.DeferredTypeFullName)); - } - - public string DeferredTypeFullName { get; set; } - } - } -} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoPresenterSession.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoPresenterSession.cs index d4d6e3878da2f..687eba81cc0c5 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoPresenterSession.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoPresenterSession.cs @@ -2,24 +2,40 @@ using System; using System.Collections.Generic; -using Microsoft.CodeAnalysis.Editor.QuickInfo; +using System.Collections.Immutable; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Editor.Wpf; +using Microsoft.CodeAnalysis.QuickInfo; +using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Projection; + +using QuickInfoItem = Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem; #pragma warning disable CS0618 // IQuickInfo* is obsolete, tracked by https://github.com/dotnet/roslyn/issues/24094 -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo.Presentation +namespace Microsoft.CodeAnalysis.Editor.QuickInfo.Presentation { internal partial class QuickInfoPresenter { private class QuickInfoPresenterSession : ForegroundThreadAffinitizedObject, IQuickInfoPresenterSession { private readonly IQuickInfoBroker _quickInfoBroker; - private readonly DeferredContentFrameworkElementFactory _elementFactory; private readonly ITextView _textView; private readonly ITextBuffer _subjectBuffer; + private readonly ClassificationTypeMap _classificationTypeMap; + private readonly IClassificationFormatMapService _classificationFormatMapService; + private readonly IProjectionBufferFactoryService _projectionBufferFactoryService; + private readonly IEditorOptionsFactoryService _editorOptionsFactoryService; + private readonly ITextEditorFactoryService _textEditorFactoryService; private IQuickInfoSession _editorSessionOpt; @@ -28,18 +44,26 @@ private class QuickInfoPresenterSession : ForegroundThreadAffinitizedObject, IQu public event EventHandler Dismissed; - public QuickInfoPresenterSession(IQuickInfoBroker quickInfoBroker, DeferredContentFrameworkElementFactory elementFactory, ITextView textView, ITextBuffer subjectBuffer) - : this(quickInfoBroker, elementFactory, textView, subjectBuffer, null) - { - } - - public QuickInfoPresenterSession(IQuickInfoBroker quickInfoBroker, DeferredContentFrameworkElementFactory elementFactory, ITextView textView, ITextBuffer subjectBuffer, IQuickInfoSession sessionOpt) + public QuickInfoPresenterSession( + IQuickInfoBroker quickInfoBroker, + ITextView textView, + ITextBuffer subjectBuffer, + IQuickInfoSession sessionOpt, + ClassificationTypeMap classificationTypeMap, + IClassificationFormatMapService classificationFormatMapService, + IProjectionBufferFactoryService projectionBufferFactoryService, + IEditorOptionsFactoryService editorOptionsFactoryService, + ITextEditorFactoryService textEditorFactoryService) { _quickInfoBroker = quickInfoBroker; - _elementFactory = elementFactory; _textView = textView; _subjectBuffer = subjectBuffer; _editorSessionOpt = sessionOpt; + _classificationTypeMap = classificationTypeMap; + _classificationFormatMapService = classificationFormatMapService; + _projectionBufferFactoryService = projectionBufferFactoryService; + _editorOptionsFactoryService = editorOptionsFactoryService; + _textEditorFactoryService = textEditorFactoryService; } public void PresentItem(ITrackingSpan triggerSpan, QuickInfoItem item, bool trackMouse) @@ -99,13 +123,100 @@ public void Dismiss() private void OnEditorSessionDismissed() { AssertIsForeground(); - this.Dismissed?.Invoke(this, new EventArgs()); + this.Dismissed?.Invoke(this, EventArgs.Empty); } internal void AugmentQuickInfoSession(IList quickInfoContent, out ITrackingSpan applicableToSpan) { applicableToSpan = _triggerSpan; - quickInfoContent.Add(_elementFactory.CreateElement(_item.Content)); + + var content = CreateContent(_item, _subjectBuffer.CurrentSnapshot); + if (content != null) + { + quickInfoContent.Add(content); + } + } + + private FrameworkElement CreateContent(QuickInfoItem quickInfoItem, ITextSnapshot snapshot) + { + var glyphs = quickInfoItem.Tags.GetGlyphs(); + var symbolGlyph = glyphs.FirstOrDefault(g => g != Glyph.CompletionWarning); + var warningGlyph = glyphs.FirstOrDefault(g => g == Glyph.CompletionWarning); + var documentSpan = quickInfoItem.RelatedSpans.Length > 0 ? CreateDocumentSpanPresentation(quickInfoItem, snapshot) : null; + + return new QuickInfoDisplayPanel( + symbolGlyph: symbolGlyph != default ? CreateSymbolPresentation(symbolGlyph) : null, + warningGlyph: warningGlyph != default ? CreateSymbolPresentation(warningGlyph) : null, + textBlocks: quickInfoItem.Sections.Select(section => new TextBlockElement(section.Kind, CreateTextPresentation(section))).ToImmutableArray(), + documentSpan: documentSpan); + } + + private FrameworkElement CreateSymbolPresentation(Glyph glyph) + { + var image = new CrispImage + { + Moniker = glyph.GetImageMoniker() + }; + + // Inform the ImageService of the background color so that images have the correct background. + var binding = new Binding("Background") + { + Converter = new BrushToColorConverter(), + RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(QuickInfoDisplayPanel), 1) + }; + + image.SetBinding(ImageThemingUtilities.ImageBackgroundColorProperty, binding); + return image; + } + + private TextBlock CreateTextPresentation(QuickInfoSection section) + { + if (section.Kind == QuickInfoSectionKinds.DocumentationComments) + { + return CreateDocumentationCommentPresentation(section.TaggedParts); + } + else + { + return CreateTextPresentation(section.TaggedParts); + } + } + + private TextBlock CreateTextPresentation(ImmutableArray text) + { + var formatMap = _classificationFormatMapService.GetClassificationFormatMap("tooltip"); + var classifiedTextBlock = text.ToTextBlock(formatMap, _classificationTypeMap); + + if (classifiedTextBlock.Inlines.Count == 0) + { + classifiedTextBlock.Visibility = Visibility.Collapsed; + } + + return classifiedTextBlock; + } + + private TextBlock CreateDocumentationCommentPresentation(ImmutableArray text) + { + var formatMap = _classificationFormatMapService.GetClassificationFormatMap("tooltip"); + var documentationTextBlock = text.ToTextBlock(formatMap, _classificationTypeMap); + + documentationTextBlock.TextWrapping = TextWrapping.Wrap; + + // If we have already computed the symbol documentation by now, update + if (documentationTextBlock.Inlines.Count == 0) + { + documentationTextBlock.Visibility = Visibility.Collapsed; + } + + return documentationTextBlock; + } + + private FrameworkElement CreateDocumentSpanPresentation(Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem info, ITextSnapshot snapshot) + { + return ProjectionBufferContent.Create( + info.RelatedSpans.Select(s => new SnapshotSpan(snapshot, new Span(s.Start, s.Length))).ToImmutableArray(), + _projectionBufferFactoryService, + _editorOptionsFactoryService, + _textEditorFactoryService); } } } diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoSource.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoSource.cs index ecccc53db6d66..ba92cf77a7935 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoSource.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoSource.cs @@ -6,7 +6,7 @@ using Microsoft.VisualStudio.Text; #pragma warning disable CS0618 // IQuickInfo* is obsolete, tracked by https://github.com/dotnet/roslyn/issues/24094 -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo.Presentation +namespace Microsoft.CodeAnalysis.Editor.QuickInfo.Presentation { internal partial class QuickInfoPresenter { diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.cs index 5919a8a84718d..cde29688f1f98 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Presentation/QuickInfoPresenter.cs @@ -1,15 +1,16 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel.Composition; -using Microsoft.CodeAnalysis.Editor.QuickInfo; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Projection; using Microsoft.VisualStudio.Utilities; #pragma warning disable CS0618 // IQuickInfo* is obsolete, tracked by https://github.com/dotnet/roslyn/issues/24094 -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo.Presentation +namespace Microsoft.CodeAnalysis.Editor.QuickInfo.Presentation { [Export(typeof(IQuickInfoSourceProvider))] [Export(typeof(IIntelliSensePresenter))] @@ -21,19 +22,41 @@ internal partial class QuickInfoPresenter : ForegroundThreadAffinitizedObject, I private static readonly object s_augmentSessionKey = new object(); private readonly IQuickInfoBroker _quickInfoBroker; - private readonly DeferredContentFrameworkElementFactory _elementFactory; + private readonly ClassificationTypeMap _classificationTypeMap; + private readonly IClassificationFormatMapService _classificationFormatMapService; + private readonly IProjectionBufferFactoryService _projectionBufferFactoryService; + private readonly IEditorOptionsFactoryService _editorOptionsFactoryService; + private readonly ITextEditorFactoryService _textEditorFactoryService; [ImportingConstructor] - public QuickInfoPresenter(IQuickInfoBroker quickInfoBroker, DeferredContentFrameworkElementFactory elementFactory) + public QuickInfoPresenter( + IQuickInfoBroker quickInfoBroker, + ClassificationTypeMap classificationTypeMap, + IClassificationFormatMapService classificationFormatMapService, + IProjectionBufferFactoryService projectionBufferFactoryService, + IEditorOptionsFactoryService editorOptionsFactoryService, + ITextEditorFactoryService textEditorFactoryService) { _quickInfoBroker = quickInfoBroker; - _elementFactory = elementFactory; + _classificationTypeMap = classificationTypeMap; + _classificationFormatMapService = classificationFormatMapService; + _projectionBufferFactoryService = projectionBufferFactoryService; + _editorOptionsFactoryService = editorOptionsFactoryService; + _textEditorFactoryService = textEditorFactoryService; } IQuickInfoPresenterSession IIntelliSensePresenter.CreateSession(ITextView textView, ITextBuffer subjectBuffer, IQuickInfoSession sessionOpt) { AssertIsForeground(); - return new QuickInfoPresenterSession(_quickInfoBroker, _elementFactory, textView, subjectBuffer, sessionOpt); + + return new QuickInfoPresenterSession( + _quickInfoBroker, + textView, subjectBuffer, sessionOpt, + _classificationTypeMap, + _classificationFormatMapService, + _projectionBufferFactoryService, + _editorOptionsFactoryService, + _textEditorFactoryService); } IQuickInfoSource IQuickInfoSourceProvider.TryCreateQuickInfoSource(ITextBuffer textBuffer) diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/ProjectionBufferContent.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/ProjectionBufferContent.cs new file mode 100644 index 0000000000000..bfc9455ce11fc --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/ProjectionBufferContent.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Media; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Projection; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.QuickInfo +{ + /// + /// Creates quick info content out of the span of an existing snapshot. The span will be + /// used to create a projection buffer out that will then be displayed in the quick info + /// window. + /// + internal class ProjectionBufferContent : ForegroundThreadAffinitizedObject + { + private readonly ImmutableArray _spans; + private readonly IProjectionBufferFactoryService _projectionBufferFactoryService; + private readonly IEditorOptionsFactoryService _editorOptionsFactoryService; + private readonly ITextEditorFactoryService _textEditorFactoryService; + private readonly IContentType _contentType; + private readonly ITextViewRoleSet _roleSet; + + private ProjectionBufferContent( + ImmutableArray spans, + IProjectionBufferFactoryService projectionBufferFactoryService, + IEditorOptionsFactoryService editorOptionsFactoryService, + ITextEditorFactoryService textEditorFactoryService, + IContentType contentType = null, + ITextViewRoleSet roleSet = null) + { + _spans = spans; + _projectionBufferFactoryService = projectionBufferFactoryService; + _editorOptionsFactoryService = editorOptionsFactoryService; + _textEditorFactoryService = textEditorFactoryService; + _contentType = contentType; + _roleSet = roleSet ?? _textEditorFactoryService.NoRoles; + } + + public static ContentControl Create( + ImmutableArray spans, + IProjectionBufferFactoryService projectionBufferFactoryService, + IEditorOptionsFactoryService editorOptionsFactoryService, + ITextEditorFactoryService textEditorFactoryService, + IContentType contentType = null, + ITextViewRoleSet roleSet = null) + { + var content = new ProjectionBufferContent( + spans, + projectionBufferFactoryService, + editorOptionsFactoryService, + textEditorFactoryService, + contentType, + roleSet); + + return content.Create(); + } + + private ContentControl Create() + { + AssertIsForeground(); + + return new ViewHostingControl(CreateView, CreateBuffer); + } + + private IWpfTextView CreateView(ITextBuffer buffer) + { + var view = _textEditorFactoryService.CreateTextView(buffer, _roleSet); + + view.SizeToFit(); + view.Background = Brushes.Transparent; + + // Zoom out a bit to shrink the text. + view.ZoomLevel *= 0.75; + + return view; + } + + private IProjectionBuffer CreateBuffer() + { + return _projectionBufferFactoryService.CreateProjectionBufferWithoutIndentation( + _editorOptionsFactoryService.GlobalOptions, _contentType, _spans.ToArray()); + } + } +} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/QuickInfoDisplayPanel.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/QuickInfoDisplayPanel.cs index 52efcc81ac51a..dffba2c1788d0 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/QuickInfoDisplayPanel.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/QuickInfoDisplayPanel.cs @@ -1,77 +1,90 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Immutable; +using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; +using Microsoft.CodeAnalysis.QuickInfo; -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo +namespace Microsoft.CodeAnalysis.Editor.QuickInfo { internal class QuickInfoDisplayPanel : StackPanel { - internal TextBlock MainDescription { get; } - internal TextBlock Documentation { get; } - internal TextBlock TypeParameterMap { get; } - internal TextBlock AnonymousTypes { get; } - internal TextBlock UsageText { get; } - internal TextBlock ExceptionText { get; } + private ImmutableArray _textBlocks; + + internal TextBlock MainDescription => _textBlocks.FirstOrDefault(tb => tb.Kind == QuickInfoSectionKinds.Description)?.Block; + internal TextBlock Documentation => _textBlocks.FirstOrDefault(tb => tb.Kind == QuickInfoSectionKinds.DocumentationComments)?.Block; + internal TextBlock TypeParameterMap => _textBlocks.FirstOrDefault(tb => tb.Kind == QuickInfoSectionKinds.TypeParameters)?.Block; + internal TextBlock AnonymousTypes => _textBlocks.FirstOrDefault(tb => tb.Kind == QuickInfoSectionKinds.AnonymousTypes)?.Block; + internal TextBlock UsageText => _textBlocks.FirstOrDefault(tb => tb.Kind == QuickInfoSectionKinds.Usage)?.Block; + internal TextBlock ExceptionText => _textBlocks.FirstOrDefault(tb => tb.Kind == QuickInfoSectionKinds.Exception)?.Block; public QuickInfoDisplayPanel( FrameworkElement symbolGlyph, FrameworkElement warningGlyph, - FrameworkElement mainDescription, - FrameworkElement documentation, - FrameworkElement typeParameterMap, - FrameworkElement anonymousTypes, - FrameworkElement usageText, - FrameworkElement exceptionText) + ImmutableArray textBlocks, + FrameworkElement documentSpan) { - this.MainDescription = (TextBlock)mainDescription; - this.Documentation = (TextBlock)documentation; - this.TypeParameterMap = (TextBlock)typeParameterMap; - this.AnonymousTypes = (TextBlock)anonymousTypes; - this.UsageText = (TextBlock)usageText; - this.ExceptionText = (TextBlock)exceptionText; + _textBlocks = textBlocks; this.Orientation = Orientation.Vertical; - Border symbolGlyphBorder = null; + for (int i = 0; i < _textBlocks.Length; i++) + { + var tb = _textBlocks[i]; + if (i == 0) + { + this.Children.Add(AddGlyphs(tb.Block, symbolGlyph, warningGlyph)); + } + else + { + this.Children.Add(tb.Block); + } + } + + if (documentSpan != null) + { + this.Children.Add(documentSpan); + } + } + + private static FrameworkElement AddGlyphs(TextBlock tb, FrameworkElement symbolGlyph, FrameworkElement warningGlyph) + { + var panel = new DockPanel() + { + LastChildFill = true, + HorizontalAlignment = HorizontalAlignment.Stretch, + Background = Brushes.Transparent + }; + if (symbolGlyph != null) { symbolGlyph.Margin = new Thickness(1, 1, 3, 1); - symbolGlyphBorder = new Border() + var symbolGlyphBorder = new Border() { BorderThickness = new Thickness(0), BorderBrush = Brushes.Transparent, VerticalAlignment = VerticalAlignment.Top, Child = symbolGlyph }; + + panel.Children.Add(symbolGlyphBorder); } - mainDescription.Margin = new Thickness(1); + tb.Margin = new Thickness(1); var mainDescriptionBorder = new Border() { BorderThickness = new Thickness(0), BorderBrush = Brushes.Transparent, VerticalAlignment = VerticalAlignment.Center, - Child = mainDescription + Child = tb }; - var symbolGlyphAndMainDescriptionDock = new DockPanel() - { - LastChildFill = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - Background = Brushes.Transparent - }; - - if (symbolGlyphBorder != null) - { - symbolGlyphAndMainDescriptionDock.Children.Add(symbolGlyphBorder); - } - - symbolGlyphAndMainDescriptionDock.Children.Add(mainDescriptionBorder); + panel.Children.Add(mainDescriptionBorder); if (warningGlyph != null) { @@ -85,51 +98,24 @@ public QuickInfoDisplayPanel( Child = warningGlyph }; - symbolGlyphAndMainDescriptionDock.Children.Add(warningGlyphBorder); + panel.Children.Add(warningGlyphBorder); } - this.Children.Add(symbolGlyphAndMainDescriptionDock); - this.Children.Add(documentation); - this.Children.Add(usageText); - this.Children.Add(typeParameterMap); - this.Children.Add(anonymousTypes); - this.Children.Add(exceptionText); + return panel; } public override string ToString() { var sb = new StringBuilder(); - BuildStringFromInlineCollection(this.MainDescription.Inlines, sb); - - if (this.Documentation.Inlines.Count > 0) - { - sb.AppendLine(); - BuildStringFromInlineCollection(this.Documentation.Inlines, sb); - } - - if (this.TypeParameterMap.Inlines.Count > 0) + foreach (var tb in _textBlocks) { - sb.AppendLine(); - BuildStringFromInlineCollection(this.TypeParameterMap.Inlines, sb); - } - - if (this.AnonymousTypes.Inlines.Count > 0) - { - sb.AppendLine(); - BuildStringFromInlineCollection(this.AnonymousTypes.Inlines, sb); - } - - if (this.UsageText.Inlines.Count > 0) - { - sb.AppendLine(); - BuildStringFromInlineCollection(this.UsageText.Inlines, sb); - } + if (sb.Length > 0) + { + sb.AppendLine(); + } - if (this.ExceptionText.Inlines.Count > 0) - { - sb.AppendLine(); - BuildStringFromInlineCollection(this.ExceptionText.Inlines, sb); + BuildStringFromInlineCollection(tb.Block.Inlines, sb); } return sb.ToString(); diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/TextBlockElement.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/TextBlockElement.cs new file mode 100644 index 0000000000000..a463c38074cc3 --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/TextBlockElement.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Windows.Controls; + +namespace Microsoft.CodeAnalysis.Editor.QuickInfo +{ + /// + /// Holds the for a particular QuickInfoSectionKind. + /// + internal class TextBlockElement + { + public string Kind { get; } + public TextBlock Block { get; } + + public TextBlockElement(string kind, TextBlock block) + { + Kind = kind; + Block = block; + } + } +} diff --git a/src/EditorFeatures/Core.Wpf/Tags/DefaultImageMonikerService.cs b/src/EditorFeatures/Core.Wpf/Tags/DefaultImageMonikerService.cs index 338a74ea2fecb..7a2d5824040ac 100644 --- a/src/EditorFeatures/Core.Wpf/Tags/DefaultImageMonikerService.cs +++ b/src/EditorFeatures/Core.Wpf/Tags/DefaultImageMonikerService.cs @@ -2,7 +2,6 @@ using System.Collections.Immutable; using System.Composition; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Wpf; using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Imaging.Interop; @@ -16,7 +15,7 @@ internal class DefaultImageMonikerService : IImageMonikerService public bool TryGetImageMoniker(ImmutableArray tags, out ImageMoniker imageMoniker) { - var glyph = tags.GetGlyph(); + var glyph = tags.GetFirstGlyph(); // We can't do the compositing of these glyphs at the editor layer. So just map them // to the non-add versions. diff --git a/src/EditorFeatures/Core.Wpf/WpfClassificationExtensions.cs b/src/EditorFeatures/Core.Wpf/WpfClassificationExtensions.cs index fa3429ca14ce2..2de70c5af65d2 100644 --- a/src/EditorFeatures/Core.Wpf/WpfClassificationExtensions.cs +++ b/src/EditorFeatures/Core.Wpf/WpfClassificationExtensions.cs @@ -8,7 +8,6 @@ using System.Windows.Documents; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; using Roslyn.Utilities; @@ -78,12 +77,12 @@ public static TextBlock ToTextBlock( string classificationFormatMap = null, bool wrap = true) { - var textBlock = new TextBlock { TextWrapping = wrap ? TextWrapping.Wrap : TextWrapping.NoWrap, TextTrimming = wrap ? TextTrimming.None : TextTrimming.CharacterEllipsis }; + textBlock.SetDefaultTextProperties(formatMap); textBlock.Inlines.AddRange(inlines); diff --git a/src/EditorFeatures/Core/CommandHandlers/QuickInfoCommandHandlerAndSourceProvider.cs b/src/EditorFeatures/Core/CommandHandlers/QuickInfoCommandHandlerAndSourceProvider.cs index 412ea944ac401..c772c11b562c2 100644 --- a/src/EditorFeatures/Core/CommandHandlers/QuickInfoCommandHandlerAndSourceProvider.cs +++ b/src/EditorFeatures/Core/CommandHandlers/QuickInfoCommandHandlerAndSourceProvider.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.VisualStudio.Language.Intellisense; @@ -31,27 +30,23 @@ internal partial class QuickInfoCommandHandlerAndSourceProvider : { private readonly IAsynchronousOperationListener _listener; private readonly IIntelliSensePresenter _presenter; - private readonly IList> _providers; [ImportingConstructor] public QuickInfoCommandHandlerAndSourceProvider( [ImportMany] IEnumerable, OrderableMetadata>> presenters, - [ImportMany] IEnumerable> providers, IAsynchronousOperationListenerProvider listenerProvider) : this(ExtensionOrderer.Order(presenters).Select(lazy => lazy.Value).FirstOrDefault(), - providers, listenerProvider) + listenerProvider) { } // For testing purposes. public QuickInfoCommandHandlerAndSourceProvider( IIntelliSensePresenter presenter, - [ImportMany] IEnumerable> providers, IAsynchronousOperationListenerProvider listenerProvider) { - _providers = ExtensionOrderer.Order(providers); - _listener = listenerProvider.GetListener(FeatureAttribute.QuickInfo); _presenter = presenter; + _listener = listenerProvider.GetListener(FeatureAttribute.QuickInfo); } private bool TryGetController(EditorCommandArgs args, out Controller controller) @@ -75,7 +70,7 @@ private bool TryGetController(EditorCommandArgs args, out Controller controller) // TODO(cyrusn): If there are no presenters then we should not create a controller. // Otherwise we'll be affecting the user's typing and they'll have no idea why :) - controller = Controller.GetInstance(args, _presenter, _listener, _providers); + controller = Controller.GetInstance(args, _presenter, _listener); return true; } diff --git a/src/EditorFeatures/Core/Extensibility/QuickInfo/IDeferredQuickInfoContent.cs b/src/EditorFeatures/Core/Extensibility/QuickInfo/IDeferredQuickInfoContent.cs deleted file mode 100644 index 69ad57188d27c..0000000000000 --- a/src/EditorFeatures/Core/Extensibility/QuickInfo/IDeferredQuickInfoContent.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.CodeAnalysis.Editor -{ - /// - /// Interface to allow providers to return some sort of quick info content whose creation can be - /// deferred to a later point (and on the UI thread) - /// - internal interface IDeferredQuickInfoContent - { - } -} diff --git a/src/EditorFeatures/Core/Extensibility/QuickInfo/IQuickInfoPresenterSession.cs b/src/EditorFeatures/Core/Extensibility/QuickInfo/IQuickInfoPresenterSession.cs index fc35c9ef96080..19cb0d1b75d50 100644 --- a/src/EditorFeatures/Core/Extensibility/QuickInfo/IQuickInfoPresenterSession.cs +++ b/src/EditorFeatures/Core/Extensibility/QuickInfo/IQuickInfoPresenterSession.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.VisualStudio.Text; namespace Microsoft.CodeAnalysis.Editor diff --git a/src/EditorFeatures/Core/Extensibility/QuickInfo/IQuickInfoProvider.cs b/src/EditorFeatures/Core/Extensibility/QuickInfo/IQuickInfoProvider.cs deleted file mode 100644 index 367f8dc890cab..0000000000000 --- a/src/EditorFeatures/Core/Extensibility/QuickInfo/IQuickInfoProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.CodeAnalysis.Editor -{ - internal interface IQuickInfoProvider - { - Task GetItemAsync(Document document, int position, CancellationToken cancellationToken); - } -} diff --git a/src/EditorFeatures/Core/Extensibility/QuickInfo/PredefinedQuickInfoProviderNames.cs b/src/EditorFeatures/Core/Extensibility/QuickInfo/PredefinedQuickInfoSourceProviderNames.cs similarity index 59% rename from src/EditorFeatures/Core/Extensibility/QuickInfo/PredefinedQuickInfoProviderNames.cs rename to src/EditorFeatures/Core/Extensibility/QuickInfo/PredefinedQuickInfoSourceProviderNames.cs index 342eb1643e978..9cd67e58a1a5f 100644 --- a/src/EditorFeatures/Core/Extensibility/QuickInfo/PredefinedQuickInfoProviderNames.cs +++ b/src/EditorFeatures/Core/Extensibility/QuickInfo/PredefinedQuickInfoSourceProviderNames.cs @@ -2,10 +2,8 @@ namespace Microsoft.CodeAnalysis.Editor { - internal static class PredefinedQuickInfoProviderNames + internal static class PredefinedQuickInfoSourceProviderNames { public const string EventHookup = "Event Hookup QuickInfo Source"; - public const string Semantic = "Semantic Quick Info Provider"; - public const string Syntactic = "Syntactic Quick Info Provider"; } } diff --git a/src/EditorFeatures/Core/Extensibility/QuickInfo/QuickInfoItem.cs b/src/EditorFeatures/Core/Extensibility/QuickInfo/QuickInfoItem.cs deleted file mode 100644 index 6a280fbd70bf2..0000000000000 --- a/src/EditorFeatures/Core/Extensibility/QuickInfo/QuickInfoItem.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Editor -{ - internal class QuickInfoItem - { - public TextSpan TextSpan { get; } - public IDeferredQuickInfoContent Content { get; } - - public QuickInfoItem(TextSpan textSpan, IDeferredQuickInfoContent content) - { - this.TextSpan = textSpan; - this.Content = content; - } - } -} diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoUtilities.cs b/src/EditorFeatures/Core/Extensibility/QuickInfo/QuickInfoUtilities.cs similarity index 86% rename from src/Features/Core/Portable/QuickInfo/QuickInfoUtilities.cs rename to src/EditorFeatures/Core/Extensibility/QuickInfo/QuickInfoUtilities.cs index 3d260c9c9e7f9..3ebd994759a7d 100644 --- a/src/Features/Core/Portable/QuickInfo/QuickInfoUtilities.cs +++ b/src/EditorFeatures/Core/Extensibility/QuickInfo/QuickInfoUtilities.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.CodeAnalysis.QuickInfo +namespace Microsoft.CodeAnalysis.Editor { internal class QuickInfoUtilities { diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Controller.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Controller.cs index 4a76a11142e64..9ba2e16db02a6 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Controller.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Controller.cs @@ -1,13 +1,12 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Language.Intellisense; @@ -23,20 +22,16 @@ internal partial class Controller : AbstractController, Model, IQuickInfoPresenterSession, IQuickInfoSession> { private static readonly object s_quickInfoPropertyKey = new object(); - - private readonly IList> _allProviders; - private IList _providers; + private QuickInfoService _service; public Controller( ITextView textView, ITextBuffer subjectBuffer, IIntelliSensePresenter presenter, IAsynchronousOperationListener asyncListener, - IDocumentProvider documentProvider, - IList> allProviders) + IDocumentProvider documentProvider) : base(textView, subjectBuffer, presenter, asyncListener, documentProvider, "QuickInfo") { - _allProviders = allProviders; } // For testing purposes @@ -46,17 +41,16 @@ internal Controller( IIntelliSensePresenter presenter, IAsynchronousOperationListener asyncListener, IDocumentProvider documentProvider, - IList providers) + QuickInfoService service) : base(textView, subjectBuffer, presenter, asyncListener, documentProvider, "QuickInfo") { - _providers = providers; + _service = service; } internal static Controller GetInstance( EditorCommandArgs args, IIntelliSensePresenter presenter, - IAsynchronousOperationListener asyncListener, - IList> allProviders) + IAsynchronousOperationListener asyncListener) { var textView = args.TextView; var subjectBuffer = args.SubjectBuffer; @@ -64,8 +58,7 @@ internal static Controller GetInstance( (v, b) => new Controller(v, b, presenter, asyncListener, - new DocumentProvider(), - allProviders)); + new DocumentProvider())); } internal override void OnModelUpdated(Model modelOpt) @@ -81,7 +74,7 @@ internal override void OnModelUpdated(Model modelOpt) // We want the span to actually only go up to the caret. So get the expected span // and then update its end point accordingly. - var triggerSpan = modelOpt.GetCurrentSpanInSnapshot(quickInfoItem.TextSpan, this.SubjectBuffer.CurrentSnapshot); + var triggerSpan = modelOpt.GetCurrentSpanInSnapshot(quickInfoItem.Span, this.SubjectBuffer.CurrentSnapshot); var trackingSpan = triggerSpan.CreateTrackingSpan(SpanTrackingMode.EdgeInclusive); sessionOpt.PresenterSession.PresentItem(trackingSpan, quickInfoItem, modelOpt.TrackMouse); @@ -95,8 +88,8 @@ public void StartSession( { AssertIsForeground(); - var providers = GetProviders(); - if (providers == null) + var service = GetService(); + if (service == null) { return; } @@ -106,30 +99,30 @@ public void StartSession( this.Presenter.CreateSession(this.TextView, this.SubjectBuffer, augmentSession)); this.sessionOpt.Computation.ChainTaskAndNotifyControllerWhenFinished( - (model, cancellationToken) => ComputeModelInBackgroundAsync(position, snapshot, providers, trackMouse, cancellationToken)); + (model, cancellationToken) => ComputeModelInBackgroundAsync(position, snapshot, service, trackMouse, cancellationToken)); } - public IList GetProviders() + public QuickInfoService GetService() { this.AssertIsForeground(); - if (_providers == null) + if (_service == null) { var snapshot = this.SubjectBuffer.CurrentSnapshot; var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document != null) { - _providers = document.Project.LanguageServices.WorkspaceServices.SelectMatchingExtensionValues(_allProviders, this.SubjectBuffer.ContentType); + _service = QuickInfoService.GetService(document); } } - return _providers; + return _service; } private async Task ComputeModelInBackgroundAsync( int position, ITextSnapshot snapshot, - IList providers, + QuickInfoService service, bool trackMouse, CancellationToken cancellationToken) { @@ -147,15 +140,10 @@ private async Task ComputeModelInBackgroundAsync( return null; } - foreach (var provider in providers) + var item = await service.GetQuickInfoAsync(document, position, cancellationToken).ConfigureAwait(false); + if (item != null) { - // TODO(cyrusn): We're calling into extensions, we need to make ourselves resilient - // to the extension crashing. - var item = await provider.GetItemAsync(document, position, cancellationToken).ConfigureAwait(false); - if (item != null) - { - return new Model(snapshot.Version, item, provider, trackMouse); - } + return new Model(snapshot.Version, item, trackMouse); } return null; diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/ClassifiableDeferredContent.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/ClassifiableDeferredContent.cs deleted file mode 100644 index 3acc7484a20fc..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/ClassifiableDeferredContent.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo -{ - internal class ClassifiableDeferredContent : IDeferredQuickInfoContent - { - internal readonly IList ClassifiableContent; - - public ClassifiableDeferredContent( - IList content) - { - this.ClassifiableContent = content; - } - } -} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/ProjectionBufferDeferredContent.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/ProjectionBufferDeferredContent.cs deleted file mode 100644 index 92b4388b6d945..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/ProjectionBufferDeferredContent.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Utilities; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo -{ - /// - /// Creates quick info content out of the span of an existing snapshot. The span will be - /// used to create an projection buffer out that will then be displayed in the quick info - /// window. - /// - internal class ProjectionBufferDeferredContent : IDeferredQuickInfoContent - { - internal SnapshotSpan Span { get; } - internal IContentType ContentType { get; } - internal ITextViewRoleSet RoleSet { get; } - - public ProjectionBufferDeferredContent( - SnapshotSpan span, - IContentType contentType = null, - ITextViewRoleSet roleSet = null) - { - Span = span; - ContentType = contentType; - RoleSet = roleSet; - } - } -} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/QuickInfoDisplayDeferredContent.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/QuickInfoDisplayDeferredContent.cs deleted file mode 100644 index 4e79f18164aae..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/QuickInfoDisplayDeferredContent.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Windows; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo -{ - internal class QuickInfoDisplayDeferredContent : IDeferredQuickInfoContent - { - public IDeferredQuickInfoContent SymbolGlyph { get; } - public IDeferredQuickInfoContent MainDescription { get; } - public IDeferredQuickInfoContent Documentation { get; } - public IDeferredQuickInfoContent TypeParameterMap { get; } - public IDeferredQuickInfoContent AnonymousTypes { get; } - public IDeferredQuickInfoContent UsageText { get; } - public IDeferredQuickInfoContent ExceptionText { get; } - public IDeferredQuickInfoContent WarningGlyph { get; } - - public QuickInfoDisplayDeferredContent( - IDeferredQuickInfoContent symbolGlyph, - IDeferredQuickInfoContent warningGlyph, - IDeferredQuickInfoContent mainDescription, - IDeferredQuickInfoContent documentation, - IDeferredQuickInfoContent typeParameterMap, - IDeferredQuickInfoContent anonymousTypes, - IDeferredQuickInfoContent usageText, - IDeferredQuickInfoContent exceptionText) - { - SymbolGlyph = symbolGlyph; - WarningGlyph = warningGlyph; - MainDescription = mainDescription; - Documentation = documentation; - TypeParameterMap = typeParameterMap; - AnonymousTypes = anonymousTypes; - UsageText = usageText; - ExceptionText = exceptionText; - } - } -} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/SymbolGlyphDeferredContent.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/SymbolGlyphDeferredContent.cs deleted file mode 100644 index 2c550383f1a6d..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/DeferredContent/SymbolGlyphDeferredContent.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo -{ - internal class SymbolGlyphDeferredContent : IDeferredQuickInfoContent - { - internal Glyph Glyph { get; } - - public SymbolGlyphDeferredContent(Glyph glyph) - { - Glyph = glyph; - } - } -} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Model.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Model.cs index ba37c6ced2a6c..deeb51e7a1a0d 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Model.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Model.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; @@ -11,20 +12,17 @@ internal class Model { public ITextVersion TextVersion { get; } public QuickInfoItem Item { get; } - public IQuickInfoProvider Provider { get; } public bool TrackMouse { get; } public Model( ITextVersion textVersion, QuickInfoItem item, - IQuickInfoProvider provider, bool trackMouse) { Contract.ThrowIfNull(item); this.TextVersion = textVersion; this.Item = item; - this.Provider = provider; this.TrackMouse = trackMouse; } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractQuickInfoProvider.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractQuickInfoProvider.cs deleted file mode 100644 index 80332d6efda03..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractQuickInfoProvider.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Projection; -using Microsoft.VisualStudio.Utilities; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo -{ - internal abstract partial class AbstractQuickInfoProvider : IQuickInfoProvider - { - public async Task GetItemAsync( - Document document, - int position, - CancellationToken cancellationToken) - { - var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - var token = await tree.GetTouchingTokenAsync(position, cancellationToken, findInsideTrivia: true).ConfigureAwait(false); - - var state = await GetQuickInfoItemAsync(document, token, position, cancellationToken).ConfigureAwait(false); - if (state != null) - { - return state; - } - - if (ShouldCheckPreviousToken(token)) - { - var previousToken = token.GetPreviousToken(); - - if ((state = await GetQuickInfoItemAsync(document, previousToken, position, cancellationToken).ConfigureAwait(false)) != null) - { - return state; - } - } - - return null; - } - - protected virtual bool ShouldCheckPreviousToken(SyntaxToken token) - { - return true; - } - - private async Task GetQuickInfoItemAsync( - Document document, - SyntaxToken token, - int position, - CancellationToken cancellationToken) - { - if (token != default && - token.Span.IntersectsWith(position)) - { - var deferredContent = await BuildContentAsync(document, token, cancellationToken).ConfigureAwait(false); - if (deferredContent != null) - { - return new QuickInfoItem(token.Span, deferredContent); - } - } - - return null; - } - - protected abstract Task BuildContentAsync(Document document, SyntaxToken token, CancellationToken cancellationToken); - - protected IDeferredQuickInfoContent CreateQuickInfoDisplayDeferredContent( - ISymbol symbol, - bool showWarningGlyph, - bool showSymbolGlyph, - IList mainDescription, - IDeferredQuickInfoContent documentation, - IList typeParameterMap, - IList anonymousTypes, - IList usageText, - IList exceptionText) - { - return new QuickInfoDisplayDeferredContent( - symbolGlyph: showSymbolGlyph ? CreateGlyphDeferredContent(symbol) : null, - warningGlyph: showWarningGlyph ? CreateWarningGlyph() : null, - mainDescription: CreateClassifiableDeferredContent(mainDescription), - documentation: documentation, - typeParameterMap: CreateClassifiableDeferredContent(typeParameterMap), - anonymousTypes: CreateClassifiableDeferredContent(anonymousTypes), - usageText: CreateClassifiableDeferredContent(usageText), - exceptionText: CreateClassifiableDeferredContent(exceptionText)); - } - - private IDeferredQuickInfoContent CreateWarningGlyph() - { - return new SymbolGlyphDeferredContent(Glyph.CompletionWarning); - } - - protected IDeferredQuickInfoContent CreateQuickInfoDisplayDeferredContent( - Glyph glyph, - IList mainDescription, - IDeferredQuickInfoContent documentation, - IList typeParameterMap, - IList anonymousTypes, - IList usageText, - IList exceptionText) - { - return new QuickInfoDisplayDeferredContent( - symbolGlyph: new SymbolGlyphDeferredContent(glyph), - warningGlyph: null, - mainDescription: CreateClassifiableDeferredContent(mainDescription), - documentation: documentation, - typeParameterMap: CreateClassifiableDeferredContent(typeParameterMap), - anonymousTypes: CreateClassifiableDeferredContent(anonymousTypes), - usageText: CreateClassifiableDeferredContent(usageText), - exceptionText: CreateClassifiableDeferredContent(exceptionText)); - } - - protected IDeferredQuickInfoContent CreateGlyphDeferredContent(ISymbol symbol) - { - return new SymbolGlyphDeferredContent(symbol.GetGlyph()); - } - - protected IDeferredQuickInfoContent CreateClassifiableDeferredContent( - IList content) - { - return new ClassifiableDeferredContent(content); - } - - protected IDeferredQuickInfoContent CreateDocumentationCommentDeferredContent( - string documentationComment) - { - return new DocumentationCommentDeferredContent(documentationComment); - } - - protected IDeferredQuickInfoContent CreateProjectionBufferDeferredContent(SnapshotSpan span) - { - return new ProjectionBufferDeferredContent(span); - } - } -} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/DocumentationCommentDeferredContent.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/DocumentationCommentDeferredContent.cs deleted file mode 100644 index 87ab72481da85..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/DocumentationCommentDeferredContent.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo -{ - internal class DocumentationCommentDeferredContent : IDeferredQuickInfoContent - { - internal string DocumentationComment { get; } - - internal void WaitForDocumentationCommentTask_ForTestingPurposesOnly() - { - } - - public DocumentationCommentDeferredContent( - string documentationComment) - { - DocumentationComment = documentationComment; - } - - } -} diff --git a/src/EditorFeatures/Core/Shared/Extensions/GlyphExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/GlyphExtensions.cs deleted file mode 100644 index acd553625f92c..0000000000000 --- a/src/EditorFeatures/Core/Shared/Extensions/GlyphExtensions.cs +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Tags; - -namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions -{ - internal static class GlyphExtensions - { - public static Glyph GetGlyph(this ImmutableArray tags) - { - foreach (var tag in tags) - { - switch (tag) - { - case WellKnownTags.Assembly: - return Glyph.Assembly; - - case WellKnownTags.File: - return tags.Contains(LanguageNames.VisualBasic) ? Glyph.BasicFile : Glyph.CSharpFile; - - case WellKnownTags.Project: - return tags.Contains(LanguageNames.VisualBasic) ? Glyph.BasicProject : Glyph.CSharpProject; - - case WellKnownTags.Class: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.ClassProtected; - case Accessibility.Private: - return Glyph.ClassPrivate; - case Accessibility.Internal: - return Glyph.ClassInternal; - case Accessibility.Public: - default: - return Glyph.ClassPublic; - } - - case WellKnownTags.Constant: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.ConstantProtected; - case Accessibility.Private: - return Glyph.ConstantPrivate; - case Accessibility.Internal: - return Glyph.ConstantInternal; - case Accessibility.Public: - default: - return Glyph.ConstantPublic; - } - - case WellKnownTags.Delegate: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.DelegateProtected; - case Accessibility.Private: - return Glyph.DelegatePrivate; - case Accessibility.Internal: - return Glyph.DelegateInternal; - case Accessibility.Public: - default: - return Glyph.DelegatePublic; - } - - case WellKnownTags.Enum: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.EnumProtected; - case Accessibility.Private: - return Glyph.EnumPrivate; - case Accessibility.Internal: - return Glyph.EnumInternal; - case Accessibility.Public: - default: - return Glyph.EnumPublic; - } - - case WellKnownTags.EnumMember: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.EnumMemberProtected; - case Accessibility.Private: - return Glyph.EnumMemberPrivate; - case Accessibility.Internal: - return Glyph.EnumMemberInternal; - case Accessibility.Public: - default: - return Glyph.EnumMemberPublic; - } - - case WellKnownTags.Error: - return Glyph.Error; - - case WellKnownTags.Event: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.EventProtected; - case Accessibility.Private: - return Glyph.EventPrivate; - case Accessibility.Internal: - return Glyph.EventInternal; - case Accessibility.Public: - default: - return Glyph.EventPublic; - } - - case WellKnownTags.ExtensionMethod: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.ExtensionMethodProtected; - case Accessibility.Private: - return Glyph.ExtensionMethodPrivate; - case Accessibility.Internal: - return Glyph.ExtensionMethodInternal; - case Accessibility.Public: - default: - return Glyph.ExtensionMethodPublic; - } - - case WellKnownTags.Field: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.FieldProtected; - case Accessibility.Private: - return Glyph.FieldPrivate; - case Accessibility.Internal: - return Glyph.FieldInternal; - case Accessibility.Public: - default: - return Glyph.FieldPublic; - } - - case WellKnownTags.Interface: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.InterfaceProtected; - case Accessibility.Private: - return Glyph.InterfacePrivate; - case Accessibility.Internal: - return Glyph.InterfaceInternal; - case Accessibility.Public: - default: - return Glyph.InterfacePublic; - } - - case WellKnownTags.Intrinsic: - return Glyph.Intrinsic; - - case WellKnownTags.Keyword: - return Glyph.Keyword; - - case WellKnownTags.Label: - return Glyph.Label; - - case WellKnownTags.Local: - return Glyph.Local; - - case WellKnownTags.Namespace: - return Glyph.Namespace; - - case WellKnownTags.Method: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.MethodProtected; - case Accessibility.Private: - return Glyph.MethodPrivate; - case Accessibility.Internal: - return Glyph.MethodInternal; - case Accessibility.Public: - default: - return Glyph.MethodPublic; - } - - case WellKnownTags.Module: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.ModulePublic; - case Accessibility.Private: - return Glyph.ModulePrivate; - case Accessibility.Internal: - return Glyph.ModuleInternal; - case Accessibility.Public: - default: - return Glyph.ModulePublic; - } - - case WellKnownTags.Folder: - return Glyph.OpenFolder; - - case WellKnownTags.Operator: - return Glyph.Operator; - - case WellKnownTags.Parameter: - return Glyph.Parameter; - - case WellKnownTags.Property: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.PropertyProtected; - case Accessibility.Private: - return Glyph.PropertyPrivate; - case Accessibility.Internal: - return Glyph.PropertyInternal; - case Accessibility.Public: - default: - return Glyph.PropertyPublic; - } - - case WellKnownTags.RangeVariable: - return Glyph.RangeVariable; - - case WellKnownTags.Reference: - return Glyph.Reference; - - case WellKnownTags.NuGet: - return Glyph.NuGet; - - case WellKnownTags.Structure: - switch (GetAccessibility(tags)) - { - case Accessibility.Protected: - return Glyph.StructureProtected; - case Accessibility.Private: - return Glyph.StructurePrivate; - case Accessibility.Internal: - return Glyph.StructureInternal; - case Accessibility.Public: - default: - return Glyph.StructurePublic; - } - - case WellKnownTags.TypeParameter: - return Glyph.TypeParameter; - - case WellKnownTags.Snippet: - return Glyph.Snippet; - - case WellKnownTags.Warning: - return Glyph.CompletionWarning; - - case WellKnownTags.StatusInformation: - return Glyph.StatusInformation; - } - } - - return Glyph.None; - } - - private static Accessibility GetAccessibility(ImmutableArray tags) - { - if (tags.Contains(WellKnownTags.Public)) - { - return Accessibility.Public; - } - else if (tags.Contains(WellKnownTags.Protected)) - { - return Accessibility.Protected; - } - else if (tags.Contains(WellKnownTags.Internal)) - { - return Accessibility.Internal; - } - else if (tags.Contains(WellKnownTags.Private)) - { - return Accessibility.Private; - } - else - { - return Accessibility.NotApplicable; - } - } - } -} diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 317e4a1476ae4..7db41aec0938f 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -10,6 +10,7 @@ Imports Microsoft.CodeAnalysis.Editor.CSharp.Formatting Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.Tags Imports Microsoft.CodeAnalysis.Text Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor @@ -2383,7 +2384,7 @@ class C state.SendTypeChars("Thing1") Await state.WaitForAsynchronousOperationsAsync() Await state.AssertSelectedCompletionItem("Thing1") - Assert.True(state.CurrentCompletionPresenterSession.SelectedItem.Tags.Contains(CompletionTags.Warning)) + Assert.True(state.CurrentCompletionPresenterSession.SelectedItem.Tags.Contains(WellKnownTags.Warning)) state.SendBackspace() state.SendBackspace() state.SendBackspace() @@ -2393,7 +2394,7 @@ class C state.SendTypeChars("M") Await state.WaitForAsynchronousOperationsAsync() Await state.AssertSelectedCompletionItem("M") - Assert.False(state.CurrentCompletionPresenterSession.SelectedItem.Tags.Contains(CompletionTags.Warning)) + Assert.False(state.CurrentCompletionPresenterSession.SelectedItem.Tags.Contains(WellKnownTags.Warning)) End Using End Function diff --git a/src/EditorFeatures/Test2/IntelliSense/QuickInfoControllerTests.vb b/src/EditorFeatures/Test2/IntelliSense/QuickInfoControllerTests.vb index e99aed1995dbc..12c9342cae79a 100644 --- a/src/EditorFeatures/Test2/IntelliSense/QuickInfoControllerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/QuickInfoControllerTests.vb @@ -1,19 +1,20 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports System.Runtime.CompilerServices Imports System.Threading Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo -Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo.Presentation -Imports Microsoft.CodeAnalysis.Editor.QuickInfo +Imports Microsoft.CodeAnalysis.Editor.QuickInfo.Presentation Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.QuickInfo Imports Microsoft.CodeAnalysis.Shared.TestHooks Imports Microsoft.VisualStudio.Language.Intellisense Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.Utilities Imports Moq +Imports Roslyn.Utilities +Imports QuickInfoItem = Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem #Disable Warning BC40000 ' IQuickInfo* is obsolete Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense @@ -24,128 +25,162 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense TestWorkspace.ResetThreadAffinity() End Sub - + Public Sub InvokeQuickInfoWithoutDocumentShouldNotQueryProviders() - Dim emptyProvider = New Mock(Of IDocumentProvider) - emptyProvider.Setup(Function(p) p.GetDocumentAsync(It.IsAny(Of ITextSnapshot), It.IsAny(Of CancellationToken))).Returns(Task.FromResult(DirectCast(Nothing, Document))) - Dim controller As Controller = CreateController(documentProvider:=emptyProvider, triggerQuickInfo:=True) - controller.WaitForController() + Dim mocks = CreateMocks(noDocument:=True, triggerQuickInfo:=True) + mocks.WaitForController() - GetMocks(controller).Provider.Verify(Function(p) p.GetItemAsync(It.IsAny(Of Document), It.IsAny(Of Integer), It.IsAny(Of CancellationToken)), Times.Never) + Assert.Equal(0, mocks.Service.GetQuickInfoAsync_InvokeCount) End Sub - + Public Sub InvokeQuickInfoWithDocumentShouldStartNewSession() - Dim controller = CreateController(triggerQuickInfo:=True) + Dim mocks = CreateMocks(triggerQuickInfo:=True) - GetMocks(controller).Presenter.Verify(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession)), Times.Once) + mocks.PresenterMock _ + .Verify( + Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession)), + Times.Once) End Sub - + Public Sub InactiveQuickInfoShouldNotHandleEscape() - Dim controller As Controller = CreateController() + Dim mocks = CreateMocks() - Dim handled = controller.TryHandleEscapeKey() + Dim handled = mocks.TryHandleEscapeKey() Assert.False(handled) End Sub - + Public Sub ActiveQuickInfoShouldHandleEscape() Dim presenter = New Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) Dim presenterSession = New Mock(Of IQuickInfoPresenterSession) - presenter.Setup(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession))).Returns(presenterSession.Object) + presenter _ + .Setup(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession))) _ + .Returns(presenterSession.Object) - Dim controller As Controller = CreateController(presenter:=presenter, triggerQuickInfo:=True) - controller.WaitForController() + Dim mocks = CreateMocks(presenter:=presenter, triggerQuickInfo:=True) - Dim handled = controller.TryHandleEscapeKey() + Dim emptyQuickInfoItem = QuickInfoItem.Create(New Text.TextSpan(0, 0)) + mocks.Service.SetItemToReturn(emptyQuickInfoItem) + + mocks.WaitForController() + + Dim handled = mocks.TryHandleEscapeKey() Assert.True(handled) presenterSession.Verify(Sub(p) p.Dismiss(), Times.Once) End Sub - + Public Sub SlowQuickInfoShouldDismissSessionButNotHandleEscape() - Dim checkpoint As New Checkpoint Dim presenter = New Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) Dim presenterSession = New Mock(Of IQuickInfoPresenterSession) - presenter.Setup(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession))).Returns(presenterSession.Object) - Dim provider = New Mock(Of IQuickInfoProvider) - provider.Setup(Function(p) p.GetItemAsync(It.IsAny(Of Document), It.IsAny(Of Integer), It.IsAny(Of CancellationToken))) _ - .Returns(Async Function() - Await checkpoint.Task.ConfigureAwait(False) - Return New QuickInfoItem(Nothing, Nothing) - End Function) + presenter _ + .Setup(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession))) _ + .Returns(presenterSession.Object) + + Dim mocks = CreateMocks(presenter:=presenter, triggerQuickInfo:=True) - Dim controller As Controller = CreateController(presenter:=presenter, provider:=provider, triggerQuickInfo:=True) + Dim checkpoint = New Checkpoint() + mocks.Service.SetCheckpoint(checkpoint) - Dim handled = controller.TryHandleEscapeKey() + Dim handled = mocks.TryHandleEscapeKey() Assert.False(handled) presenterSession.Verify(Sub(p) p.Dismiss(), Times.Once) End Sub - + Public Sub CaretPositionChangedShouldDismissSession() Dim presenter = New Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) Dim presenterSession = New Mock(Of IQuickInfoPresenterSession) - presenter.Setup(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession))).Returns(presenterSession.Object) - Dim controller = CreateController(presenter:=presenter, triggerQuickInfo:=True) + presenter _ + .Setup(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession))) _ + .Returns(presenterSession.Object) - Mock.Get(GetMocks(controller).View.Object.Caret).Raise(Sub(c) AddHandler c.PositionChanged, Nothing, New CaretPositionChangedEventArgs(Nothing, Nothing, Nothing)) + Dim mocks = CreateMocks(presenter:=presenter, triggerQuickInfo:=True) + + Mock.Get(mocks.View.Caret) _ + .Raise(Sub(c) AddHandler c.PositionChanged, Nothing, New CaretPositionChangedEventArgs(Nothing, Nothing, Nothing)) presenterSession.Verify(Sub(p) p.Dismiss(), Times.Once) End Sub - + Public Sub SubjectBufferPostChangedShouldDismissSession() Dim presenter = New Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) Dim presenterSession = New Mock(Of IQuickInfoPresenterSession) - presenter.Setup(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession))).Returns(presenterSession.Object) - Dim controller = CreateController(presenter:=presenter, triggerQuickInfo:=True) + presenter _ + .Setup(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of IQuickInfoSession))) _ + .Returns(presenterSession.Object) + + Dim mocks = CreateMocks(presenter:=presenter, triggerQuickInfo:=True) - Mock.Get(GetMocks(controller).View.Object.TextBuffer).Raise(Sub(b) AddHandler b.PostChanged, Nothing, New EventArgs()) + Mock.Get(mocks.View.TextBuffer) _ + .Raise(Sub(b) AddHandler b.PostChanged, Nothing, New EventArgs()) presenterSession.Verify(Sub(p) p.Dismiss(), Times.Once) End Sub - + + Public Sub PresenterUpdatesExistingSessionIfNotDismissed() Dim broker = New Mock(Of IQuickInfoBroker)() - Dim frameworkElementFactory = New DeferredContentFrameworkElementFactory({}, {}) - Dim presenter As IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession) = New QuickInfoPresenter(broker.Object, frameworkElementFactory) - Dim mockEditorSession = New Mock(Of IQuickInfoSession) - mockEditorSession.Setup(Function(m) m.IsDismissed).Returns(False) - mockEditorSession.Setup(Sub(m) m.Recalculate()) - mockEditorSession.Setup(Function(m) m.Properties).Returns(New PropertyCollection()) + Dim presenter As IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession) = + New QuickInfoPresenter(broker.Object, Nothing, Nothing, Nothing, Nothing, Nothing) - Dim presenterSession = presenter.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), mockEditorSession.Object) + Dim quickInfoSession = New Mock(Of IQuickInfoSession) + With quickInfoSession + .Setup(Function(m) m.IsDismissed).Returns(False) + .Setup(Sub(m) m.Recalculate()) + .Setup(Function(m) m.Properties).Returns(New PropertyCollection()) + End With + + Dim presenterSession = presenter.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), quickInfoSession.Object) presenterSession.PresentItem(Nothing, Nothing, False) - mockEditorSession.Verify(Sub(m) m.Recalculate()) + quickInfoSession.Verify(Sub(m) m.Recalculate()) End Sub - + Public Sub PresenterDoesNotRecalculateDismissedSession() + Dim quickInfoSession = New Mock(Of IQuickInfoSession)() + quickInfoSession _ + .Setup(Function(m) m.Properties) _ + .Returns(New PropertyCollection()) + Dim broker = New Mock(Of IQuickInfoBroker)() - Dim brokerSession = New Mock(Of IQuickInfoSession)() - brokerSession.Setup(Function(m) m.Properties).Returns(New PropertyCollection()) - broker.Setup(Function(m) m.CreateQuickInfoSession(It.IsAny(Of ITextView), It.IsAny(Of ITrackingPoint), It.IsAny(Of Boolean))).Returns(brokerSession.Object) + broker _ + .Setup(Function(m) m.CreateQuickInfoSession(It.IsAny(Of ITextView), It.IsAny(Of ITrackingPoint), It.IsAny(Of Boolean))) _ + .Returns(quickInfoSession.Object) + + Dim presenter As IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession) = + New QuickInfoPresenter(broker.Object, Nothing, Nothing, Nothing, Nothing, Nothing) - Dim frameworkElementFactory = New DeferredContentFrameworkElementFactory({}, {}) - Dim presenter As IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession) = New QuickInfoPresenter(broker.Object, frameworkElementFactory) Dim mockEditorSession = New Mock(Of IQuickInfoSession) - mockEditorSession.Setup(Function(m) m.IsDismissed).Returns(True) - mockEditorSession.Setup(Sub(m) m.Recalculate()) - mockEditorSession.Setup(Function(m) m.Properties).Returns(New PropertyCollection()) + With mockEditorSession + .Setup(Function(m) m.IsDismissed) _ + .Returns(True) + .Setup(Sub(m) m.Recalculate()) + .Setup(Function(m) m.Properties) _ + .Returns(New PropertyCollection()) + End With Dim mockTextBuffer = New Mock(Of ITextBuffer)() - mockTextBuffer.Setup(Function(m) m.CurrentSnapshot).Returns(It.IsAny(Of ITextSnapshot)) + mockTextBuffer _ + .Setup(Function(m) m.CurrentSnapshot) _ + .Returns(It.IsAny(Of ITextSnapshot)) Dim mockTrackingSpan = New Mock(Of ITrackingSpan) - mockTrackingSpan.Setup(Function(m) m.TextBuffer).Returns(mockTextBuffer.Object) - mockTrackingSpan.Setup(Function(m) m.GetSpan(It.IsAny(Of ITextSnapshot))).Returns(New SnapshotSpan()) - mockTrackingSpan.Setup(Function(m) m.GetStartPoint(It.IsAny(Of ITextSnapshot))).Returns(New SnapshotPoint(New Mock(Of ITextSnapshot)().Object, 0)) + With mockTrackingSpan + .Setup(Function(m) m.TextBuffer) _ + .Returns(mockTextBuffer.Object) + .Setup(Function(m) m.GetSpan(It.IsAny(Of ITextSnapshot))) _ + .Returns(New SnapshotSpan()) + .Setup(Function(m) m.GetStartPoint(It.IsAny(Of ITextSnapshot))) _ + .Returns(New SnapshotPoint(New Mock(Of ITextSnapshot)().Object, 0)) + End With Dim presenterSession = presenter.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), mockEditorSession.Object) presenterSession.PresentItem(mockTrackingSpan.Object, Nothing, False) @@ -166,33 +201,103 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense End Function)() Private Shared ReadOnly s_bufferFactory As ITextBufferFactoryService = DirectCast(s_document.Project.Solution.Workspace, TestWorkspace).GetService(Of ITextBufferFactoryService) - Private Shared ReadOnly s_controllerMocksMap As New ConditionalWeakTable(Of Controller, ControllerMocks) - Private Shared Function GetMocks(controller As Controller) As ControllerMocks - Dim result As ControllerMocks = Nothing - Roslyn.Utilities.Contract.ThrowIfFalse(s_controllerMocksMap.TryGetValue(controller, result)) - Return result - End Function + Private Class QuickInfoMocks + Private ReadOnly _controller As Controller + Private ReadOnly _viewMock As Mock(Of ITextView) + + Public ReadOnly Property PresenterMock As Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) + + Public ReadOnly Property Service As MockQuickInfoService + + Public ReadOnly Property View As ITextView + Get + Return _viewMock.Object + End Get + End Property + + Public Sub New( + controller As Controller, + service As MockQuickInfoService, + viewMock As Mock(Of ITextView), + presenterMock As Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession))) + + _controller = controller + Me.Service = service + _viewMock = viewMock + Me.PresenterMock = presenterMock + End Sub + + Public Sub WaitForController() + _controller.WaitForController() + End Sub + + Public Function TryHandleEscapeKey() As Boolean + Return _controller.TryHandleEscapeKey() + End Function + End Class + + Private Class MockQuickInfoService + Inherits QuickInfoService + + Private _item As QuickInfoItem + Private _checkpoint As Checkpoint + + Public GetQuickInfoAsync_InvokeCount As Integer + + Public Sub SetItemToReturn(item As QuickInfoItem) + _item = item + End Sub + + Public Sub SetCheckpoint(checkpoint As Checkpoint) + _checkpoint = checkpoint + End Sub + + Public Overrides Async Function GetQuickInfoAsync( + document As Document, + position As Integer, + Optional cancellationToken As CancellationToken = Nothing + ) As Task(Of QuickInfoItem) + + If _checkpoint IsNot Nothing Then + Await _checkpoint.Task.ConfigureAwait(False) + End If + + GetQuickInfoAsync_InvokeCount += 1 + Return _item + End Function + End Class + + Private Shared Function CreateMocks( + Optional presenter As Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) = Nothing, + Optional noDocument As Boolean = False, + Optional triggerQuickInfo As Boolean = False, + Optional augmentSession As IQuickInfoSession = Nothing + ) As QuickInfoMocks + + Dim view = New Mock(Of ITextView) With { + .DefaultValue = DefaultValue.Mock + } - Private Shared Function CreateController(Optional documentProvider As Mock(Of IDocumentProvider) = Nothing, - Optional presenter As Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) = Nothing, - Optional provider As Mock(Of IQuickInfoProvider) = Nothing, - Optional triggerQuickInfo As Boolean = False, - Optional augmentSession As IQuickInfoSession = Nothing) As Controller - Dim view = New Mock(Of ITextView) With {.DefaultValue = DefaultValue.Mock} Dim asyncListener = New Mock(Of IAsynchronousOperationListener) Dim buffer = s_bufferFactory.CreateTextBuffer() - presenter = If(presenter, New Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) With {.DefaultValue = DefaultValue.Mock}) - If documentProvider Is Nothing Then - documentProvider = New Mock(Of IDocumentProvider) - documentProvider.Setup(Function(p) p.GetDocumentAsync(It.IsAny(Of ITextSnapshot), It.IsAny(Of CancellationToken))).Returns(Task.FromResult(s_document)) + If presenter Is Nothing Then + presenter = New Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) With { + .DefaultValue = DefaultValue.Mock + } End If - If provider Is Nothing Then - provider = New Mock(Of IQuickInfoProvider) - provider.Setup(Function(p) p.GetItemAsync(It.IsAny(Of Document), It.IsAny(Of Integer), It.IsAny(Of CancellationToken))) _ - .Returns(Task.FromResult(New QuickInfoItem(Nothing, Nothing))) - End If + Dim documentTask = If( + noDocument, + SpecializedTasks.Default(Of Document), + Task.FromResult(s_document)) + + Dim documentProvider = New Mock(Of IDocumentProvider) + documentProvider _ + .Setup(Function(p) p.GetDocumentAsync(It.IsAny(Of ITextSnapshot), It.IsAny(Of CancellationToken))) _ + .Returns(documentTask) + + Dim service = New MockQuickInfoService() Dim controller = New Controller( view.Object, @@ -200,51 +305,17 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense presenter.Object, asyncListener.Object, documentProvider.Object, - {provider.Object}) + service) - s_controllerMocksMap.Add(controller, New ControllerMocks( - view, - buffer, - presenter, - asyncListener, - documentProvider, - provider)) + Dim mocks = New QuickInfoMocks(controller, service, view, presenter) If triggerQuickInfo Then - controller.InvokeQuickInfo(position:=0, trackMouse:=False, augmentSession:=augmentSession) + controller.InvokeQuickInfo(position:=0, trackMouse:=False, augmentSession) End If - Return controller - End Function - - Private Shared Function CreateMock(Of T As Class)() As T - Dim mock = New Mock(Of T) - Return mock.Object + Return mocks End Function - Private Class ControllerMocks - Public ReadOnly View As Mock(Of ITextView) - Public ReadOnly Buffer As ITextBuffer - Public ReadOnly Presenter As Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)) - Public ReadOnly AsyncListener As Mock(Of IAsynchronousOperationListener) - Public ReadOnly DocumentProvider As Mock(Of IDocumentProvider) - Public ReadOnly Provider As Mock(Of IQuickInfoProvider) - - Public Sub New(view As Mock(Of ITextView), - buffer As ITextBuffer, - presenter As Mock(Of IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession)), - asyncListener As Mock(Of IAsynchronousOperationListener), - documentProvider As Mock(Of IDocumentProvider), provider As Mock(Of IQuickInfoProvider)) - Me.View = view - Me.Buffer = buffer - Me.Presenter = presenter - Me.AsyncListener = asyncListener - Me.DocumentProvider = documentProvider - Me.Provider = provider - End Sub - - - End Class End Class End Namespace #Enable Warning BC40000 ' IQuickInfo* is obsolete diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb index 649ba1bba5acf..fc9cf039e524a 100644 --- a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb @@ -6,6 +6,7 @@ Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Snippets +Imports Microsoft.CodeAnalysis.Tags Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Operations @@ -2141,7 +2142,7 @@ End Class Await state.WaitForAsynchronousOperationsAsync() ' Should only have one item called 'Double' and it should have a keyword glyph Dim doubleItem = state.CurrentCompletionPresenterSession.CompletionItems.Single(Function(c) c.DisplayText = "Double") - Assert.True(doubleItem.Tags.Contains(CompletionTags.Keyword)) + Assert.True(doubleItem.Tags.Contains(WellKnownTags.Keyword)) End Using End Function @@ -2162,8 +2163,8 @@ End Class ' We should have gotten the item corresponding to [Double] and the item for the Double keyword Dim doubleItems = state.CurrentCompletionPresenterSession.CompletionItems.Where(Function(c) c.DisplayText = "Double") Assert.Equal(2, doubleItems.Count()) - Assert.True(doubleItems.Any(Function(c) c.Tags.Contains(CompletionTags.Keyword))) - Assert.True(doubleItems.Any(Function(c) c.Tags.Contains(CompletionTags.Class) AndAlso c.Tags.Contains(CompletionTags.Internal))) + Assert.True(doubleItems.Any(Function(c) c.Tags.Contains(WellKnownTags.Keyword))) + Assert.True(doubleItems.Any(Function(c) c.Tags.Contains(WellKnownTags.Class) AndAlso c.Tags.Contains(WellKnownTags.Internal))) End Using End Function diff --git a/src/EditorFeatures/TestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/TestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs index b1e0cc0e0d70b..4e65b6269a0ac 100644 --- a/src/EditorFeatures/TestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs +++ b/src/EditorFeatures/TestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs @@ -200,7 +200,7 @@ protected async Task TestAddDocument( { var codeActions = await GetCodeActionsAsync(workspace, parameters); await TestAddDocument( - workspace, expectedMarkup, index, expectedContainers, + workspace, expectedMarkup, index, expectedContainers, expectedDocumentName, codeActions); } } @@ -344,7 +344,7 @@ private async Task TestAsync( TestParameters parameters) { MarkupTestFile.GetSpans( - expectedMarkup.NormalizeLineEndings(), + expectedMarkup.NormalizeLineEndings(), out var expected, out IDictionary> spanMap); var conflictSpans = spanMap.GetOrAdd("Conflict", _ => ImmutableArray.Empty); diff --git a/src/EditorFeatures/TestUtilities/QuickInfo/AbstractQuickInfoSourceTests.cs b/src/EditorFeatures/TestUtilities/QuickInfo/AbstractQuickInfoSourceTests.cs index e68d14a46d754..9d6c7dedcd294 100644 --- a/src/EditorFeatures/TestUtilities/QuickInfo/AbstractQuickInfoSourceTests.cs +++ b/src/EditorFeatures/TestUtilities/QuickInfo/AbstractQuickInfoSourceTests.cs @@ -4,9 +4,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Text; -using Roslyn.Test.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.UnitTests.QuickInfo diff --git a/src/EditorFeatures/TestUtilities/QuickInfo/AbstractSemanticQuickInfoSourceTests.cs b/src/EditorFeatures/TestUtilities/QuickInfo/AbstractSemanticQuickInfoSourceTests.cs index d9901335419b0..153db9b116e36 100644 --- a/src/EditorFeatures/TestUtilities/QuickInfo/AbstractSemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/TestUtilities/QuickInfo/AbstractSemanticQuickInfoSourceTests.cs @@ -1,13 +1,13 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; -using System.Windows.Controls; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; using Microsoft.CodeAnalysis.Editor.UnitTests.Classification; using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.QuickInfo; using Xunit; namespace Microsoft.CodeAnalysis.Editor.UnitTests.QuickInfo @@ -160,132 +160,95 @@ protected Tuple[] NoClassifications() return null; } - private static void AssertTextAndClassifications(string expectedText, Tuple[] expectedClassifications, IDeferredQuickInfoContent actualContent) + internal Action SymbolGlyph(Glyph expectedGlyph) { - var actualClassifications = ((ClassifiableDeferredContent)actualContent).ClassifiableContent; - - ClassificationTestHelper.VerifyTextAndClassifications(expectedText, expectedClassifications, actualClassifications); + return qi => + { + Assert.Contains(expectedGlyph, qi.Tags.GetGlyphs()); + }; } - protected void WaitForDocumentationComment(object content) + internal Action WarningGlyph(Glyph expectedGlyph) { - if (content is QuickInfoDisplayDeferredContent deferredContent) - { - if (deferredContent.Documentation is DocumentationCommentDeferredContent docCommentDeferredContent) - { - docCommentDeferredContent.WaitForDocumentationCommentTask_ForTestingPurposesOnly(); - } - } + return SymbolGlyph(expectedGlyph); } - internal Action SymbolGlyph(Glyph expectedGlyph) + internal void AssertSection( + string expectedText, + ImmutableArray sections, + string textBlockKind, + Tuple[] expectedClassifications = null) { - return content => - { - var actualIcon = (SymbolGlyphDeferredContent)((QuickInfoDisplayDeferredContent)content).SymbolGlyph; - Assert.Equal(expectedGlyph, actualIcon.Glyph); - }; + var textBlock = sections.FirstOrDefault(tb => tb.Kind == textBlockKind); + var text = textBlock != null ? textBlock.TaggedParts : ImmutableArray.Empty; + AssertTaggedText(expectedText, text, expectedClassifications); } - protected Action MainDescription( + protected void AssertTaggedText( string expectedText, + ImmutableArray taggedText, Tuple[] expectedClassifications = null) { - return content => - { - switch (content) - { - case QuickInfoDisplayDeferredContent qiContent: - { - AssertTextAndClassifications(expectedText, expectedClassifications, (ClassifiableDeferredContent)qiContent.MainDescription); - } - break; - - case ClassifiableDeferredContent classifiable: - { - var actualContent = classifiable.ClassifiableContent; - ClassificationTestHelper.VerifyTextAndClassifications(expectedText, expectedClassifications, actualContent); - } - break; - } - }; + var actualText = string.Concat(taggedText.Select(tt => tt.Text)); + Assert.Equal(expectedText, actualText); } - protected Action Documentation( + protected Action MainDescription( string expectedText, Tuple[] expectedClassifications = null) { - return content => - { - var documentationCommentContent = ((QuickInfoDisplayDeferredContent)content).Documentation; - switch (documentationCommentContent) - { - case DocumentationCommentDeferredContent docComment: - { - Assert.Equal(expectedText, docComment.DocumentationComment); - } - break; - - case ClassifiableDeferredContent classifiable: - { - var actualContent = classifiable.ClassifiableContent; - Assert.Equal(expectedText, actualContent.GetFullText()); - ClassificationTestHelper.VerifyTextAndClassifications(expectedText, expectedClassifications, actualContent); - } - break; - } - }; + return item => AssertSection(expectedText, item.Sections, QuickInfoSectionKinds.Description, expectedClassifications); } - protected Action TypeParameterMap( + protected Action Documentation( string expectedText, Tuple[] expectedClassifications = null) { - return content => - { - AssertTextAndClassifications(expectedText, expectedClassifications, ((QuickInfoDisplayDeferredContent)content).TypeParameterMap); - }; + return item => AssertSection(expectedText, item.Sections, QuickInfoSectionKinds.DocumentationComments, expectedClassifications); } - protected Action AnonymousTypes( + protected Action TypeParameterMap( string expectedText, Tuple[] expectedClassifications = null) { - return content => - { - AssertTextAndClassifications(expectedText, expectedClassifications, ((QuickInfoDisplayDeferredContent)content).AnonymousTypes); - }; + return item => AssertSection(expectedText, item.Sections, QuickInfoSectionKinds.TypeParameters, expectedClassifications); } + protected Action AnonymousTypes( + string expectedText, + Tuple[] expectedClassifications = null) + { + return item => AssertSection(expectedText, item.Sections, QuickInfoSectionKinds.AnonymousTypes, expectedClassifications); + } - protected Action NoTypeParameterMap + protected Action NoTypeParameterMap { get { - return content => - { - AssertTextAndClassifications("", NoClassifications(), ((QuickInfoDisplayDeferredContent)content).TypeParameterMap); - }; + return item => AssertSection(string.Empty, item.Sections, QuickInfoSectionKinds.TypeParameters); } } - protected Action Usage(string expectedText, bool expectsWarningGlyph = false) + protected Action Usage(string expectedText, bool expectsWarningGlyph = false) { - return content => + return item => { - var quickInfoContent = (QuickInfoDisplayDeferredContent)content; - Assert.Equal(expectedText, ((ClassifiableDeferredContent)quickInfoContent.UsageText).ClassifiableContent.GetFullText()); - var warningGlyph = quickInfoContent.WarningGlyph as SymbolGlyphDeferredContent; - Assert.Equal(expectsWarningGlyph, warningGlyph != null && warningGlyph.Glyph == Glyph.CompletionWarning); + AssertSection(expectedText, item.Sections, QuickInfoSectionKinds.Usage); + + if (expectsWarningGlyph) + { + WarningGlyph(Glyph.CompletionWarning)(item); + } + else + { + Assert.DoesNotContain(Glyph.CompletionWarning, item.Tags.GetGlyphs()); + } }; } - protected Action Exceptions(string expectedText) + protected Action Exceptions(string expectedText) { - return content => - { - AssertTextAndClassifications(expectedText, expectedClassifications: null, actualContent: ((QuickInfoDisplayDeferredContent)content).ExceptionText); - }; + return item => AssertSection(expectedText, item.Sections, QuickInfoSectionKinds.Exception); } protected static async Task CanUseSpeculativeSemanticModelAsync(Document document, int position) @@ -296,6 +259,6 @@ protected static async Task CanUseSpeculativeSemanticModelAsync(Document d return !service.GetMemberBodySpanForSpeculativeBinding(node).IsEmpty; } - protected abstract Task TestAsync(string markup, params Action[] expectedResults); + protected abstract Task TestAsync(string markup, params Action[] expectedResults); } } diff --git a/src/EditorFeatures/VisualBasic/VBEditorResources.Designer.vb b/src/EditorFeatures/VisualBasic/VBEditorResources.Designer.vb index 3bebcbb15a747..335caf12597cf 100644 --- a/src/EditorFeatures/VisualBasic/VBEditorResources.Designer.vb +++ b/src/EditorFeatures/VisualBasic/VBEditorResources.Designer.vb @@ -195,15 +195,6 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.VBEditorResources End Get End Property - ''' - ''' Looks up a localized string similar to <Multiple Types>. - ''' - Friend ReadOnly Property Multiple_Types() As String - Get - Return ResourceManager.GetString("Multiple_Types", resourceCulture) - End Get - End Property - ''' ''' Looks up a localized string similar to New. ''' diff --git a/src/EditorFeatures/VisualBasic/VBEditorResources.resx b/src/EditorFeatures/VisualBasic/VBEditorResources.resx index 69fbd33ec491b..4766b13d19381 100644 --- a/src/EditorFeatures/VisualBasic/VBEditorResources.resx +++ b/src/EditorFeatures/VisualBasic/VBEditorResources.resx @@ -168,9 +168,6 @@ Visual Basic Pretty List - - <Multiple Types> - not supported diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.cs.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.cs.xlf index a631e41c61da7..6e670f43ef2aa 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.cs.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.cs.xlf @@ -87,11 +87,6 @@ Visual Basic formátování typu Pretty List - - <Multiple Types> - <Víc typů> - - not supported nepodporované diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.de.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.de.xlf index 775c0515f1e39..3b0665fdbf556 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.de.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.de.xlf @@ -87,11 +87,6 @@ Visual Basic - Automatische Strukturierung - - <Multiple Types> - <Mehrere Typen> - - not supported nicht unterstützt diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.es.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.es.xlf index 1ff39d61622a2..3228e13eed6a7 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.es.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.es.xlf @@ -87,11 +87,6 @@ Lista descriptiva de Visual Basic - - <Multiple Types> - <Varios tipos> - - not supported no compatible diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.fr.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.fr.xlf index f387d97bd7d2c..659f754c5d0f1 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.fr.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.fr.xlf @@ -87,11 +87,6 @@ Liste automatique Visual Basic - - <Multiple Types> - <Plusieurs types> - - not supported non pris en charge diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.it.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.it.xlf index 3414e733b8185..0d536c01d3de8 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.it.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.it.xlf @@ -87,11 +87,6 @@ Elenco Visual Basic - - <Multiple Types> - <Più tipi> - - not supported non supportato diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ja.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ja.xlf index 8b1892443c683..786e466ae9977 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ja.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ja.xlf @@ -87,11 +87,6 @@ Visual Basic の再フォーマット - - <Multiple Types> - <複数の種類> - - not supported サポートされていません diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ko.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ko.xlf index a097e7223f0e6..08b9ca15c7477 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ko.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ko.xlf @@ -87,11 +87,6 @@ Visual Basic 서식 다시 적용 - - <Multiple Types> - <여러 형식> - - not supported 지원되지 않음 diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.pl.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.pl.xlf index bfa1305e66466..3871f85210761 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.pl.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.pl.xlf @@ -87,11 +87,6 @@ Lista Pretty języka Visual Basic - - <Multiple Types> - <Wiele typów> - - not supported nieobsługiwane diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.pt-BR.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.pt-BR.xlf index 08550419b0b82..d8823dc0a626c 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.pt-BR.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.pt-BR.xlf @@ -87,11 +87,6 @@ Reformatação Automática do Visual Basic - - <Multiple Types> - <Vários Tipos> - - not supported não suportado diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ru.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ru.xlf index c2cc620bf887b..740def5287fdf 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ru.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.ru.xlf @@ -87,11 +87,6 @@ Изящный список Visual Basic - - <Multiple Types> - <Несколько типов> - - not supported не поддерживается diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.tr.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.tr.xlf index e6df38b81c564..3c7d0c05119c8 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.tr.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.tr.xlf @@ -87,11 +87,6 @@ Visual Basic Düzgün Listesi - - <Multiple Types> - <Multiple Types> - - not supported Desteklenmiyor diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.zh-Hans.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.zh-Hans.xlf index ca091ca56e572..d10565af637ff 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.zh-Hans.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.zh-Hans.xlf @@ -87,11 +87,6 @@ Visual Basic 整齐列表 - - <Multiple Types> - <多种类型> - - not supported 不支持 diff --git a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.zh-Hant.xlf b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.zh-Hant.xlf index 0d92e09ff28ff..83b1c3d4cdc1c 100644 --- a/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.zh-Hant.xlf +++ b/src/EditorFeatures/VisualBasic/xlf/VBEditorResources.zh-Hant.xlf @@ -87,11 +87,6 @@ Visual Basic 美化清單 - - <Multiple Types> - <多項類型> - - not supported 不支援 diff --git a/src/EditorFeatures/VisualBasicTest/QuickInfo/SemanticQuickInfoSourceTests.vb b/src/EditorFeatures/VisualBasicTest/QuickInfo/SemanticQuickInfoSourceTests.vb index 72f9457cac18a..d7cb1bae6f612 100644 --- a/src/EditorFeatures/VisualBasicTest/QuickInfo/SemanticQuickInfoSourceTests.vb +++ b/src/EditorFeatures/VisualBasicTest/QuickInfo/SemanticQuickInfoSourceTests.vb @@ -4,20 +4,22 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Editor.UnitTests.QuickInfo Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces -Imports Microsoft.CodeAnalysis.Editor.VisualBasic.QuickInfo +Imports Microsoft.CodeAnalysis.QuickInfo Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.QuickInfo Public Class SemanticQuickInfoSourceTests Inherits AbstractSemanticQuickInfoSourceTests - Protected Overrides Function TestAsync(markup As String, ParamArray expectedResults() As Action(Of Object)) As Task + Protected Overrides Function TestAsync(markup As String, ParamArray expectedResults() As Action(Of QuickInfoItem)) As Task Return TestWithReferencesAsync(markup, Array.Empty(Of String)(), expectedResults) End Function - Protected Async Function TestSharedAsync(workspace As TestWorkspace, position As Integer, ParamArray expectedResults() As Action(Of Object)) As Task - Dim provider = New SemanticQuickInfoProvider() + Protected Async Function TestSharedAsync(workspace As TestWorkspace, position As Integer, ParamArray expectedResults() As Action(Of QuickInfoItem)) As Task + Dim service = workspace.Services _ + .GetLanguageServices(LanguageNames.VisualBasic) _ + .GetService(Of QuickInfoService) - Await TestSharedAsync(workspace, provider, position, expectedResults) + Await TestSharedAsync(workspace, service, position, expectedResults) ' speculative semantic model Dim document = workspace.CurrentSolution.Projects.First().Documents.First() @@ -28,36 +30,33 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.QuickInfo edit.Apply() End Using - Await TestSharedAsync(workspace, provider, position, expectedResults) + Await TestSharedAsync(workspace, service, position, expectedResults) End If End Function - Private Async Function TestSharedAsync(workspace As TestWorkspace, provider As SemanticQuickInfoProvider, position As Integer, expectedResults() As Action(Of Object)) As Task - Dim state = Await provider.GetItemAsync(workspace.CurrentSolution.Projects.First().Documents.First(), - position, cancellationToken:=CancellationToken.None) - - If state IsNot Nothing Then - WaitForDocumentationComment(state.Content) - End If + Private Async Function TestSharedAsync(workspace As TestWorkspace, service As QuickInfoService, position As Integer, expectedResults() As Action(Of QuickInfoItem)) As Task + Dim info = Await service.GetQuickInfoAsync( + workspace.CurrentSolution.Projects.First().Documents.First(), + position, cancellationToken:=CancellationToken.None) If expectedResults Is Nothing Then - Assert.Null(state) + Assert.Null(info) Else - Assert.NotNull(state) + Assert.NotNull(info) For Each expected In expectedResults - expected(state.Content) + expected(info) Next End If End Function - Protected Async Function TestFromXmlAsync(markup As String, ParamArray expectedResults As Action(Of Object)()) As Task + Private Async Function TestFromXmlAsync(markup As String, ParamArray expectedResults As Action(Of QuickInfoItem)()) As Task Using workspace = TestWorkspace.Create(markup) Await TestSharedAsync(workspace, workspace.Documents.First().CursorPosition.Value, expectedResults) End Using End Function - Protected Async Function TestWithReferencesAsync(markup As String, metadataReferences As String(), ParamArray expectedResults() As Action(Of Object)) As Task + Private Async Function TestWithReferencesAsync(markup As String, metadataReferences As String(), ParamArray expectedResults() As Action(Of QuickInfoItem)) As Task Dim code As String = Nothing Dim position As Integer = Nothing MarkupTestFile.GetPosition(markup, code, position) @@ -67,7 +66,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.QuickInfo End Using End Function - Protected Async Function TestWithImportsAsync(markup As String, ParamArray expectedResults() As Action(Of Object)) As Task + Private Async Function TestWithImportsAsync(markup As String, ParamArray expectedResults() As Action(Of QuickInfoItem)) As Task Dim markupWithImports = "Imports System" & vbCrLf & "Imports System.Collections.Generic" & vbCrLf & @@ -77,7 +76,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.QuickInfo Await TestAsync(markupWithImports, expectedResults) End Function - Protected Async Function TestInClassAsync(markup As String, ParamArray expectedResults() As Action(Of Object)) As Task + Private Async Function TestInClassAsync(markup As String, ParamArray expectedResults() As Action(Of QuickInfoItem)) As Task Dim markupInClass = "Class C" & vbCrLf & markup & vbCrLf & @@ -86,7 +85,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.QuickInfo Await TestWithImportsAsync(markupInClass, expectedResults) End Function - Protected Async Function TestInMethodAsync(markup As String, ParamArray expectedResults() As Action(Of Object)) As Task + Private Async Function TestInMethodAsync(markup As String, ParamArray expectedResults() As Action(Of QuickInfoItem)) As Task Dim markupInClass = "Class C" & vbCrLf & "Sub M()" & vbCrLf & @@ -510,7 +509,7 @@ End Class End Function - + Public Async Function TestOverriddenMethod() As Task Await TestAsync( Class A @@ -583,7 +582,7 @@ End class Public Async Function TestDimMultipleInFieldDeclaration() As Task - Await TestInClassAsync("$$Dim x As Integer, y As String", MainDescription(VBEditorResources.Multiple_Types)) + Await TestInClassAsync("$$Dim x As Integer, y As String", MainDescription(VBFeaturesResources.Multiple_Types)) End Function @@ -605,7 +604,7 @@ End Module Public Async Function TestDimMultipleInLocalDeclaration() As Task - Await TestInMethodAsync("$$Dim x As Integer, y As String", MainDescription(VBEditorResources.Multiple_Types)) + Await TestInMethodAsync("$$Dim x As Integer, y As String", MainDescription(VBFeaturesResources.Multiple_Types)) End Function @@ -1413,7 +1412,7 @@ Class C go$$o() End Function End Class - + .ToString() @@ -1754,9 +1753,10 @@ End Class]]>.NormalizedValue(), Public Async Function TestAwaitKeywordOnTaskReturningAsync() As Task - Dim markup = - - + Dim markup = + + + Imports System.Threading.Tasks Class C @@ -1765,8 +1765,8 @@ Class C End Function End Class - - .ToString() + + .ToString() Dim description = <%= FeaturesResources.Awaited_task_returns %><%= " " %><%= FeaturesResources.no_value %>.ConvertTestSourceTag() @@ -1787,7 +1787,7 @@ Class C Return 42 End Function End Class - + .ToString() diff --git a/src/Features/CSharp/Portable/QuickInfo/CSharpQuickInfoSevice.cs b/src/Features/CSharp/Portable/QuickInfo/CSharpQuickInfoSevice.cs new file mode 100644 index 0000000000000..e26e2e051a092 --- /dev/null +++ b/src/Features/CSharp/Portable/QuickInfo/CSharpQuickInfoSevice.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Composition; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.QuickInfo; + +namespace Microsoft.CodeAnalysis.CSharp.QuickInfo +{ + [ExportLanguageServiceFactory(typeof(QuickInfoService), LanguageNames.CSharp), Shared] + internal class CSharpQuickInfoServiceFactory : ILanguageServiceFactory + { + public ILanguageService CreateLanguageService(HostLanguageServices languageServices) + { + return new CSharpQuickInfoService(languageServices.WorkspaceServices.Workspace); + } + } + + internal class CSharpQuickInfoService : QuickInfoServiceWithProviders + { + internal CSharpQuickInfoService(Workspace workspace) + : base(workspace, LanguageNames.CSharp) + { + } + } +} + diff --git a/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs b/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs new file mode 100644 index 0000000000000..943c2aa7ca285 --- /dev/null +++ b/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Composition; +using Microsoft.CodeAnalysis.QuickInfo; + +namespace Microsoft.CodeAnalysis.CSharp.QuickInfo +{ + [ExportQuickInfoProvider(QuickInfoProviderNames.Semantic, LanguageNames.CSharp), Shared] + internal class CSharpSemanticQuickInfoProvider : CommonSemanticQuickInfoProvider + { + protected override bool ShouldCheckPreviousToken(SyntaxToken token) + { + return !token.Parent.IsKind(SyntaxKind.XmlCrefAttribute); + } + } +} diff --git a/src/EditorFeatures/CSharp/QuickInfo/SyntacticQuickInfoProvider.cs b/src/Features/CSharp/Portable/QuickInfo/CSharpSyntacticQuickInfoProvider.cs similarity index 75% rename from src/EditorFeatures/CSharp/QuickInfo/SyntacticQuickInfoProvider.cs rename to src/Features/CSharp/Portable/QuickInfo/CSharpSyntacticQuickInfoProvider.cs index 14c22107f7c97..fe83607ababaa 100644 --- a/src/EditorFeatures/CSharp/QuickInfo/SyntacticQuickInfoProvider.cs +++ b/src/Features/CSharp/Portable/QuickInfo/CSharpSyntacticQuickInfoProvider.cs @@ -1,27 +1,23 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using System.ComponentModel.Composition; +using System.Collections.Immutable; +using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Projection; -namespace Microsoft.CodeAnalysis.Editor.CSharp.QuickInfo +namespace Microsoft.CodeAnalysis.CSharp.QuickInfo { - [ExportQuickInfoProvider(PredefinedQuickInfoProviderNames.Syntactic, LanguageNames.CSharp)] - internal class SyntacticQuickInfoProvider : AbstractQuickInfoProvider + [ExportQuickInfoProvider(QuickInfoProviderNames.Syntactic, LanguageNames.CSharp), Shared] + [ExtensionOrder(After = QuickInfoProviderNames.Semantic)] + internal class CSharpSyntacticQuickInfoProvider : CommonQuickInfoProvider { - protected override async Task BuildContentAsync( + protected override async Task BuildQuickInfoAsync( Document document, SyntaxToken token, CancellationToken cancellationToken) @@ -62,17 +58,10 @@ protected override async Task BuildContentAsync( spanStart = parent.Parent.SpanStart; } - // Now that we know what we want to display, create a small elision buffer with that - // span, jam it in a view and show that to the user. + // encode document spans that correspond to the text to show var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var textSnapshot = text.FindCorrespondingEditorTextSnapshot(); - if (textSnapshot == null) - { - return null; - } - - var span = new SnapshotSpan(textSnapshot, Span.FromBounds(spanStart, spanEnd)); - return this.CreateProjectionBufferDeferredContent(span); + var spans = ImmutableArray.Create(TextSpan.FromBounds(spanStart, spanEnd)); + return QuickInfoItem.Create(token.Span, relatedSpans: spans); } private static bool IsScopeBlock(SyntaxNode node) @@ -105,7 +94,7 @@ private static void MarkInterestedSpanNearbyScopeBlock(SyntaxNode block, SyntaxT private static bool TryFindFurthestNearbyComment(ref T triviaSearchList, out SyntaxTrivia nearbyTrivia) where T : IEnumerable { - nearbyTrivia = default(SyntaxTrivia); + nearbyTrivia = default; foreach (var trivia in triviaSearchList) { diff --git a/src/Features/CSharp/Portable/RemoveUnusedVariable/CSharpRemoveUnusedVariableCodeFixProvider.cs b/src/Features/CSharp/Portable/RemoveUnusedVariable/CSharpRemoveUnusedVariableCodeFixProvider.cs index 64d0d289e1aed..3be20f579c30e 100644 --- a/src/Features/CSharp/Portable/RemoveUnusedVariable/CSharpRemoveUnusedVariableCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/RemoveUnusedVariable/CSharpRemoveUnusedVariableCodeFixProvider.cs @@ -15,10 +15,10 @@ internal partial class CSharpRemoveUnusedVariableCodeFixProvider : AbstractRemov private const string CS0168 = nameof(CS0168); private const string CS0219 = nameof(CS0219); - public sealed override ImmutableArray FixableDiagnosticIds + public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CS0168, CS0219); protected override bool IsCatchDeclarationIdentifier(SyntaxToken token) => token.Parent is CatchDeclarationSyntax catchDeclaration && catchDeclaration.Identifier == token; } -} \ No newline at end of file +} diff --git a/src/Features/Core/Portable/Common/GlyphExtensions.cs b/src/Features/Core/Portable/Common/GlyphExtensions.cs new file mode 100644 index 0000000000000..ca08a524f5e7f --- /dev/null +++ b/src/Features/Core/Portable/Common/GlyphExtensions.cs @@ -0,0 +1,302 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Tags; + +namespace Microsoft.CodeAnalysis +{ + internal static class GlyphExtensions + { + public static ImmutableArray GetGlyphs(this ImmutableArray tags) + { + var builder = ImmutableArray.CreateBuilder(initialCapacity: tags.Length); + + foreach (var tag in tags) + { + var glyph = GetGlyph(tag, tags); + if (glyph != Glyph.None) + { + builder.Add(glyph); + } + } + + return builder.ToImmutable(); + } + + public static Glyph GetFirstGlyph(this ImmutableArray tags) + { + var glyphs = GetGlyphs(tags); + + return !glyphs.IsEmpty + ? glyphs[0] + : Glyph.None; + } + + private static Glyph GetGlyph(string tag, ImmutableArray allTags) + { + switch (tag) + { + case WellKnownTags.Assembly: + return Glyph.Assembly; + + case WellKnownTags.File: + return allTags.Contains(LanguageNames.VisualBasic) ? Glyph.BasicFile : Glyph.CSharpFile; + + case WellKnownTags.Project: + return allTags.Contains(LanguageNames.VisualBasic) ? Glyph.BasicProject : Glyph.CSharpProject; + + case WellKnownTags.Class: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.ClassProtected; + case Accessibility.Private: + return Glyph.ClassPrivate; + case Accessibility.Internal: + return Glyph.ClassInternal; + case Accessibility.Public: + default: + return Glyph.ClassPublic; + } + + case WellKnownTags.Constant: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.ConstantProtected; + case Accessibility.Private: + return Glyph.ConstantPrivate; + case Accessibility.Internal: + return Glyph.ConstantInternal; + case Accessibility.Public: + default: + return Glyph.ConstantPublic; + } + + case WellKnownTags.Delegate: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.DelegateProtected; + case Accessibility.Private: + return Glyph.DelegatePrivate; + case Accessibility.Internal: + return Glyph.DelegateInternal; + case Accessibility.Public: + default: + return Glyph.DelegatePublic; + } + + case WellKnownTags.Enum: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.EnumProtected; + case Accessibility.Private: + return Glyph.EnumPrivate; + case Accessibility.Internal: + return Glyph.EnumInternal; + case Accessibility.Public: + default: + return Glyph.EnumPublic; + } + + case WellKnownTags.EnumMember: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.EnumMemberProtected; + case Accessibility.Private: + return Glyph.EnumMemberPrivate; + case Accessibility.Internal: + return Glyph.EnumMemberInternal; + case Accessibility.Public: + default: + return Glyph.EnumMemberPublic; + } + + case WellKnownTags.Error: + return Glyph.Error; + + case WellKnownTags.Event: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.EventProtected; + case Accessibility.Private: + return Glyph.EventPrivate; + case Accessibility.Internal: + return Glyph.EventInternal; + case Accessibility.Public: + default: + return Glyph.EventPublic; + } + + case WellKnownTags.ExtensionMethod: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.ExtensionMethodProtected; + case Accessibility.Private: + return Glyph.ExtensionMethodPrivate; + case Accessibility.Internal: + return Glyph.ExtensionMethodInternal; + case Accessibility.Public: + default: + return Glyph.ExtensionMethodPublic; + } + + case WellKnownTags.Field: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.FieldProtected; + case Accessibility.Private: + return Glyph.FieldPrivate; + case Accessibility.Internal: + return Glyph.FieldInternal; + case Accessibility.Public: + default: + return Glyph.FieldPublic; + } + + case WellKnownTags.Interface: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.InterfaceProtected; + case Accessibility.Private: + return Glyph.InterfacePrivate; + case Accessibility.Internal: + return Glyph.InterfaceInternal; + case Accessibility.Public: + default: + return Glyph.InterfacePublic; + } + + case WellKnownTags.Intrinsic: + return Glyph.Intrinsic; + + case WellKnownTags.Keyword: + return Glyph.Keyword; + + case WellKnownTags.Label: + return Glyph.Label; + + case WellKnownTags.Local: + return Glyph.Local; + + case WellKnownTags.Namespace: + return Glyph.Namespace; + + case WellKnownTags.Method: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.MethodProtected; + case Accessibility.Private: + return Glyph.MethodPrivate; + case Accessibility.Internal: + return Glyph.MethodInternal; + case Accessibility.Public: + default: + return Glyph.MethodPublic; + } + + case WellKnownTags.Module: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.ModulePublic; + case Accessibility.Private: + return Glyph.ModulePrivate; + case Accessibility.Internal: + return Glyph.ModuleInternal; + case Accessibility.Public: + default: + return Glyph.ModulePublic; + } + + case WellKnownTags.Folder: + return Glyph.OpenFolder; + + case WellKnownTags.Operator: + return Glyph.Operator; + + case WellKnownTags.Parameter: + return Glyph.Parameter; + + case WellKnownTags.Property: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.PropertyProtected; + case Accessibility.Private: + return Glyph.PropertyPrivate; + case Accessibility.Internal: + return Glyph.PropertyInternal; + case Accessibility.Public: + default: + return Glyph.PropertyPublic; + } + + case WellKnownTags.RangeVariable: + return Glyph.RangeVariable; + + case WellKnownTags.Reference: + return Glyph.Reference; + + case WellKnownTags.NuGet: + return Glyph.NuGet; + + case WellKnownTags.Structure: + switch (GetAccessibility(allTags)) + { + case Accessibility.Protected: + return Glyph.StructureProtected; + case Accessibility.Private: + return Glyph.StructurePrivate; + case Accessibility.Internal: + return Glyph.StructureInternal; + case Accessibility.Public: + default: + return Glyph.StructurePublic; + } + + case WellKnownTags.TypeParameter: + return Glyph.TypeParameter; + + case WellKnownTags.Snippet: + return Glyph.Snippet; + + case WellKnownTags.Warning: + return Glyph.CompletionWarning; + + case WellKnownTags.StatusInformation: + return Glyph.StatusInformation; + } + + return Glyph.None; + } + + private static Accessibility GetAccessibility(ImmutableArray tags) + { + foreach (var tag in tags) + { + switch (tag) + { + case WellKnownTags.Public: + return Accessibility.Public; + case WellKnownTags.Protected: + return Accessibility.Protected; + case WellKnownTags.Internal: + return Accessibility.Internal; + case WellKnownTags.Private: + return Accessibility.Private; + } + } + + return Accessibility.NotApplicable; + } + } +} diff --git a/src/Features/Core/Portable/Completion/GlyphTags.cs b/src/Features/Core/Portable/Common/GlyphTags.cs similarity index 99% rename from src/Features/Core/Portable/Completion/GlyphTags.cs rename to src/Features/Core/Portable/Common/GlyphTags.cs index 0f9f84d5a8b8f..428aecc536bd1 100644 --- a/src/Features/Core/Portable/Completion/GlyphTags.cs +++ b/src/Features/Core/Portable/Common/GlyphTags.cs @@ -3,7 +3,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.Tags; -namespace Microsoft.CodeAnalysis.Completion +namespace Microsoft.CodeAnalysis { internal static class GlyphTags { diff --git a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs b/src/Features/Core/Portable/Completion/CommonCompletionItem.cs index 6972d5cfcf5c1..493b592c7ba22 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionItem.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using System.Linq; +using Microsoft.CodeAnalysis.Tags; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion @@ -29,7 +30,7 @@ public static CompletionItem Create( if (showsWarningIcon) { - tags = tags.Add(CompletionTags.Warning); + tags = tags.Add(WellKnownTags.Warning); } properties = properties ?? ImmutableDictionary.Empty; diff --git a/src/Features/Core/Portable/Completion/CommonCompletionService.cs b/src/Features/Core/Portable/Completion/CommonCompletionService.cs index d9128324c0666..207c83117adcd 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionService.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionService.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Tags; namespace Microsoft.CodeAnalysis.Completion { @@ -33,12 +34,12 @@ protected override CompletionItem GetBetterItem(CompletionItem item, CompletionI protected static bool IsKeywordItem(CompletionItem item) { - return item.Tags.Contains(CompletionTags.Keyword); + return item.Tags.Contains(WellKnownTags.Keyword); } protected static bool IsSnippetItem(CompletionItem item) { - return item.Tags.Contains(CompletionTags.Snippet); + return item.Tags.Contains(WellKnownTags.Snippet); } } } diff --git a/src/Features/Core/Portable/Completion/CompletionHelper.cs b/src/Features/Core/Portable/Completion/CompletionHelper.cs index 4c5cf8c4b0896..f63dad22497c8 100644 --- a/src/Features/Core/Portable/Completion/CompletionHelper.cs +++ b/src/Features/Core/Portable/Completion/CompletionHelper.cs @@ -6,6 +6,7 @@ using System.Globalization; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.PatternMatching; +using Microsoft.CodeAnalysis.Tags; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Completion @@ -183,7 +184,7 @@ private static bool TagsEqual(ImmutableArray tags1, ImmutableArray public ImmutableDictionary Properties { get; } /// - /// Descriptive tags from . + /// Descriptive tags from . /// These tags may influence how the item is displayed. /// public ImmutableArray Tags { get; } diff --git a/src/Features/Core/Portable/Completion/CompletionItemFilter.cs b/src/Features/Core/Portable/Completion/CompletionItemFilter.cs index bf0645fd3a2b0..e13131fef4e5f 100644 --- a/src/Features/Core/Portable/Completion/CompletionItemFilter.cs +++ b/src/Features/Core/Portable/Completion/CompletionItemFilter.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Tags; namespace Microsoft.CodeAnalysis.Completion { @@ -61,22 +62,22 @@ public static bool ShouldBeFilteredOutOfCompletionList( return true; } - public static readonly CompletionItemFilter NamespaceFilter = new CompletionItemFilter(FeaturesResources.Namespaces, CompletionTags.Namespace, 'n'); - public static readonly CompletionItemFilter ClassFilter = new CompletionItemFilter(FeaturesResources.Classes, CompletionTags.Class, 'c'); - public static readonly CompletionItemFilter ModuleFilter = new CompletionItemFilter(FeaturesResources.Modules, CompletionTags.Module, 'u'); - public static readonly CompletionItemFilter StructureFilter = new CompletionItemFilter(FeaturesResources.Structures, CompletionTags.Structure, 's'); - public static readonly CompletionItemFilter InterfaceFilter = new CompletionItemFilter(FeaturesResources.Interfaces, CompletionTags.Interface, 'i'); - public static readonly CompletionItemFilter EnumFilter = new CompletionItemFilter(FeaturesResources.Enums, CompletionTags.Enum, 'e'); - public static readonly CompletionItemFilter DelegateFilter = new CompletionItemFilter(FeaturesResources.Delegates, CompletionTags.Delegate, 'd'); - public static readonly CompletionItemFilter ConstantFilter = new CompletionItemFilter(FeaturesResources.Constants, CompletionTags.Constant, 'o'); - public static readonly CompletionItemFilter FieldFilter = new CompletionItemFilter(FeaturesResources.Fields, CompletionTags.Field, 'f'); - public static readonly CompletionItemFilter EventFilter = new CompletionItemFilter(FeaturesResources.Events, CompletionTags.Event, 'v'); - public static readonly CompletionItemFilter PropertyFilter = new CompletionItemFilter(FeaturesResources.Properties, CompletionTags.Property, 'p'); - public static readonly CompletionItemFilter MethodFilter = new CompletionItemFilter(FeaturesResources.Methods, CompletionTags.Method, 'm'); - public static readonly CompletionItemFilter ExtensionMethodFilter = new CompletionItemFilter(FeaturesResources.Extension_methods, CompletionTags.ExtensionMethod, 'x'); - public static readonly CompletionItemFilter LocalAndParameterFilter = new CompletionItemFilter(FeaturesResources.Locals_and_parameters, ImmutableArray.Create(CompletionTags.Local, CompletionTags.Parameter), 'l'); - public static readonly CompletionItemFilter KeywordFilter = new CompletionItemFilter(FeaturesResources.Keywords, ImmutableArray.Create(CompletionTags.Keyword), 'k'); - public static readonly CompletionItemFilter SnippetFilter = new CompletionItemFilter(FeaturesResources.Snippets, ImmutableArray.Create(CompletionTags.Snippet), 't'); + public static readonly CompletionItemFilter NamespaceFilter = new CompletionItemFilter(FeaturesResources.Namespaces, WellKnownTags.Namespace, 'n'); + public static readonly CompletionItemFilter ClassFilter = new CompletionItemFilter(FeaturesResources.Classes, WellKnownTags.Class, 'c'); + public static readonly CompletionItemFilter ModuleFilter = new CompletionItemFilter(FeaturesResources.Modules, WellKnownTags.Module, 'u'); + public static readonly CompletionItemFilter StructureFilter = new CompletionItemFilter(FeaturesResources.Structures, WellKnownTags.Structure, 's'); + public static readonly CompletionItemFilter InterfaceFilter = new CompletionItemFilter(FeaturesResources.Interfaces, WellKnownTags.Interface, 'i'); + public static readonly CompletionItemFilter EnumFilter = new CompletionItemFilter(FeaturesResources.Enums, WellKnownTags.Enum, 'e'); + public static readonly CompletionItemFilter DelegateFilter = new CompletionItemFilter(FeaturesResources.Delegates, WellKnownTags.Delegate, 'd'); + public static readonly CompletionItemFilter ConstantFilter = new CompletionItemFilter(FeaturesResources.Constants, WellKnownTags.Constant, 'o'); + public static readonly CompletionItemFilter FieldFilter = new CompletionItemFilter(FeaturesResources.Fields, WellKnownTags.Field, 'f'); + public static readonly CompletionItemFilter EventFilter = new CompletionItemFilter(FeaturesResources.Events, WellKnownTags.Event, 'v'); + public static readonly CompletionItemFilter PropertyFilter = new CompletionItemFilter(FeaturesResources.Properties, WellKnownTags.Property, 'p'); + public static readonly CompletionItemFilter MethodFilter = new CompletionItemFilter(FeaturesResources.Methods, WellKnownTags.Method, 'm'); + public static readonly CompletionItemFilter ExtensionMethodFilter = new CompletionItemFilter(FeaturesResources.Extension_methods, WellKnownTags.ExtensionMethod, 'x'); + public static readonly CompletionItemFilter LocalAndParameterFilter = new CompletionItemFilter(FeaturesResources.Locals_and_parameters, ImmutableArray.Create(WellKnownTags.Local, WellKnownTags.Parameter), 'l'); + public static readonly CompletionItemFilter KeywordFilter = new CompletionItemFilter(FeaturesResources.Keywords, ImmutableArray.Create(WellKnownTags.Keyword), 'k'); + public static readonly CompletionItemFilter SnippetFilter = new CompletionItemFilter(FeaturesResources.Snippets, ImmutableArray.Create(WellKnownTags.Snippet), 't'); public static readonly ImmutableArray NamespaceFilters = ImmutableArray.Create(NamespaceFilter); public static readonly ImmutableArray ClassFilters = ImmutableArray.Create(ClassFilter); diff --git a/src/Features/Core/Portable/Completion/CompletionTags.cs b/src/Features/Core/Portable/Completion/CompletionTags.cs index 158a446073ea1..a09a4680220ea 100644 --- a/src/Features/Core/Portable/Completion/CompletionTags.cs +++ b/src/Features/Core/Portable/Completion/CompletionTags.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.ComponentModel; using Microsoft.CodeAnalysis.Tags; namespace Microsoft.CodeAnalysis.Completion @@ -8,6 +10,8 @@ namespace Microsoft.CodeAnalysis.Completion /// The set of well known tags used for the property. /// These tags influence the presentation of items in the list. /// + [Obsolete("Use Microsoft.CodeAnalysis.Tags.WellKnownTags instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] public static class CompletionTags { // accessibility diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs index fd5e6cdaf3d41..bea06ed941039 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Tags; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -68,7 +69,7 @@ private async Task> RecommendCompletionItemsAsync(Do return keywords?.Select(k => CreateItem(k, syntaxContext)); } - protected static ImmutableArray s_Tags = ImmutableArray.Create(CompletionTags.Intrinsic); + protected static ImmutableArray s_Tags = ImmutableArray.Create(WellKnownTags.Intrinsic); protected static CompletionItemRules s_keywordRules = CompletionItemRules.Default; diff --git a/src/Features/Core/Portable/FeaturesResources.Designer.cs b/src/Features/Core/Portable/FeaturesResources.Designer.cs index d9ef20dfe9613..bcbcaafba2947 100644 --- a/src/Features/Core/Portable/FeaturesResources.Designer.cs +++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs @@ -3635,15 +3635,6 @@ internal static string User_Diagnostic_Analyzer_Failure { } } - /// - /// Looks up a localized string similar to Variable declaration can be deconstructed. - /// - internal static string Variable_declaration_can_be_deconstructed { - get { - return ResourceManager.GetString("Variable_declaration_can_be_deconstructed", resourceCulture); - } - } - /// /// Looks up a localized string similar to Using readonly references will prevent the debug session from continuing.. /// @@ -3671,6 +3662,15 @@ internal static string Using_ref_structs_will_prevent_the_debug_session_from_con } } + /// + /// Looks up a localized string similar to Variable declaration can be deconstructed. + /// + internal static string Variable_declaration_can_be_deconstructed { + get { + return ResourceManager.GetString("Variable_declaration_can_be_deconstructed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Variable declaration can be inlined. /// diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index fad430187ade4..829e5479dd0bd 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -1334,4 +1334,4 @@ This version used in: {2} indexer - + \ No newline at end of file diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs index 740e8a67d2eac..41f805a35d5e6 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Tags; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindUsages @@ -43,7 +44,7 @@ internal abstract partial class DefinitionItem private const string NonNavigable = nameof(NonNavigable); /// - /// Descriptive tags from . These tags may influence how the + /// Descriptive tags from . These tags may influence how the /// item is displayed. /// public ImmutableArray Tags { get; } diff --git a/src/Features/Core/Portable/PublicAPI.Unshipped.txt b/src/Features/Core/Portable/PublicAPI.Unshipped.txt index 8b137891791fe..ef07252d89ccd 100644 --- a/src/Features/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Features/Core/Portable/PublicAPI.Unshipped.txt @@ -1 +1,23 @@ - +Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem +Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem.RelatedSpans.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem.Sections.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem.Span.get -> Microsoft.CodeAnalysis.Text.TextSpan +Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem.Tags.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.QuickInfo.QuickInfoSection +Microsoft.CodeAnalysis.QuickInfo.QuickInfoSection.Kind.get -> string +Microsoft.CodeAnalysis.QuickInfo.QuickInfoSection.TaggedParts.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.QuickInfo.QuickInfoSection.Text.get -> string +Microsoft.CodeAnalysis.QuickInfo.QuickInfoSectionKinds +Microsoft.CodeAnalysis.QuickInfo.QuickInfoService +Microsoft.CodeAnalysis.QuickInfo.QuickInfoService.QuickInfoService() -> void +const Microsoft.CodeAnalysis.QuickInfo.QuickInfoSectionKinds.AnonymousTypes = "AnonymousTypes" -> string +const Microsoft.CodeAnalysis.QuickInfo.QuickInfoSectionKinds.Description = "Description" -> string +const Microsoft.CodeAnalysis.QuickInfo.QuickInfoSectionKinds.DocumentationComments = "DocumentationComments" -> string +const Microsoft.CodeAnalysis.QuickInfo.QuickInfoSectionKinds.Exception = "Exception" -> string +const Microsoft.CodeAnalysis.QuickInfo.QuickInfoSectionKinds.Text = "Text" -> string +const Microsoft.CodeAnalysis.QuickInfo.QuickInfoSectionKinds.TypeParameters = "TypeParameters" -> string +const Microsoft.CodeAnalysis.QuickInfo.QuickInfoSectionKinds.Usage = "Usage" -> string +static Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem.Create(Microsoft.CodeAnalysis.Text.TextSpan span, System.Collections.Immutable.ImmutableArray tags = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray sections = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray relatedSpans = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.QuickInfo.QuickInfoItem +static Microsoft.CodeAnalysis.QuickInfo.QuickInfoSection.Create(string kind, System.Collections.Immutable.ImmutableArray taggedParts) -> Microsoft.CodeAnalysis.QuickInfo.QuickInfoSection +static Microsoft.CodeAnalysis.QuickInfo.QuickInfoService.GetService(Microsoft.CodeAnalysis.Document document) -> Microsoft.CodeAnalysis.QuickInfo.QuickInfoService +virtual Microsoft.CodeAnalysis.QuickInfo.QuickInfoService.GetQuickInfoAsync(Microsoft.CodeAnalysis.Document document, int position, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task diff --git a/src/Features/Core/Portable/QuickInfo/CommonQuickInfoProvider.cs b/src/Features/Core/Portable/QuickInfo/CommonQuickInfoProvider.cs new file mode 100644 index 0000000000000..667d8ea0d4e94 --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/CommonQuickInfoProvider.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + internal abstract class CommonQuickInfoProvider : QuickInfoProvider + { + public override async Task GetQuickInfoAsync(QuickInfoContext context) + { + var document = context.Document; + var position = context.Position; + var cancellationToken = context.CancellationToken; + + var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var token = await tree.GetTouchingTokenAsync(position, cancellationToken, findInsideTrivia: true).ConfigureAwait(false); + + var info = await GetQuickInfoAsync(document, token, position, cancellationToken).ConfigureAwait(false); + + if (info == null && ShouldCheckPreviousToken(token)) + { + var previousToken = token.GetPreviousToken(); + info = await GetQuickInfoAsync(document, previousToken, position, cancellationToken).ConfigureAwait(false); + } + + return info; + } + + protected virtual bool ShouldCheckPreviousToken(SyntaxToken token) + { + return true; + } + + private async Task GetQuickInfoAsync( + Document document, + SyntaxToken token, + int position, + CancellationToken cancellationToken) + { + if (token != default && + token.Span.IntersectsWith(position)) + { + return await BuildQuickInfoAsync(document, token, cancellationToken).ConfigureAwait(false); + } + + return null; + } + + protected abstract Task BuildQuickInfoAsync(Document document, SyntaxToken token, CancellationToken cancellationToken); + } +} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractSemanticQuickInfoProvider.ErrorVisitor.cs b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.ErrorVisitor.cs similarity index 94% rename from src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractSemanticQuickInfoProvider.ErrorVisitor.cs rename to src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.ErrorVisitor.cs index f51f2460880f7..204cd10199d9e 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractSemanticQuickInfoProvider.ErrorVisitor.cs +++ b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.ErrorVisitor.cs @@ -2,10 +2,11 @@ using System.Linq; using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo +namespace Microsoft.CodeAnalysis.QuickInfo { - internal abstract partial class AbstractSemanticQuickInfoProvider + internal abstract partial class CommonSemanticQuickInfoProvider { private class ErrorVisitor : SymbolVisitor { diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractSemanticQuickInfoProvider.SymbolComparer.cs b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.SymbolComparer.cs similarity index 88% rename from src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractSemanticQuickInfoProvider.SymbolComparer.cs rename to src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.SymbolComparer.cs index e11ff28bb6c2e..06947ba71f035 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractSemanticQuickInfoProvider.SymbolComparer.cs +++ b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.SymbolComparer.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Shared.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo +namespace Microsoft.CodeAnalysis.QuickInfo { - internal abstract partial class AbstractSemanticQuickInfoProvider + internal abstract partial class CommonSemanticQuickInfoProvider { private class SymbolComparer : IEqualityComparer { diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractSemanticQuickInfoProvider.cs b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs similarity index 54% rename from src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractSemanticQuickInfoProvider.cs rename to src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs index cd59ecd2b1b4e..ad12544bd0a4e 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/AbstractSemanticQuickInfoProvider.cs +++ b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs @@ -7,45 +7,56 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DocumentationComments; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Projection; -using Microsoft.VisualStudio.Utilities; +using Microsoft.CodeAnalysis.Tags; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo +namespace Microsoft.CodeAnalysis.QuickInfo { - internal abstract partial class AbstractSemanticQuickInfoProvider : AbstractQuickInfoProvider + internal abstract partial class CommonSemanticQuickInfoProvider : CommonQuickInfoProvider { - protected override async Task BuildContentAsync( + protected override async Task BuildQuickInfoAsync( Document document, SyntaxToken token, CancellationToken cancellationToken) { - var linkedDocumentIds = document.GetLinkedDocumentIds(); + var (model, symbols, supportedPlatforms) = await ComputeQuickInfoDataAsync(document, token, cancellationToken).ConfigureAwait(false); - var modelAndSymbols = await this.BindTokenAsync(document, token, cancellationToken).ConfigureAwait(false); - if (modelAndSymbols.Item2.Length == 0 && !linkedDocumentIds.Any()) + if (symbols.IsDefaultOrEmpty) { return null; } - if (!linkedDocumentIds.Any()) + return await CreateContentAsync(document.Project.Solution.Workspace, + token, model, symbols, supportedPlatforms, + cancellationToken).ConfigureAwait(false); + } + + private async Task<(SemanticModel model, ImmutableArray symbols, SupportedPlatformData supportedPlatforms)> ComputeQuickInfoDataAsync( + Document document, + SyntaxToken token, + CancellationToken cancellationToken) + { + var linkedDocumentIds = document.GetLinkedDocumentIds(); + if (linkedDocumentIds.Any()) { - return await CreateContentAsync(document.Project.Solution.Workspace, - token, - modelAndSymbols.Item1, - modelAndSymbols.Item2, - supportedPlatforms: null, - cancellationToken: cancellationToken).ConfigureAwait(false); + return await ComputeFromLinkedDocumentsAsync(document, linkedDocumentIds, token, cancellationToken).ConfigureAwait(false); } + var (model, symbols) = await BindTokenAsync(document, token, cancellationToken).ConfigureAwait(false); + + return (model, symbols, supportedPlatforms: null); + } + + private async Task<(SemanticModel model, ImmutableArray symbols, SupportedPlatformData supportedPlatforms)> ComputeFromLinkedDocumentsAsync( + Document document, + ImmutableArray linkedDocumentIds, + SyntaxToken token, + CancellationToken cancellationToken) + { // Linked files/shared projects: imagine the following when GOO is false // #if GOO // int x = 3; @@ -53,42 +64,41 @@ protected override async Task BuildContentAsync( // var y = x$$; // // 'x' will bind as an error type, so we'll show incorrect information. - // Instead, we need to find the head in which we get the best binding, + // Instead, we need to find the head in which we get the best binding, // which in this case is the one with no errors. + var (model, symbols) = await BindTokenAsync(document, token, cancellationToken).ConfigureAwait(false); + var candidateProjects = new List() { document.Project.Id }; var invalidProjects = new List(); - var candidateResults = new List>>(); - candidateResults.Add(Tuple.Create(document.Id, modelAndSymbols.Item1, modelAndSymbols.Item2)); + var candidateResults = new List<(DocumentId docId, SemanticModel model, ImmutableArray symbols)> + { + (document.Id, model, symbols) + }; - foreach (var link in linkedDocumentIds) + foreach (var linkedDocumentId in linkedDocumentIds) { - var linkedDocument = document.Project.Solution.GetDocument(link); - var linkedToken = await FindTokenInLinkedDocument(token, document, linkedDocument, cancellationToken).ConfigureAwait(false); + var linkedDocument = document.Project.Solution.GetDocument(linkedDocumentId); + var linkedToken = await FindTokenInLinkedDocumentAsync(token, document, linkedDocument, cancellationToken).ConfigureAwait(false); if (linkedToken != default) { // Not in an inactive region, so this file is a candidate. - candidateProjects.Add(link.ProjectId); - var linkedModelAndSymbols = await this.BindTokenAsync(linkedDocument, linkedToken, cancellationToken).ConfigureAwait(false); - candidateResults.Add(Tuple.Create(link, linkedModelAndSymbols.Item1, linkedModelAndSymbols.Item2)); + candidateProjects.Add(linkedDocumentId.ProjectId); + var (linkedModel, linkedSymbols) = await BindTokenAsync(linkedDocument, linkedToken, cancellationToken).ConfigureAwait(false); + candidateResults.Add((linkedDocumentId, linkedModel, linkedSymbols)); } } // Take the first result with no errors. - var bestBinding = candidateResults.FirstOrDefault( - c => c.Item3.Length > 0 && !ErrorVisitor.ContainsError(c.Item3.FirstOrDefault())); + // If every file binds with errors, take the first candidate, which is from the current file. + var bestBinding = candidateResults.FirstOrNullable(c => HasNoErrors(c.symbols)) + ?? candidateResults.First(); - // Every file binds with errors. Take the first candidate, which is from the current file. - if (bestBinding == null) + if (bestBinding.symbols.IsDefaultOrEmpty) { - bestBinding = candidateResults.First(); - } - - if (bestBinding.Item3 == null || !bestBinding.Item3.Any()) - { - return null; + return default; } // We calculate the set of supported projects @@ -96,17 +106,26 @@ protected override async Task BuildContentAsync( foreach (var candidate in candidateResults) { // Does the candidate have anything remotely equivalent? - if (!candidate.Item3.Intersect(bestBinding.Item3, LinkedFilesSymbolEquivalenceComparer.Instance).Any()) + if (!candidate.symbols.Intersect(bestBinding.symbols, LinkedFilesSymbolEquivalenceComparer.Instance).Any()) { - invalidProjects.Add(candidate.Item1.ProjectId); + invalidProjects.Add(candidate.docId.ProjectId); } } var supportedPlatforms = new SupportedPlatformData(invalidProjects, candidateProjects, document.Project.Solution.Workspace); - return await CreateContentAsync(document.Project.Solution.Workspace, token, bestBinding.Item2, bestBinding.Item3, supportedPlatforms, cancellationToken).ConfigureAwait(false); + + return (bestBinding.model, bestBinding.symbols, supportedPlatforms); } - private async Task FindTokenInLinkedDocument(SyntaxToken token, Document originalDocument, Document linkedDocument, CancellationToken cancellationToken) + private static bool HasNoErrors(ImmutableArray symbols) + => symbols.Length > 0 + && !ErrorVisitor.ContainsError(symbols.FirstOrDefault()); + + private async Task FindTokenInLinkedDocumentAsync( + SyntaxToken token, + Document originalDocument, + Document linkedDocument, + CancellationToken cancellationToken) { if (!linkedDocument.SupportsSyntaxTree) { @@ -142,7 +161,7 @@ private async Task FindTokenInLinkedDocument(SyntaxToken token, Doc return default; } - protected async Task CreateContentAsync( + protected async Task CreateContentAsync( Workspace workspace, SyntaxToken token, SemanticModel semanticModel, @@ -151,43 +170,59 @@ protected async Task CreateContentAsync( CancellationToken cancellationToken) { var descriptionService = workspace.Services.GetLanguageServices(token.Language).GetService(); + var formatter = workspace.Services.GetLanguageServices(semanticModel.Language).GetService(); + var syntaxFactsService = workspace.Services.GetLanguageServices(semanticModel.Language).GetService(); + var showWarningGlyph = supportedPlatforms != null && supportedPlatforms.HasValidAndInvalidProjects(); + var showSymbolGlyph = true; - var sections = await descriptionService.ToDescriptionGroupsAsync(workspace, semanticModel, token.SpanStart, symbols.AsImmutable(), cancellationToken).ConfigureAwait(false); + var groups = await descriptionService.ToDescriptionGroupsAsync(workspace, semanticModel, token.SpanStart, symbols.AsImmutable(), cancellationToken).ConfigureAwait(false); + bool TryGetGroupText(SymbolDescriptionGroups group, out ImmutableArray taggedParts) + => groups.TryGetValue(group, out taggedParts) && !taggedParts.IsDefaultOrEmpty; - var mainDescriptionBuilder = new List(); - if (sections.TryGetValue(SymbolDescriptionGroups.MainDescription, out var parts)) + var sections = ImmutableArray.CreateBuilder(initialCapacity: groups.Count); + + void AddSection(string kind, ImmutableArray taggedParts) + => sections.Add(QuickInfoSection.Create(kind, taggedParts)); + + if (TryGetGroupText(SymbolDescriptionGroups.MainDescription, out var mainDescriptionTaggedParts)) { - mainDescriptionBuilder.AddRange(parts); + AddSection(QuickInfoSectionKinds.Description, mainDescriptionTaggedParts); } - var typeParameterMapBuilder = new List(); - if (sections.TryGetValue(SymbolDescriptionGroups.TypeParameterMap, out parts)) + var documentationContent = GetDocumentationContent(symbols, groups, semanticModel, token, formatter, syntaxFactsService, cancellationToken); + if (syntaxFactsService.IsAwaitKeyword(token) && + (symbols.First() as INamedTypeSymbol)?.SpecialType == SpecialType.System_Void) { - if (!parts.IsDefaultOrEmpty) - { - typeParameterMapBuilder.AddLineBreak(); - typeParameterMapBuilder.AddRange(parts); - } + documentationContent = default; + showSymbolGlyph = false; } - var anonymousTypesBuilder = new List(); - if (sections.TryGetValue(SymbolDescriptionGroups.AnonymousTypes, out parts)) + if (!documentationContent.IsDefaultOrEmpty) { - if (!parts.IsDefaultOrEmpty) - { - anonymousTypesBuilder.AddLineBreak(); - anonymousTypesBuilder.AddRange(parts); - } + AddSection(QuickInfoSectionKinds.DocumentationComments, documentationContent); } - var usageTextBuilder = new List(); - if (sections.TryGetValue(SymbolDescriptionGroups.AwaitableUsageText, out parts)) + if (TryGetGroupText(SymbolDescriptionGroups.TypeParameterMap, out var typeParameterMapText)) { - if (!parts.IsDefaultOrEmpty) - { - usageTextBuilder.AddRange(parts); - } + var builder = ImmutableArray.CreateBuilder(); + builder.AddLineBreak(); + builder.AddRange(typeParameterMapText); + AddSection(QuickInfoSectionKinds.TypeParameters, builder.ToImmutable()); + } + + if (TryGetGroupText(SymbolDescriptionGroups.AnonymousTypes, out var anonymousTypesText)) + { + var builder = ImmutableArray.CreateBuilder(); + builder.AddLineBreak(); + builder.AddRange(anonymousTypesText); + AddSection(QuickInfoSectionKinds.AnonymousTypes, builder.ToImmutable()); + } + + var usageTextBuilder = ImmutableArray.CreateBuilder(); + if (TryGetGroupText(SymbolDescriptionGroups.AwaitableUsageText, out var awaitableUsageText)) + { + usageTextBuilder.AddRange(awaitableUsageText); } if (supportedPlatforms != null) @@ -195,41 +230,31 @@ protected async Task CreateContentAsync( usageTextBuilder.AddRange(supportedPlatforms.ToDisplayParts().ToTaggedText()); } - var exceptionsTextBuilder = new List(); - if (sections.TryGetValue(SymbolDescriptionGroups.Exceptions, out parts)) + if (usageTextBuilder.Count > 0) { - if (!parts.IsDefaultOrEmpty) - { - exceptionsTextBuilder.AddRange(parts); - } + AddSection(QuickInfoSectionKinds.Usage, usageTextBuilder.ToImmutable()); } - var formatter = workspace.Services.GetLanguageServices(semanticModel.Language).GetService(); - var syntaxFactsService = workspace.Services.GetLanguageServices(semanticModel.Language).GetService(); - var documentationContent = GetDocumentationContent(symbols, sections, semanticModel, token, formatter, syntaxFactsService, cancellationToken); - var showWarningGlyph = supportedPlatforms != null && supportedPlatforms.HasValidAndInvalidProjects(); - var showSymbolGlyph = true; + if (TryGetGroupText(SymbolDescriptionGroups.Exceptions, out var exceptionsText)) + { + AddSection(QuickInfoSectionKinds.Exception, exceptionsText); + } - if (workspace.Services.GetLanguageServices(semanticModel.Language).GetService().IsAwaitKeyword(token) && - (symbols.First() as INamedTypeSymbol)?.SpecialType == SpecialType.System_Void) + var tags = ImmutableArray.Empty; + if (showSymbolGlyph) { - documentationContent = CreateDocumentationCommentDeferredContent(null); - showSymbolGlyph = false; + tags = tags.AddRange(GlyphTags.GetTags(symbols.First().GetGlyph())); + } + + if (showWarningGlyph) + { + tags = tags.Add(WellKnownTags.Warning); } - return this.CreateQuickInfoDisplayDeferredContent( - symbol: symbols.First(), - showWarningGlyph: showWarningGlyph, - showSymbolGlyph: showSymbolGlyph, - mainDescription: mainDescriptionBuilder, - documentation: documentationContent, - typeParameterMap: typeParameterMapBuilder, - anonymousTypes: anonymousTypesBuilder, - usageText: usageTextBuilder, - exceptionText: exceptionsTextBuilder); + return QuickInfoItem.Create(token.Span, tags, sections.ToImmutable()); } - private IDeferredQuickInfoContent GetDocumentationContent( + private ImmutableArray GetDocumentationContent( IEnumerable symbols, IDictionary> sections, SemanticModel semanticModel, @@ -241,8 +266,8 @@ private IDeferredQuickInfoContent GetDocumentationContent( if (sections.TryGetValue(SymbolDescriptionGroups.Documentation, out var parts)) { var documentationBuilder = new List(); - documentationBuilder.AddRange(parts); - return CreateClassifiableDeferredContent(documentationBuilder); + documentationBuilder.AddRange(sections[SymbolDescriptionGroups.Documentation]); + return documentationBuilder.ToImmutableArray(); } else if (symbols.Any()) { @@ -259,17 +284,15 @@ private IDeferredQuickInfoContent GetDocumentationContent( if (documentation != null) { - return CreateClassifiableDeferredContent(documentation.ToList()); + return documentation.ToImmutableArray(); } } - return CreateDocumentationCommentDeferredContent(null); + return default; } - private async Task>> BindTokenAsync( - Document document, - SyntaxToken token, - CancellationToken cancellationToken) + private async Task<(SemanticModel semanticModel, ImmutableArray symbols)> BindTokenAsync( + Document document, SyntaxToken token, CancellationToken cancellationToken) { var semanticModel = await document.GetSemanticModelForNodeAsync(token.Parent, cancellationToken).ConfigureAwait(false); var enclosingType = semanticModel.GetEnclosingNamedType(token.SpanStart, cancellationToken); @@ -289,9 +312,8 @@ private async Task>> BindToken if (symbols.Any()) { var typeParameter = symbols.First() as ITypeParameterSymbol; - return ValueTuple.Create( - semanticModel, - typeParameter != null && typeParameter.TypeParameterKind == TypeParameterKind.Cref + return (semanticModel, + symbols: typeParameter != null && typeParameter.TypeParameterKind == TypeParameterKind.Cref ? ImmutableArray.Empty : symbols); } @@ -304,22 +326,20 @@ private async Task>> BindToken var typeInfo = semanticModel.GetTypeInfo(token.Parent, cancellationToken); if (IsOk(typeInfo.Type)) { - return ValueTuple.Create(semanticModel, - ImmutableArray.Create(typeInfo.Type)); + return (semanticModel, symbols: ImmutableArray.Create(typeInfo.Type)); } } - return ValueTuple.Create(semanticModel, ImmutableArray.Empty); + return (semanticModel, symbols: ImmutableArray.Empty); } private static bool IsOk(ISymbol symbol) - { - return symbol != null && !symbol.IsErrorType() && !symbol.IsAnonymousFunction(); - } + => symbol != null + && !symbol.IsErrorType() + && !symbol.IsAnonymousFunction(); private static bool IsAccessible(ISymbol symbol, INamedTypeSymbol within) - { - return within == null || symbol.IsAccessibleWithin(within); - } + => within == null + || symbol.IsAccessibleWithin(within); } } diff --git a/src/EditorFeatures/Core/Extensibility/QuickInfo/ExportQuickInfoProviderAttribute.cs b/src/Features/Core/Portable/QuickInfo/ExportQuickInfoProviderAttribute.cs similarity index 60% rename from src/EditorFeatures/Core/Extensibility/QuickInfo/ExportQuickInfoProviderAttribute.cs rename to src/Features/Core/Portable/QuickInfo/ExportQuickInfoProviderAttribute.cs index 594cb8265a61e..7565629b3bd03 100644 --- a/src/EditorFeatures/Core/Extensibility/QuickInfo/ExportQuickInfoProviderAttribute.cs +++ b/src/Features/Core/Portable/QuickInfo/ExportQuickInfoProviderAttribute.cs @@ -1,19 +1,23 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.ComponentModel.Composition; +using System.Composition; -namespace Microsoft.CodeAnalysis.Editor +namespace Microsoft.CodeAnalysis.QuickInfo { + /// + /// Use this attribute to export a so that it will + /// be found and used by the per language associated . + /// [MetadataAttribute] [AttributeUsage(AttributeTargets.Class)] - internal class ExportQuickInfoProviderAttribute : ExportAttribute + internal sealed class ExportQuickInfoProviderAttribute : ExportAttribute { public string Name { get; } public string Language { get; } public ExportQuickInfoProviderAttribute(string name, string language) - : base(typeof(IQuickInfoProvider)) + : base(typeof(QuickInfoProvider)) { this.Name = name ?? throw new ArgumentNullException(nameof(name)); this.Language = language ?? throw new ArgumentNullException(nameof(language)); diff --git a/src/Features/Core/Portable/QuickInfo/IndentationHelper.cs b/src/Features/Core/Portable/QuickInfo/IndentationHelper.cs new file mode 100644 index 0000000000000..2f8571e11cd5f --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/IndentationHelper.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + // Reproduces logic in IProjectionBufferFactoryServiceExtensions (editor layer) + // Used for tests currenty, but probably needed for other non-vs-editor API consumers. + internal static class IndentationHelper + { + /// + /// Recomputes span segments so that all text lines appear to have the same reduction in indentation. + /// This operation is typically used to align text for display when the initial span does not include all of the first line's identation. + /// This operation will potentially split spans that cover multiple lines into separate spans. + /// + /// + /// The initial set of spans to align. + /// The number of spaces to + /// + public static ImmutableArray GetSpansWithAlignedIndentation( + SourceText text, + ImmutableArray spans, + int tabSize) + { + if (!spans.IsDefault && spans.Length > 0) + { + // We need to figure out the shortest indentation level of the exposed lines. We'll + // then remove that indentation from all lines. + var indentationColumn = DetermineIndentationColumn(text, spans, tabSize); + + var adjustedSpans = new List(); + + for (var i = 0; i < spans.Length; i++) + { + var span = spans[i]; + var startLineNumber = text.Lines.GetLineFromPosition(span.Start).LineNumber; + var endLineNumber = text.Lines.GetLineFromPosition(span.End).LineNumber; + + for (var lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) + { + var line = text.Lines[lineNumber]; + var lineOffsetOfColumn = line.GetLineOffsetFromColumn(indentationColumn, tabSize); + + var deletion = TextSpan.FromBounds(line.Start, line.Start + lineOffsetOfColumn); + + if (deletion.Start > span.Start) + { + var spanBeforeDeletion = TextSpan.FromBounds(span.Start, Math.Min(span.End, deletion.Start)); + if (spanBeforeDeletion.Length > 0) + { + adjustedSpans.Add(spanBeforeDeletion); + } + } + + if (deletion.End > span.Start) + { + span = TextSpan.FromBounds(Math.Min(deletion.End, span.End), span.End); + } + } + + if (span.Length > 0) + { + adjustedSpans.Add(span); + } + } + + return adjustedSpans.ToImmutableArray(); + } + else + { + return ImmutableArray.Empty; + } + } + + private static int DetermineIndentationColumn( + SourceText text, + ImmutableArray spans, + int tabSize) + { + int? indentationColumn = null; + foreach (var span in spans) + { + var startLineNumber = text.Lines.GetLineFromPosition(span.Start).LineNumber; + var endLineNumber = text.Lines.GetLineFromPosition(span.End).LineNumber; + + // If the span starts after the first non-whitespace of the first line, we'll + // exclude that line to avoid throwing off the calculation. Otherwise, the + // incorrect indentation will be returned for lambda cases like so: + // + // void M() + // { + // Func f = () => + // { + // return 1; + // }; + // } + // + // Without throwing out the first line in the example above, the indentation column + // used will be 4, rather than 8. + var startLineFirstNonWhitespace = text.Lines[startLineNumber].GetFirstNonWhitespacePosition(); + if (startLineFirstNonWhitespace.HasValue && startLineFirstNonWhitespace.Value < span.Start) + { + startLineNumber++; + } + + for (var lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) + { + var line = text.Lines[lineNumber]; + if (line.IsEmptyOrWhitespace()) + { + continue; + } + + indentationColumn = indentationColumn.HasValue + ? Math.Min(indentationColumn.Value, line.GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(tabSize)) + : line.GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(tabSize); + } + } + + return indentationColumn ?? 0; + } + } +} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/LinkedFileDiscrepancyException.cs b/src/Features/Core/Portable/QuickInfo/LinkedFileDiscrepancyException.cs similarity index 90% rename from src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/LinkedFileDiscrepancyException.cs rename to src/Features/Core/Portable/QuickInfo/LinkedFileDiscrepancyException.cs index 396a015688c0e..9cc3ddebf79df 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Providers/LinkedFileDiscrepancyException.cs +++ b/src/Features/Core/Portable/QuickInfo/LinkedFileDiscrepancyException.cs @@ -2,7 +2,7 @@ using System; -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo +namespace Microsoft.CodeAnalysis.QuickInfo { // Used to aid the investigation of https://devdiv.visualstudio.com/DevDiv/_workitems?id=209299 internal class LinkedFileDiscrepancyException : Exception diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoContext.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoContext.cs new file mode 100644 index 0000000000000..134e5b0d31eb0 --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoContext.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + /// + /// The context presented to a when providing quick info. + /// + internal sealed class QuickInfoContext + { + /// + /// The document that quick info was requested within. + /// + public Document Document { get; } + + /// + /// The caret position where quick info was requested from. + /// + public int Position { get; } + + /// + /// The cancellation token to use for this operation. + /// + public CancellationToken CancellationToken { get; } + + /// + /// Creates a instance. + /// + public QuickInfoContext( + Document document, + int position, + CancellationToken cancellationToken) + { + this.Document = document ?? throw new ArgumentNullException(nameof(document)); + this.Position = position; + this.CancellationToken = cancellationToken; + } + } +} diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoItem.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoItem.cs new file mode 100644 index 0000000000000..27bb52320c061 --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoItem.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + public sealed class QuickInfoItem + { + /// + /// The span of the document that the item is based on. + /// + public TextSpan Span { get; } + + /// + /// Descriptive tags from the type. + /// These tags may influence how the item is displayed. + /// + public ImmutableArray Tags { get; } + + /// + /// One or more describing the item. + /// + public ImmutableArray Sections { get; } + + /// + /// Alternate regions of the document that help describe the item. + /// + public ImmutableArray RelatedSpans { get; } + + private QuickInfoItem( + TextSpan span, + ImmutableArray tags, + ImmutableArray sections, + ImmutableArray relatedSpans) + { + this.Span = span; + this.Tags = tags.IsDefault ? ImmutableArray.Empty : tags; + this.Sections = sections.IsDefault ? ImmutableArray.Empty : sections; + this.RelatedSpans = relatedSpans.IsDefault ? ImmutableArray.Empty : relatedSpans; + } + + public static QuickInfoItem Create( + TextSpan span, + ImmutableArray tags = default, + ImmutableArray sections = default, + ImmutableArray relatedSpans = default) + { + return new QuickInfoItem(span, tags, sections, relatedSpans); + } + } +} diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoProvider.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoProvider.cs new file mode 100644 index 0000000000000..579d87f4cfae8 --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoProvider.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + /// + /// A provider that produces 's. + /// Providers are used with some implementations. + /// + internal abstract class QuickInfoProvider + { + /// + /// Gets the for the position. + /// + /// The or null if no item is available. + public abstract Task GetQuickInfoAsync(QuickInfoContext context); + } +} diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoProviderMetadata.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoProviderMetadata.cs new file mode 100644 index 0000000000000..de0df15bbd52d --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoProviderMetadata.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.Host.Mef; +using System.Collections.Generic; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + internal class QuickInfoProviderMetadata : OrderableLanguageMetadata + { + public QuickInfoProviderMetadata(IDictionary data) + : base(data) + { + } + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoProviderNames.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoProviderNames.cs new file mode 100644 index 0000000000000..ba09d851c00fa --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoProviderNames.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + /// + /// Some of the known names in use. + /// Names are used for ordering providers with the . + /// + internal static class QuickInfoProviderNames + { + public const string Semantic = nameof(Semantic); + public const string Syntactic = nameof(Syntactic); + } +} diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoSection.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoSection.cs new file mode 100644 index 0000000000000..8630557205547 --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoSection.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Linq; +using System.Threading; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + /// + /// Sections are used to make up a . + /// + public sealed class QuickInfoSection + { + /// + /// The kind of this section. Use for the most common kinds. + /// + public string Kind { get; } + + /// + /// The individual tagged parts of this section. + /// + public ImmutableArray TaggedParts { get; } + + private QuickInfoSection(string kind, ImmutableArray taggedParts) + { + this.Kind = kind ?? string.Empty; + this.TaggedParts = taggedParts.NullToEmpty(); + } + + /// + /// Creates a new instance of . + /// + /// The kind of the section. Use for the most common kinds. + /// The individual tagged parts of the section. + public static QuickInfoSection Create(string kind, ImmutableArray taggedParts) + { + return new QuickInfoSection(kind, taggedParts); + } + + private string _text; + + /// + /// The text of the section without tags. + /// + public string Text + { + get + { + if (_text == null) + { + if (this.TaggedParts.Length == 0) + { + _text = string.Empty; + } + else + { + Interlocked.CompareExchange(ref _text, string.Concat(this.TaggedParts.Select(t => t.Text)), null); + } + } + + return _text; + } + } + } +} diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoSectionKinds.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoSectionKinds.cs new file mode 100644 index 0000000000000..684ed7fef9ff4 --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoSectionKinds.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + /// + /// The set of well known kinds used for the property. + /// These tags influence the presentation of quick info section. + /// + public static class QuickInfoSectionKinds + { + public const string Description = nameof(Description); + public const string DocumentationComments = nameof(DocumentationComments); + public const string TypeParameters = nameof(TypeParameters); + public const string AnonymousTypes = nameof(AnonymousTypes); + public const string Usage = nameof(Usage); + public const string Exception = nameof(Exception); + public const string Text = nameof(Text); + } +} diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoService.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoService.cs new file mode 100644 index 0000000000000..dd14b0bb3d2ab --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoService.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + /// + /// A service that is used to determine the appropriate quick info for a position in a document. + /// + public abstract class QuickInfoService : ILanguageService + { + /// + /// Gets the appropriate for the specified document. + /// + public static QuickInfoService GetService(Document document) + => document?.GetLanguageService(); + + /// + /// Gets the associated with position in the document. + /// + public virtual Task GetQuickInfoAsync( + Document document, + int position, + CancellationToken cancellationToken = default) + { + return SpecializedTasks.Default(); + } + } +} diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoServiceWithProviders.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoServiceWithProviders.cs new file mode 100644 index 0000000000000..1f6b42798d140 --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoServiceWithProviders.cs @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Extensions; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Utilities; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + /// + /// Base class for 's that delegate to 's. + /// + internal abstract class QuickInfoServiceWithProviders : QuickInfoService + { + private readonly Workspace _workspace; + private readonly string _language; + private ImmutableArray _providers; + + protected QuickInfoServiceWithProviders(Workspace workspace, string language) + { + _workspace = workspace; + _language = language; + } + + private ImmutableArray GetProviders() + { + if (_providers.IsDefault) + { + var mefExporter = (IMefHostExportProvider)_workspace.Services.HostServices; + + var providers = ExtensionOrderer + .Order(mefExporter.GetExports() + .Where(lz => lz.Metadata.Language == _language)) + .Select(lz => lz.Value) + .ToImmutableArray(); + + ImmutableInterlocked.InterlockedCompareExchange(ref _providers, providers, default(ImmutableArray)); + } + + return _providers; + } + + public override async Task GetQuickInfoAsync(Document document, int position, CancellationToken cancellationToken) + { + var extensionManager = _workspace.Services.GetService(); + + // returns the first non-empty quick info found (based on provider order) + foreach (var provider in GetProviders()) + { + try + { + if (!extensionManager.IsDisabled(provider)) + { + var context = new QuickInfoContext(document, position, cancellationToken); + + var info = await provider.GetQuickInfoAsync(context).ConfigureAwait(false); + if (info != null) + { + return info; + } + } + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception e) when (extensionManager.CanHandleException(provider, e)) + { + extensionManager.HandleException(provider, e); + } + } + + return null; + } + } +} diff --git a/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb b/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb index 848dc08292b35..9868c05825702 100644 --- a/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb +++ b/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb @@ -10,6 +10,7 @@ Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Imports Microsoft.CodeAnalysis.VisualBasic.Completion.SuggestionMode Imports Microsoft.CodeAnalysis.Host +Imports Microsoft.CodeAnalysis.Tags Namespace Microsoft.CodeAnalysis.VisualBasic.Completion @@ -45,7 +46,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion Private ReadOnly _workspace As Workspace Public Sub New(workspace As Workspace, - Optional exclusiveProviders As ImmutableArray(Of CompletionProvider) ? = Nothing) + Optional exclusiveProviders As ImmutableArray(Of CompletionProvider)? = Nothing) MyBase.New(workspace, exclusiveProviders) _workspace = workspace End Sub @@ -114,7 +115,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion ' the glyph when matching. Dim keywordCompletionItem = If(IsKeywordItem(existingItem), existingItem, If(IsKeywordItem(item), item, Nothing)) - If keywordCompletionItem IsNot Nothing AndAlso keywordCompletionItem.Tags.Contains(CompletionTags.Intrinsic) Then + If keywordCompletionItem IsNot Nothing AndAlso keywordCompletionItem.Tags.Contains(WellKnownTags.Intrinsic) Then Dim otherItem = If(keywordCompletionItem Is item, existingItem, item) Dim changeText = GetChangeText(otherItem) If changeText = keywordCompletionItem.DisplayText Then diff --git a/src/Features/VisualBasic/Portable/QuickInfo/VisualBasicQuickInfoService.vb b/src/Features/VisualBasic/Portable/QuickInfo/VisualBasicQuickInfoService.vb new file mode 100644 index 0000000000000..883c199b84c39 --- /dev/null +++ b/src/Features/VisualBasic/Portable/QuickInfo/VisualBasicQuickInfoService.vb @@ -0,0 +1,26 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Composition +Imports Microsoft.CodeAnalysis.Host +Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.QuickInfo + +Namespace Microsoft.CodeAnalysis.VisualBasic.QuickInfo + + Friend Class VisualBasicQuickInfoServiceFactory + Implements ILanguageServiceFactory + + Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService + Return New VisualBasicQuickInfoService(languageServices.WorkspaceServices.Workspace) + End Function + + End Class + + Friend Class VisualBasicQuickInfoService + Inherits QuickInfoServiceWithProviders + + Public Sub New(workspace As Workspace) + MyBase.New(workspace, LanguageNames.VisualBasic) + End Sub + End Class +End Namespace diff --git a/src/EditorFeatures/VisualBasic/QuickInfo/SemanticQuickInfoProvider.vb b/src/Features/VisualBasic/Portable/QuickInfo/VisualBasicSemanticQuickInfoProvider.vb similarity index 63% rename from src/EditorFeatures/VisualBasic/QuickInfo/SemanticQuickInfoProvider.vb rename to src/Features/VisualBasic/Portable/QuickInfo/VisualBasicSemanticQuickInfoProvider.vb index 68bf1a7128611..c392b00839c25 100644 --- a/src/EditorFeatures/VisualBasic/QuickInfo/SemanticQuickInfoProvider.vb +++ b/src/Features/VisualBasic/Portable/QuickInfo/VisualBasicSemanticQuickInfoProvider.vb @@ -1,40 +1,36 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports System.ComponentModel.Composition +Imports System.Collections.Immutable +Imports System.Composition Imports System.Threading -Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.Editor.Implementation.Intellisense.QuickInfo -Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities +Imports Microsoft.CodeAnalysis.QuickInfo Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators -Imports Microsoft.VisualStudio.Language.Intellisense -Imports Microsoft.VisualStudio.Text.Editor -Imports Microsoft.VisualStudio.Text.Projection -Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.QuickInfo +Namespace Microsoft.CodeAnalysis.VisualBasic.QuickInfo + + Friend Class VisualBasicSemanticQuickInfoProvider + Inherits CommonSemanticQuickInfoProvider - - Friend Class SemanticQuickInfoProvider - Inherits AbstractSemanticQuickInfoProvider + Protected Overrides Async Function BuildQuickInfoAsync( + document As Document, + token As SyntaxToken, + cancellationToken As CancellationToken) As Task(Of QuickInfoItem) - Protected Overrides Async Function BuildContentAsync(document As Document, - token As SyntaxToken, - cancellationToken As CancellationToken) As Task(Of IDeferredQuickInfoContent) - Dim vbToken = CType(token, SyntaxToken) - Dim parent = vbToken.Parent + Dim parent = token.Parent Dim predefinedCastExpression = TryCast(parent, PredefinedCastExpressionSyntax) - If predefinedCastExpression IsNot Nothing AndAlso vbToken = predefinedCastExpression.Keyword Then + If predefinedCastExpression IsNot Nothing AndAlso token = predefinedCastExpression.Keyword Then Dim compilation = Await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(False) Dim documentation = New PredefinedCastExpressionDocumentation(predefinedCastExpression.Keyword.Kind, compilation) - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, documentation, Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, documentation, Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) End If - Select Case vbToken.Kind + Select Case token.Kind Case SyntaxKind.AddHandlerKeyword If TypeOf parent Is AddRemoveHandlerStatementSyntax Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New AddHandlerStatementDocumentation(), Glyph.Keyword, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New AddHandlerStatementDocumentation(), Glyph.Keyword, cancellationToken).ConfigureAwait(False) End If Case SyntaxKind.DimKeyword @@ -46,56 +42,57 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.QuickInfo Case SyntaxKind.CTypeKeyword If TypeOf parent Is CTypeExpressionSyntax Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New CTypeCastExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New CTypeCastExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) End If Case SyntaxKind.DirectCastKeyword If TypeOf parent Is DirectCastExpressionSyntax Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New DirectCastExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New DirectCastExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) End If Case SyntaxKind.GetTypeKeyword If TypeOf parent Is GetTypeExpressionSyntax Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New GetTypeExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New GetTypeExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) End If Case SyntaxKind.GetXmlNamespaceKeyword If TypeOf parent Is GetXmlNamespaceExpressionSyntax Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New GetXmlNamespaceExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New GetXmlNamespaceExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) End If Case SyntaxKind.IfKeyword If parent.Kind = SyntaxKind.BinaryConditionalExpression Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New BinaryConditionalExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New BinaryConditionalExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) ElseIf parent.Kind = SyntaxKind.TernaryConditionalExpression Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New TernaryConditionalExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New TernaryConditionalExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) End If Case SyntaxKind.RemoveHandlerKeyword If TypeOf parent Is AddRemoveHandlerStatementSyntax Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New RemoveHandlerStatementDocumentation(), Glyph.Keyword, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New RemoveHandlerStatementDocumentation(), Glyph.Keyword, cancellationToken).ConfigureAwait(False) End If Case SyntaxKind.TryCastKeyword If TypeOf parent Is TryCastExpressionSyntax Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New TryCastExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New TryCastExpressionDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) End If Case SyntaxKind.IdentifierToken If SyntaxFacts.GetContextualKeywordKind(token.ToString()) = SyntaxKind.MidKeyword Then If parent.Kind = SyntaxKind.MidExpression Then - Return Await BuildContentForIntrinsicOperatorAsync(document, parent, New MidAssignmentDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) + Return Await BuildContentForIntrinsicOperatorAsync(document, token, parent, New MidAssignmentDocumentation(), Glyph.MethodPublic, cancellationToken).ConfigureAwait(False) End If End If End Select - Return Await MyBase.BuildContentAsync(document, token, cancellationToken).ConfigureAwait(False) + Return Await MyBase.BuildQuickInfoAsync(document, token, cancellationToken).ConfigureAwait(False) End Function - Private Overloads Async Function BuildContentAsync(document As Document, - token As SyntaxToken, - declarators As SeparatedSyntaxList(Of VariableDeclaratorSyntax), - cancellationToken As CancellationToken) As Task(Of IDeferredQuickInfoContent) + Private Overloads Async Function BuildContentAsync( + document As Document, + token As SyntaxToken, + declarators As SeparatedSyntaxList(Of VariableDeclaratorSyntax), + cancellationToken As CancellationToken) As Task(Of QuickInfoItem) If declarators.Count = 0 Then Return Nothing @@ -124,19 +121,18 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.QuickInfo End If If types.Count > 1 Then - Dim contentBuilder = New List(Of TaggedText) - contentBuilder.AddText(VBEditorResources.Multiple_Types) - Return Me.CreateClassifiableDeferredContent(contentBuilder) + Return QuickInfoItem.Create(token.Span, sections:=ImmutableArray.Create(QuickInfoSection.Create(QuickInfoSectionKinds.Description, ImmutableArray.Create(New TaggedText(TextTags.Text, VBFeaturesResources.Multiple_Types))))) End If Return Await CreateContentAsync(document.Project.Solution.Workspace, token, semantics, types, supportedPlatforms:=Nothing, cancellationToken:=cancellationToken).ConfigureAwait(False) End Function Private Async Function BuildContentForIntrinsicOperatorAsync(document As Document, + token As SyntaxToken, expression As SyntaxNode, documentation As AbstractIntrinsicOperatorDocumentation, glyph As Glyph, - cancellationToken As CancellationToken) As Task(Of IDeferredQuickInfoContent) + cancellationToken As CancellationToken) As Task(Of QuickInfoItem) Dim builder = New List(Of SymbolDisplayPart) builder.AddRange(documentation.PrefixParts) @@ -155,7 +151,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.QuickInfo If typeNameToBind IsNot Nothing Then ' We'll try to bind the type name - Dim typeInfo = SemanticModel.GetTypeInfo(typeNameToBind, cancellationToken) + Dim typeInfo = semanticModel.GetTypeInfo(typeNameToBind, cancellationToken) If typeInfo.Type IsNot Nothing Then builder.AddRange(typeInfo.Type.ToMinimalDisplayParts(semanticModel, position)) @@ -168,14 +164,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.QuickInfo builder.AddRange(documentation.GetSuffix(semanticModel, position, expression, cancellationToken)) - Return CreateQuickInfoDisplayDeferredContent( - glyph, - builder.ToTaggedText(), - CreateDocumentationCommentDeferredContent(documentation.DocumentationText), - SpecializedCollections.EmptyList(Of TaggedText), - SpecializedCollections.EmptyList(Of TaggedText), - SpecializedCollections.EmptyList(Of TaggedText), - SpecializedCollections.EmptyList(Of TaggedText)) + Return QuickInfoItem.Create( + token.Span, + tags:=GlyphTags.GetTags(glyph), + sections:=ImmutableArray.Create( + QuickInfoSection.Create(QuickInfoSectionKinds.Description, builder.ToTaggedText()), + QuickInfoSection.Create(QuickInfoSectionKinds.DocumentationComments, ImmutableArray.Create(New TaggedText(TextTags.Text, documentation.DocumentationText))))) End Function End Class End Namespace + diff --git a/src/Features/VisualBasic/Portable/VBFeaturesResources.Designer.vb b/src/Features/VisualBasic/Portable/VBFeaturesResources.Designer.vb index 6f7b10494bd75..19883611b9a77 100644 --- a/src/Features/VisualBasic/Portable/VBFeaturesResources.Designer.vb +++ b/src/Features/VisualBasic/Portable/VBFeaturesResources.Designer.vb @@ -1583,6 +1583,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VBFeaturesResources End Get End Property + ''' + ''' Looks up a localized string similar to <Multiple Types>. + ''' + Friend ReadOnly Property Multiple_Types() As String + Get + Return ResourceManager.GetString("Multiple_Types", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Multiplies two numbers and returns the product.. ''' diff --git a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx index a3b390c38d98a..7e947d750ba6c 100644 --- a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx +++ b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx @@ -1235,6 +1235,9 @@ Sub(<parameterList>) <statement> Make Async Sub + + <Multiple Types> + Add 'Me.' diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf index 09a401402ab0e..d5e584c2ccc51 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf @@ -1817,6 +1817,11 @@ Sub(<seznam_parametrů>) <výraz> Použít kontrolu „IsNot Nothing“ + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf index 57d367731bfab..f547399d8cda7 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf @@ -1817,6 +1817,11 @@ Sub(<Parameterliste>) <Ausdruck> Prüfung "IsNot Nothing" verwenden + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf index 58aa22694490e..909803d92c46b 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf @@ -1817,6 +1817,11 @@ Sub(<listaDeParámetros>) <instrucción> Usar comprobación "IsNot Nothing" + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf index 4abbf3ba372a7..380263c9fad79 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf @@ -1817,6 +1817,11 @@ Sub(<parameterList>) <statement> Utiliser la vérification 'IsNot Nothing' + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf index 6931d559638d8..d0ebe150a1ace 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf @@ -1817,6 +1817,11 @@ Sub(<elencoParametri>) <istruzione> Usa controllo 'IsNot Nothing' + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf index e2bc488452a83..28e29a07eb8a7 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf @@ -1816,6 +1816,11 @@ Sub(<parameterList>) <statement> IsNot Nothing' チェックを使用します + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf index a7ee85331ac74..2f8f313d0f9eb 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf @@ -1817,6 +1817,11 @@ Sub(<parameterList>) <statement> IsNot Nothing' 검사 사용 + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf index 99f5a5e39cc0e..990e8a6aeb659 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf @@ -1817,6 +1817,11 @@ Sub(<listaParametrów>) <instrukcja> Użyj sprawdzania „IsNot Nothing” + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf index 044c98ab074fa..7d07e141553f9 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf @@ -1817,6 +1817,11 @@ Sub(<parameterList>) <statement> Usar a verificação 'IsNot Nothing' + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf index bf14e99e9e58b..3e4110b994513 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf @@ -1817,6 +1817,11 @@ Sub(<parameterList>) <statement> Использовать флажок "IsNot Nothing" + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf index 077f256dbb4b1..322c459b6ed29 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf @@ -1817,6 +1817,11 @@ Sub(<parameterList>) <statement> IsNot Nothing' denetimi kullan + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf index fb2f2ac6ead14..68fd850e64c15 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf @@ -1817,6 +1817,11 @@ Sub(<parameterList>) <statement> 使用 "IsNot Nothing" 检查 + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf index d16c470ed4c00..75e955c890292 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf @@ -1817,6 +1817,11 @@ Sub(<parameterList>) <statement> 使用 'IsNot Nothing' 檢查 + + <Multiple Types> + <Multiple Types> + + \ No newline at end of file diff --git a/src/VisualStudio/CSharp/Impl/EventHookup/EventHookupQuickInfoSourceProvider.cs b/src/VisualStudio/CSharp/Impl/EventHookup/EventHookupQuickInfoSourceProvider.cs index 9b5acd1e80e5c..74abf97f0f268 100644 --- a/src/VisualStudio/CSharp/Impl/EventHookup/EventHookupQuickInfoSourceProvider.cs +++ b/src/VisualStudio/CSharp/Impl/EventHookup/EventHookupQuickInfoSourceProvider.cs @@ -15,9 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.EventHookup /// added due to errors in the code (which happen right after "eventName +=") /// [Export(typeof(IQuickInfoSourceProvider))] - [Name(PredefinedQuickInfoProviderNames.EventHookup)] - [Order(After = PredefinedQuickInfoProviderNames.Semantic)] - [Order(After = PredefinedQuickInfoProviderNames.Syntactic)] + [Name(PredefinedQuickInfoSourceProviderNames.EventHookup)] [Order(After = "squiggle")] [ContentType(ContentTypeNames.CSharpContentType)] internal sealed class EventHookupQuickInfoSourceProvider : IQuickInfoSourceProvider diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs index 8e2998848e2dc..c162991f78ae2 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs @@ -2,15 +2,14 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Editor; -using Microsoft.CodeAnalysis.Editor.FindUsages; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; +using Microsoft.CodeAnalysis.Editor.QuickInfo; using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Preview; @@ -141,12 +140,13 @@ private ContentControl CreateToolTipContent(ITextBuffer textBuffer) PredefinedTextViewRoles.Document, PredefinedTextViewRoles.Editable); - var content = new ProjectionBufferDeferredContent( - snapshotSpan, + return ProjectionBufferContent.Create( + ImmutableArray.Create(snapshotSpan), + Presenter.ProjectionBufferFactoryService, + Presenter.EditorOptionsFactoryService, + Presenter.TextEditorFactoryService, contentType, roleSet); - - return (ContentControl)Presenter.DeferredContentFrameworkElementFactory.CreateElement(content); } private ITextBuffer CreateNewBuffer() diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/Entry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/Entry.cs index 87f846ee51172..ed1104e0ffaa4 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/Entry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/Entry.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Windows; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Wpf; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.Shell.TableControl; namespace Microsoft.VisualStudio.LanguageServices.FindUsages @@ -32,11 +31,11 @@ private object GetValue(string keyName) { switch (keyName) { - case StandardTableKeyNames2.Definition: - return DefinitionBucket; + case StandardTableKeyNames2.Definition: + return DefinitionBucket; - case StandardTableKeyNames2.DefinitionIcon: - return DefinitionBucket.DefinitionItem.Tags.GetGlyph().GetImageMoniker(); + case StandardTableKeyNames2.DefinitionIcon: + return DefinitionBucket.DefinitionItem.Tags.GetFirstGlyph().GetImageMoniker(); } return GetValueWorker(keyName); diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs index fd0575d35bf0e..850567948b0d4 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs @@ -62,7 +62,7 @@ private object GetValue(string key) return inlines; case StandardTableKeyNames2.DefinitionIcon: - return DefinitionItem.Tags.GetGlyph().GetImageMoniker(); + return DefinitionItem.Tags.GetFirstGlyph().GetImageMoniker(); } return null; diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/StreamingFindUsagesPresenter.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/StreamingFindUsagesPresenter.cs index 680ef8ff0ad1b..fbdc81e8ed7d0 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/StreamingFindUsagesPresenter.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/StreamingFindUsagesPresenter.cs @@ -3,9 +3,7 @@ using System; using System.Collections.Generic; using System.Composition; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Host; -using Microsoft.CodeAnalysis.Editor.QuickInfo; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.PooledObjects; @@ -37,7 +35,8 @@ internal partial class StreamingFindUsagesPresenter : public readonly ClassificationTypeMap TypeMap; public readonly IEditorFormatMapService FormatMapService; public readonly IClassificationFormatMap ClassificationFormatMap; - public readonly DeferredContentFrameworkElementFactory DeferredContentFrameworkElementFactory; + public readonly IProjectionBufferFactoryService ProjectionBufferFactoryService; + public readonly IEditorOptionsFactoryService EditorOptionsFactoryService; private readonly IFindAllReferencesService _vsFindAllReferencesService; private readonly VisualStudioWorkspace _workspace; @@ -52,21 +51,23 @@ public StreamingFindUsagesPresenter( ITextBufferFactoryService textBufferFactoryService, ITextEditorFactoryService textEditorFactoryService, IContentTypeRegistryService contentTypeRegistryService, - DeferredContentFrameworkElementFactory frameworkElementFactory, ClassificationTypeMap typeMap, IEditorFormatMapService formatMapService, - IClassificationFormatMapService classificationFormatMapService) + IClassificationFormatMapService classificationFormatMapService, + IProjectionBufferFactoryService projectionBufferFactoryService, + IEditorOptionsFactoryService editorOptionsFactoryService) { _workspace = workspace; _serviceProvider = serviceProvider; TextBufferFactoryService = textBufferFactoryService; ContentTypeRegistryService = contentTypeRegistryService; - DeferredContentFrameworkElementFactory = frameworkElementFactory; TextEditorFactoryService = textEditorFactoryService; TypeMap = typeMap; FormatMapService = formatMapService; ClassificationFormatMap = classificationFormatMapService.GetClassificationFormatMap("tooltip"); + ProjectionBufferFactoryService = projectionBufferFactoryService; + EditorOptionsFactoryService = editorOptionsFactoryService; _vsFindAllReferencesService = (IFindAllReferencesService)_serviceProvider.GetService(typeof(SVsFindAllReferences)); } diff --git a/src/VisualStudio/Core/Def/Implementation/Library/FindResults/LibraryManager_FindReferences.cs b/src/VisualStudio/Core/Def/Implementation/Library/FindResults/LibraryManager_FindReferences.cs index 80e61adc261be..e5982bc67865f 100644 --- a/src/VisualStudio/Core/Def/Implementation/Library/FindResults/LibraryManager_FindReferences.cs +++ b/src/VisualStudio/Core/Def/Implementation/Library/FindResults/LibraryManager_FindReferences.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Roslyn.Utilities; @@ -52,7 +51,7 @@ private ImmutableArray CreateReferenceItems( { var result = ImmutableArray.CreateBuilder(); - var definitionGlyph = definitionItem.Tags.GetGlyph(); + var definitionGlyph = definitionItem.Tags.GetFirstGlyph(); // Skip the first definition. We'll present it in the definition item. var definitionLocationsAndGlyphs = diff --git a/src/VisualStudio/Core/Def/Implementation/Library/FindResults/TreeItems/DefinitionTreeItem.cs b/src/VisualStudio/Core/Def/Implementation/Library/FindResults/TreeItems/DefinitionTreeItem.cs index 292e6b915c745..2cfb6fbe11b79 100644 --- a/src/VisualStudio/Core/Def/Implementation/Library/FindResults/TreeItems/DefinitionTreeItem.cs +++ b/src/VisualStudio/Core/Def/Implementation/Library/FindResults/TreeItems/DefinitionTreeItem.cs @@ -3,7 +3,6 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Roslyn.Utilities; @@ -21,7 +20,7 @@ public DefinitionTreeItem( Workspace workspace, DefinitionItem definitionItem, ImmutableArray referenceItems) - : base(definitionItem.Tags.GetGlyph().GetGlyphIndex()) + : base(definitionItem.Tags.GetFirstGlyph().GetGlyphIndex()) { _workspace = workspace; _definitionItem = definitionItem; diff --git a/src/VisualStudio/Core/Def/Shared/VisualStudioImageMonikerService.cs b/src/VisualStudio/Core/Def/Shared/VisualStudioImageMonikerService.cs index 90ed147c362cd..9143c88db88e8 100644 --- a/src/VisualStudio/Core/Def/Shared/VisualStudioImageMonikerService.cs +++ b/src/VisualStudio/Core/Def/Shared/VisualStudioImageMonikerService.cs @@ -6,7 +6,6 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Tags; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Imaging.Interop; @@ -56,7 +55,7 @@ public bool TryGetImageMoniker(ImmutableArray tags, out ImageMoniker ima private ImageMoniker GetImageMoniker(ImmutableArray tags) { - var glyph = tags.GetGlyph(); + var glyph = tags.GetFirstGlyph(); switch (glyph) { case Glyph.AddReference: diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/TextLineExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/TextLineExtensions.cs index 4efd7797ecd9a..12e0ec4a70068 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/TextLineExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/TextLineExtensions.cs @@ -55,7 +55,16 @@ public static string GetLeadingWhitespace(this TextLine line) /// public static bool IsEmptyOrWhitespace(this TextLine line) { - return string.IsNullOrWhiteSpace(line.ToString()); + var text = line.Text; + for (var i = line.Span.Start; i < line.Span.End; i++) + { + if (!char.IsWhiteSpace(text[i])) + { + return false; + } + } + + return true; } public static int GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(this TextLine line, int tabSize)