diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs index 36a12b89f69f1..02fe8aa17cc07 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.CSharp.Completion.Providers; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; @@ -2490,8 +2489,8 @@ public override void set_Bar(int bay, int value) if (service.GetProvider(completionItem, document.Project) is ICustomCommitCompletionProvider customCommitCompletionProvider) { - var textView = testWorkspace.GetTestDocument(documentId)!.GetTextView(); - customCommitCompletionProvider.Commit(completionItem, textView, textView.TextBuffer, textView.TextSnapshot, '\t'); + var textView = testDocument.GetTextView(); + customCommitCompletionProvider.Commit(completionItem, document, textView, textView.TextBuffer, textView.TextSnapshot, '\t'); var actualCodeAfterCommit = textView.TextBuffer.CurrentSnapshot.AsText().ToString(); var caretPosition = textView.Caret.Position.BufferPosition.Position; MarkupTestFile.GetPosition(csharpFileAfterCommit, out var actualExpectedCode, out int expectedCaretPosition); @@ -2747,8 +2746,8 @@ public override bool Equals(object obj) if (service.GetProvider(completionItem, document.Project) is ICustomCommitCompletionProvider customCommitCompletionProvider) { - var textView = testWorkspace.GetTestDocument(documentId)!.GetTextView(); - customCommitCompletionProvider.Commit(completionItem, textView, textView.TextBuffer, textView.TextSnapshot, '\t'); + var textView = testDocument.GetTextView(); + customCommitCompletionProvider.Commit(completionItem, document, textView, textView.TextBuffer, textView.TextSnapshot, '\t'); var actualCodeAfterCommit = textView.TextBuffer.CurrentSnapshot.AsText().ToString(); var caretPosition = textView.Caret.Position.BufferPosition.Position; MarkupTestFile.GetPosition(csharpFileAfterCommit, out var actualExpectedCode, out int expectedCaretPosition); @@ -2803,8 +2802,8 @@ public override bool Equals(object obj) if (service.GetProvider(completionItem, document.Project) is ICustomCommitCompletionProvider customCommitCompletionProvider) { - var textView = testWorkspace.GetTestDocument(documentId)!.GetTextView(); - customCommitCompletionProvider.Commit(completionItem, textView, textView.TextBuffer, textView.TextSnapshot, '\t'); + var textView = testDocument.GetTextView(); + customCommitCompletionProvider.Commit(completionItem, document, textView, textView.TextBuffer, textView.TextSnapshot, '\t'); var actualCodeAfterCommit = textView.TextBuffer.CurrentSnapshot.AsText().ToString(); var caretPosition = textView.Caret.Position.BufferPosition.Position; MarkupTestFile.GetPosition(csharpFileAfterCommit, out var actualExpectedCode, out int expectedCaretPosition); diff --git a/src/EditorFeatures/Core/Extensibility/Completion/ICustomCommitCompletionProvider.cs b/src/EditorFeatures/Core/Extensibility/Completion/ICustomCommitCompletionProvider.cs index f9839e39bd277..9711a7b613d43 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/ICustomCommitCompletionProvider.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/ICustomCommitCompletionProvider.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Microsoft.CodeAnalysis.Completion; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; @@ -16,6 +14,6 @@ namespace Microsoft.CodeAnalysis.Editor /// internal interface ICustomCommitCompletionProvider { - void Commit(CompletionItem completionItem, ITextView textView, ITextBuffer subjectBuffer, ITextSnapshot triggerSnapshot, char? commitChar); + void Commit(CompletionItem completionItem, Document document, ITextView textView, ITextBuffer subjectBuffer, ITextSnapshot triggerSnapshot, char? commitChar); } } diff --git a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs index 0c8f84d08feb8..0bdf181116093 100644 --- a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs +++ b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs @@ -243,7 +243,7 @@ private AsyncCompletionData.CommitResult Commit( var provider = completionService.GetProvider(roslynItem, document.Project); if (provider is ICustomCommitCompletionProvider customCommitProvider) { - customCommitProvider.Commit(roslynItem, view, subjectBuffer, triggerSnapshot, commitCharacter); + customCommitProvider.Commit(roslynItem, document, view, subjectBuffer, triggerSnapshot, commitCharacter); return new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.None); } diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index 9182640df9684..544d6dd3f804e 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -14,16 +14,12 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.InteractiveWindow; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; @@ -541,10 +537,11 @@ private void VerifyCustomCommitWorker( // textview is created lazily, so need to access it before making // changes to document, so the cursor position is tracked correctly. + var document = workspace.CurrentSolution.GetRequiredDocument(workspaceFixture.Target.CurrentDocument.Id); var textView = workspaceFixture.Target.CurrentDocument.GetTextView(); var textBuffer = workspaceFixture.Target.CurrentDocument.GetTextBuffer(); - customCommitCompletionProvider.Commit(completionItem, textView, textBuffer, textView.TextSnapshot, commitChar); + customCommitCompletionProvider.Commit(completionItem, document, textView, textBuffer, textView.TextSnapshot, commitChar); var actualCodeAfterCommit = textBuffer.CurrentSnapshot.AsText().ToString(); var caretPosition = textView.Caret.Position.BufferPosition.Position; diff --git a/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb b/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb index 073d3e22b342d..9a550da860a3b 100644 --- a/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb +++ b/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb @@ -373,7 +373,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Async Function AssertCompletionItemsContainAll(ParamArray displayText As String()) As Task Await WaitForAsynchronousOperationsAsync() Dim items = GetCompletionItems() - Assert.True(displayText.All(Function(v) items.Any(Function(i) i.DisplayText = v))) + Assert.All(displayText, Sub(v) Assert.Contains(v, items.Select(Function(i) i.DisplayText))) End Function Public Async Function AssertCompletionItemsContain(displayText As String, displayTextSuffix As String) As Task diff --git a/src/VisualStudio/CSharp/Impl/Snippets/SnippetExpansionClient.cs b/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs similarity index 73% rename from src/VisualStudio/CSharp/Impl/Snippets/SnippetExpansionClient.cs rename to src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs index e31f7dea9d69d..e4424fea36c34 100644 --- a/src/VisualStudio/CSharp/Impl/Snippets/SnippetExpansionClient.cs +++ b/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs @@ -4,74 +4,55 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; +using System.Composition; using System.Linq; using System.Threading; using System.Xml.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.VisualStudio.Editor; -using Microsoft.VisualStudio.LanguageServices.Implementation.Snippets; +using Microsoft.VisualStudio.LanguageServices.Snippets; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Editor.Commanding; using Microsoft.VisualStudio.TextManager.Interop; -using MSXML; using Roslyn.Utilities; using VsTextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan; namespace Microsoft.VisualStudio.LanguageServices.CSharp.Snippets { - internal sealed partial class SnippetExpansionClient : AbstractSnippetExpansionClient + [ExportLanguageService(typeof(ISnippetExpansionLanguageHelper), LanguageNames.CSharp)] + [Shared] + internal class CSharpSnippetExpansionLanguageHelper : AbstractSnippetExpansionLanguageHelper { - public SnippetExpansionClient( - IThreadingContext threadingContext, - Guid languageServiceGuid, - ITextView textView, - ITextBuffer subjectBuffer, - SignatureHelpControllerProvider signatureHelpControllerProvider, - IEditorCommandHandlerServiceFactory editorCommandHandlerServiceFactory, - IVsEditorAdaptersFactoryService editorAdaptersFactoryService, - ImmutableArray> argumentProviders, - EditorOptionsService editorOptionsService) - : base( - threadingContext, - languageServiceGuid, - textView, - subjectBuffer, - signatureHelpControllerProvider, - editorCommandHandlerServiceFactory, - editorAdaptersFactoryService, - argumentProviders, - editorOptionsService) + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CSharpSnippetExpansionLanguageHelper() { } + public override Guid LanguageServiceGuid => Guids.CSharpLanguageServiceId; + public override string FallbackDefaultLiteral => "default"; + /// The tracking span of the inserted "/**/" if there is an $end$ location, null /// otherwise. - protected override ITrackingSpan? InsertEmptyCommentAndGetEndPositionTrackingSpan() + public override ITrackingSpan? InsertEmptyCommentAndGetEndPositionTrackingSpan(IVsExpansionSession expansionSession, ITextView textView, ITextBuffer subjectBuffer) { - RoslynDebug.AssertNotNull(ExpansionSession); + RoslynDebug.AssertNotNull(expansionSession); var endSpanInSurfaceBuffer = new VsTextSpan[1]; - if (ExpansionSession.GetEndSpan(endSpanInSurfaceBuffer) != VSConstants.S_OK) + if (expansionSession.GetEndSpan(endSpanInSurfaceBuffer) != VSConstants.S_OK) { return null; } - if (!TryGetSubjectBufferSpan(endSpanInSurfaceBuffer[0], out var subjectBufferEndSpan)) + if (!TryGetSubjectBufferSpan(textView, subjectBuffer, endSpanInSurfaceBuffer[0], out var subjectBufferEndSpan)) { return null; } @@ -79,15 +60,13 @@ public SnippetExpansionClient( var endPosition = subjectBufferEndSpan.Start.Position; var commentString = "/**/"; - SubjectBuffer.Insert(endPosition, commentString); + subjectBuffer.Insert(endPosition, commentString); var commentSpan = new Span(endPosition, commentString.Length); - return SubjectBuffer.CurrentSnapshot.CreateTrackingSpan(commentSpan, SpanTrackingMode.EdgeExclusive); + return subjectBuffer.CurrentSnapshot.CreateTrackingSpan(commentSpan, SpanTrackingMode.EdgeExclusive); } - protected override string FallbackDefaultLiteral => "default"; - - internal override Document AddImports( + public override Document AddImports( Document document, AddImportPlacementOptions addImportOptions, SyntaxFormattingOptions formattingOptions, @@ -131,7 +110,7 @@ internal override Document AddImports( return formattedDocument; } - private static IList GetUsingDirectivesToAdd( + private static List GetUsingDirectivesToAdd( SyntaxNode contextLocation, XElement snippetNode, XElement importsNode) { var namespaceXmlName = XName.Get("Namespace", snippetNode.Name.NamespaceName); diff --git a/src/VisualStudio/CSharp/Impl/Snippets/SnippetCommandHandler.cs b/src/VisualStudio/CSharp/Impl/Snippets/SnippetCommandHandler.cs index 3b4ac8be43bbb..62d64c8962b3a 100644 --- a/src/VisualStudio/CSharp/Impl/Snippets/SnippetCommandHandler.cs +++ b/src/VisualStudio/CSharp/Impl/Snippets/SnippetCommandHandler.cs @@ -5,16 +5,11 @@ #nullable disable using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.ComponentModel.Composition; -using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Editor.CSharp.CompleteStatement; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; @@ -24,11 +19,10 @@ using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; using Microsoft.VisualStudio.LanguageServices.Implementation.Snippets; -using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Editor.Commanding; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; +using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities; namespace Microsoft.VisualStudio.LanguageServices.CSharp.Snippets @@ -45,21 +39,18 @@ internal sealed class SnippetCommandHandler : ICommandHandler, IChainedCommandHandler { - private readonly ImmutableArray> _argumentProviders; + private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService; [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public SnippetCommandHandler( IThreadingContext threadingContext, - SignatureHelpControllerProvider signatureHelpControllerProvider, - IEditorCommandHandlerServiceFactory editorCommandHandlerServiceFactory, IVsEditorAdaptersFactoryService editorAdaptersFactoryService, - SVsServiceProvider serviceProvider, - [ImportMany] IEnumerable> argumentProviders, + IVsService textManager, EditorOptionsService editorOptionsService) - : base(threadingContext, signatureHelpControllerProvider, editorCommandHandlerServiceFactory, editorAdaptersFactoryService, editorOptionsService, serviceProvider) + : base(threadingContext, editorOptionsService, textManager) { - _argumentProviders = argumentProviders.ToImmutableArray(); + _editorAdaptersFactoryService = editorAdaptersFactoryService; } public bool ExecuteCommand(SurroundWithCommandArgs args, CommandExecutionContext context) @@ -101,8 +92,7 @@ public void ExecuteCommand(TypeCharCommandArgs args, Action nextCommandHandler, { AssertIsForeground(); if (args.TypedChar == ';' - && AreSnippetsEnabled(args) - && args.TextView.Properties.TryGetProperty(typeof(AbstractSnippetExpansionClient), out AbstractSnippetExpansionClient snippetExpansionClient) + && AreSnippetsEnabledWithClient(args, out var snippetExpansionClient) && snippetExpansionClient.IsFullMethodCallSnippet) { // Commit the snippet. Leave the caret in place, but clear the selection. Subsequent handlers in the @@ -115,27 +105,6 @@ public void ExecuteCommand(TypeCharCommandArgs args, Action nextCommandHandler, nextCommandHandler(); } - protected override AbstractSnippetExpansionClient GetSnippetExpansionClient(ITextView textView, ITextBuffer subjectBuffer) - { - if (!textView.Properties.TryGetProperty(typeof(AbstractSnippetExpansionClient), out AbstractSnippetExpansionClient expansionClient)) - { - expansionClient = new SnippetExpansionClient( - ThreadingContext, - Guids.CSharpLanguageServiceId, - textView, - subjectBuffer, - SignatureHelpControllerProvider, - EditorCommandHandlerServiceFactory, - EditorAdaptersFactoryService, - _argumentProviders, - EditorOptionsService); - - textView.Properties.AddProperty(typeof(AbstractSnippetExpansionClient), expansionClient); - } - - return expansionClient; - } - protected override bool TryInvokeInsertionUI(ITextView textView, ITextBuffer subjectBuffer, bool surroundWith = false) { if (!TryGetExpansionManager(out var expansionManager)) @@ -143,9 +112,13 @@ protected override bool TryInvokeInsertionUI(ITextView textView, ITextBuffer sub return false; } + var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); + if (document == null) + return false; + expansionManager.InvokeInsertionUI( - EditorAdaptersFactoryService.GetViewAdapter(textView), - GetSnippetExpansionClient(textView, subjectBuffer), + _editorAdaptersFactoryService.GetViewAdapter(textView), + GetSnippetExpansionClientFactory(document).GetOrCreateSnippetExpansionClient(document, textView, subjectBuffer), Guids.CSharpLanguageServiceId, bstrTypes: surroundWith ? ["SurroundsWith"] : ["Expansion", "SurroundsWith"], iCountTypes: surroundWith ? 1 : 2, diff --git a/src/VisualStudio/Core/Def/Snippets/AbstractSnippetCommandHandler.cs b/src/VisualStudio/Core/Def/Snippets/AbstractSnippetCommandHandler.cs index c2ca6270624a9..a64930f9c0132 100644 --- a/src/VisualStudio/Core/Def/Snippets/AbstractSnippetCommandHandler.cs +++ b/src/VisualStudio/Core/Def/Snippets/AbstractSnippetCommandHandler.cs @@ -5,11 +5,10 @@ #nullable disable using System; +using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; @@ -17,8 +16,6 @@ using Microsoft.CodeAnalysis.Snippets; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Commanding; -using Microsoft.VisualStudio.Editor; -using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding; @@ -38,31 +35,24 @@ internal abstract class AbstractSnippetCommandHandler : ICommandHandler, IChainedCommandHandler { - protected readonly SignatureHelpControllerProvider SignatureHelpControllerProvider; - protected readonly IEditorCommandHandlerServiceFactory EditorCommandHandlerServiceFactory; - protected readonly IVsEditorAdaptersFactoryService EditorAdaptersFactoryService; - protected readonly EditorOptionsService EditorOptionsService; - protected readonly SVsServiceProvider ServiceProvider; + private readonly EditorOptionsService _editorOptionsService; + private readonly IVsService _textManager; public string DisplayName => FeaturesResources.Snippets; public AbstractSnippetCommandHandler( IThreadingContext threadingContext, - SignatureHelpControllerProvider signatureHelpControllerProvider, - IEditorCommandHandlerServiceFactory editorCommandHandlerServiceFactory, - IVsEditorAdaptersFactoryService editorAdaptersFactoryService, EditorOptionsService editorOptionsService, - SVsServiceProvider serviceProvider) + IVsService textManager) : base(threadingContext) { - SignatureHelpControllerProvider = signatureHelpControllerProvider; - EditorCommandHandlerServiceFactory = editorCommandHandlerServiceFactory; - EditorAdaptersFactoryService = editorAdaptersFactoryService; - EditorOptionsService = editorOptionsService; - ServiceProvider = serviceProvider; + _editorOptionsService = editorOptionsService; + _textManager = textManager; } - protected abstract AbstractSnippetExpansionClient GetSnippetExpansionClient(ITextView textView, ITextBuffer subjectBuffer); + protected ISnippetExpansionClientFactory GetSnippetExpansionClientFactory(Document document) + => document.Project.Services.SolutionServices.GetRequiredService(); + protected abstract bool IsSnippetExpansionContext(Document document, int startPosition, CancellationToken cancellationToken); protected abstract bool TryInvokeInsertionUI(ITextView textView, ITextBuffer subjectBuffer, bool surroundWith = false); @@ -72,15 +62,15 @@ protected virtual bool TryInvokeSnippetPickerOnQuestionMark(ITextView textView, public bool ExecuteCommand(TabKeyCommandArgs args, CommandExecutionContext context) { AssertIsForeground(); - if (!AreSnippetsEnabled(args)) + if (AreSnippetsEnabledWithClient(args, out var snippetExpansionClient) + && snippetExpansionClient.TryHandleTab()) { - return false; + return true; } - if (args.TextView.Properties.TryGetProperty(typeof(AbstractSnippetExpansionClient), out AbstractSnippetExpansionClient snippetExpansionClient) && - snippetExpansionClient.TryHandleTab()) + if (!AreSnippetsEnabled(args)) { - return true; + return false; } // Insert snippet/show picker only if we don't have a selection: the user probably wants to indent instead @@ -125,8 +115,7 @@ public CommandState GetCommandState(AutomaticLineEnderCommandArgs args, Func _textManager.GetValueOrNullAsync(CancellationToken.None)); if (textManager == null) { expansionManager = null; @@ -315,6 +301,7 @@ protected bool TryGetExpansionManager(out IVsExpansionManager expansionManager) return expansionManager != null; } +#nullable enable protected bool AreSnippetsEnabled(EditorCommandArgs args) { // Don't execute in cloud environment, should be handled by LSP @@ -323,9 +310,39 @@ protected bool AreSnippetsEnabled(EditorCommandArgs args) return false; } - return EditorOptionsService.GlobalOptions.GetOption(SnippetsOptionsStorage.Snippets) && + if (!_editorOptionsService.GlobalOptions.GetOption(SnippetsOptionsStorage.Snippets)) + { + return false; + } + + var textContainer = args.SubjectBuffer.AsTextContainer(); + if (Workspace.TryGetWorkspace(textContainer, out var workspace) && workspace.Kind == WorkspaceKind.Interactive) + { // TODO (https://github.com/dotnet/roslyn/issues/5107): enable in interactive - !(Workspace.TryGetWorkspace(args.SubjectBuffer.AsTextContainer(), out var workspace) && workspace.Kind == WorkspaceKind.Interactive); + return false; + } + + return true; + } + + protected bool AreSnippetsEnabledWithClient(EditorCommandArgs args, [NotNullWhen(true)] out SnippetExpansionClient? snippetExpansionClient) + { + if (!AreSnippetsEnabled(args)) + { + snippetExpansionClient = null; + return false; + } + + var document = args.SubjectBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); + if (document is null) + { + snippetExpansionClient = null; + return false; + } + + var expansionClientFactory = document.Project.Services.SolutionServices.GetService(); + snippetExpansionClient = expansionClientFactory?.TryGetSnippetExpansionClient(args.TextView); + return snippetExpansionClient is not null; } } } diff --git a/src/VisualStudio/Core/Def/Snippets/AbstractSnippetExpansionLanguageHelper.cs b/src/VisualStudio/Core/Def/Snippets/AbstractSnippetExpansionLanguageHelper.cs new file mode 100644 index 0000000000000..c4aa716835af8 --- /dev/null +++ b/src/VisualStudio/Core/Def/Snippets/AbstractSnippetExpansionLanguageHelper.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Xml.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.AddImport; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; +using Microsoft.VisualStudio.LanguageServices.Implementation.Snippets; +using Microsoft.VisualStudio.LanguageServices.Implementation.Venus; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using VsTextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan; + +namespace Microsoft.VisualStudio.LanguageServices.Snippets; + +internal abstract class AbstractSnippetExpansionLanguageHelper + : ISnippetExpansionLanguageHelper +{ + public abstract Guid LanguageServiceGuid { get; } + public abstract string FallbackDefaultLiteral { get; } + + public abstract Document AddImports(Document document, AddImportPlacementOptions addImportOptions, SyntaxFormattingOptions formattingOptions, int position, XElement snippetNode, CancellationToken cancellationToken); + public abstract ITrackingSpan? InsertEmptyCommentAndGetEndPositionTrackingSpan(IVsExpansionSession expansionSession, ITextView textView, ITextBuffer subjectBuffer); + + public bool TryGetSubjectBufferSpan(ITextView textView, ITextBuffer subjectBuffer, VsTextSpan surfaceBufferTextSpan, out SnapshotSpan subjectBufferSpan) + { + var snapshotSpan = textView.TextSnapshot.GetSpan(surfaceBufferTextSpan); + var subjectBufferSpanCollection = textView.BufferGraph.MapDownToBuffer(snapshotSpan, SpanTrackingMode.EdgeExclusive, subjectBuffer); + + // Bail if a snippet span does not map down to exactly one subject buffer span. + if (subjectBufferSpanCollection.Count == 1) + { + subjectBufferSpan = subjectBufferSpanCollection.Single(); + return true; + } + + subjectBufferSpan = default; + return false; + } + + protected static bool TryAddImportsToContainedDocument(Document document, IEnumerable memberImportsNamespaces) + { + var containedDocument = ContainedDocument.TryGetContainedDocument(document.Id); + if (containedDocument == null) + { + return false; + } + + if (containedDocument.ContainedLanguageHost is IVsContainedLanguageHostInternal containedLanguageHost) + { + foreach (var importClause in memberImportsNamespaces) + { + if (containedLanguageHost.InsertImportsDirective(importClause) != VSConstants.S_OK) + { + return false; + } + } + } + + return true; + } +} diff --git a/src/VisualStudio/Core/Def/Snippets/ISnippetExpansionClientFactory.cs b/src/VisualStudio/Core/Def/Snippets/ISnippetExpansionClientFactory.cs new file mode 100644 index 0000000000000..1535c164db0f6 --- /dev/null +++ b/src/VisualStudio/Core/Def/Snippets/ISnippetExpansionClientFactory.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Host; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.Snippets; + +internal interface ISnippetExpansionClientFactory : IWorkspaceService +{ + SnippetExpansionClient? TryGetSnippetExpansionClient(ITextView textView); + SnippetExpansionClient GetOrCreateSnippetExpansionClient(Document document, ITextView textView, ITextBuffer subjectBuffer); +} diff --git a/src/VisualStudio/Core/Def/Snippets/ISnippetExpansionLanguageHelper.cs b/src/VisualStudio/Core/Def/Snippets/ISnippetExpansionLanguageHelper.cs new file mode 100644 index 0000000000000..cdd3bde889e44 --- /dev/null +++ b/src/VisualStudio/Core/Def/Snippets/ISnippetExpansionLanguageHelper.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; +using System.Xml.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.AddImport; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using VsTextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan; + +namespace Microsoft.VisualStudio.LanguageServices.Snippets; + +internal interface ISnippetExpansionLanguageHelper : ILanguageService +{ + Guid LanguageServiceGuid { get; } + string FallbackDefaultLiteral { get; } + + bool TryGetSubjectBufferSpan(ITextView textView, ITextBuffer subjectBuffer, VsTextSpan surfaceBufferTextSpan, out SnapshotSpan subjectBufferSpan); + ITrackingSpan? InsertEmptyCommentAndGetEndPositionTrackingSpan(IVsExpansionSession expansionSession, ITextView textView, ITextBuffer subjectBuffer); + Document AddImports(Document document, AddImportPlacementOptions addImportOptions, SyntaxFormattingOptions formattingOptions, int position, XElement snippetNode, CancellationToken cancellationToken); +} diff --git a/src/VisualStudio/Core/Def/Snippets/AbstractSnippetExpansionClient.cs b/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs similarity index 95% rename from src/VisualStudio/Core/Def/Snippets/AbstractSnippetExpansionClient.cs rename to src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs index 63452122e8668..ad248417ca2ba 100644 --- a/src/VisualStudio/Core/Def/Snippets/AbstractSnippetExpansionClient.cs +++ b/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs @@ -34,7 +34,7 @@ using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; -using Microsoft.VisualStudio.LanguageServices.Implementation.Venus; +using Microsoft.VisualStudio.LanguageServices.Snippets; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding; @@ -47,7 +47,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Snippets { - internal abstract class AbstractSnippetExpansionClient : ForegroundThreadAffinitizedObject, IVsExpansionClient + internal class SnippetExpansionClient : ForegroundThreadAffinitizedObject, IVsExpansionClient { /// /// The name of a snippet field created for caret placement in Full Method Call snippet sessions when the @@ -60,12 +60,12 @@ internal abstract class AbstractSnippetExpansionClient : ForegroundThreadAffinit /// private static readonly string s_fullMethodCallDescriptionSentinel = Guid.NewGuid().ToString("N"); + private readonly ISnippetExpansionLanguageHelper _languageHelper; private readonly SignatureHelpControllerProvider _signatureHelpControllerProvider; private readonly IEditorCommandHandlerServiceFactory _editorCommandHandlerServiceFactory; - protected readonly IVsEditorAdaptersFactoryService EditorAdaptersFactoryService; - protected readonly Guid LanguageServiceGuid; - protected readonly ITextView TextView; - protected readonly ITextBuffer SubjectBuffer; + private readonly IVsEditorAdaptersFactoryService EditorAdaptersFactoryService; + private readonly ITextView TextView; + private readonly ITextBuffer SubjectBuffer; internal readonly EditorOptionsService EditorOptionsService; private readonly ImmutableArray> _allArgumentProviders; @@ -87,9 +87,9 @@ internal abstract class AbstractSnippetExpansionClient : ForegroundThreadAffinit // Writes to this state only occur on the main thread. private readonly State _state = new(); - public AbstractSnippetExpansionClient( + public SnippetExpansionClient( IThreadingContext threadingContext, - Guid languageServiceGuid, + ISnippetExpansionLanguageHelper languageHelper, ITextView textView, ITextBuffer subjectBuffer, SignatureHelpControllerProvider signatureHelpControllerProvider, @@ -99,7 +99,7 @@ public AbstractSnippetExpansionClient( EditorOptionsService editorOptionsService) : base(threadingContext) { - LanguageServiceGuid = languageServiceGuid; + _languageHelper = languageHelper; TextView = textView; SubjectBuffer = subjectBuffer; _signatureHelpControllerProvider = signatureHelpControllerProvider; @@ -154,9 +154,6 @@ public int GetExpansionFunction(IXMLDOMNode xmlFunctionNode, string bstrFieldNam return VSConstants.E_INVALIDARG; } } - protected abstract ITrackingSpan? InsertEmptyCommentAndGetEndPositionTrackingSpan(); - internal abstract Document AddImports(Document document, AddImportPlacementOptions addImportOptions, SyntaxFormattingOptions formattingOptions, int position, XElement snippetNode, CancellationToken cancellationToken); - protected abstract string FallbackDefaultLiteral { get; } public int FormatSpan(IVsTextLines pBuffer, VsTextSpan[] tsInSurfaceBuffer) { @@ -217,7 +214,7 @@ public int FormatSpan(IVsTextLines pBuffer, VsTextSpan[] tsInSurfaceBuffer) fullSnippetSpan[0].iStartIndex == tsInSurfaceBuffer[0].iStartIndex && fullSnippetSpan[0].iEndLine == tsInSurfaceBuffer[0].iEndLine && fullSnippetSpan[0].iEndIndex == tsInSurfaceBuffer[0].iEndIndex; - var endPositionTrackingSpan = isFullSnippetFormat ? InsertEmptyCommentAndGetEndPositionTrackingSpan() : null; + var endPositionTrackingSpan = isFullSnippetFormat ? _languageHelper.InsertEmptyCommentAndGetEndPositionTrackingSpan(ExpansionSession, TextView, SubjectBuffer) : null; var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(SubjectBuffer.CurrentSnapshot, snippetTrackingSpan.GetSpan(SubjectBuffer.CurrentSnapshot)); @@ -340,7 +337,7 @@ private static bool SetEndPositionIfNoneSpecified(IVsExpansionSession pSession) return true; } - protected static bool TryGetSnippetNode(IVsExpansionSession pSession, [NotNullWhen(true)] out XElement? snippetNode) + private static bool TryGetSnippetNode(IVsExpansionSession pSession, [NotNullWhen(true)] out XElement? snippetNode) { IXMLDOMNode? xmlNode = null; snippetNode = null; @@ -521,7 +518,7 @@ public virtual bool TryInsertExpansion(int startPositionInSubjectBuffer, int end return true; } - if (expansion.InsertExpansion(textSpan, textSpan, this, LanguageServiceGuid, out _state._expansionSession) == VSConstants.S_OK) + if (expansion.InsertExpansion(textSpan, textSpan, this, _languageHelper.LanguageServiceGuid, out _state._expansionSession) == VSConstants.S_OK) { // This expansion is not derived from a symbol, so make sure the state isn't tracking any symbol // information @@ -559,7 +556,7 @@ private bool TryInsertArgumentCompletionSnippet(SnapshotSpan triggerSpan, Snapsh var doc = (DOMDocument)new DOMDocumentClass(); if (doc.loadXML(snippet.ToString(SaveOptions.OmitDuplicateNamespaces))) { - if (expansion.InsertSpecificExpansion(doc, textSpan, this, LanguageServiceGuid, pszRelativePath: null, out _state._expansionSession) == VSConstants.S_OK) + if (expansion.InsertSpecificExpansion(doc, textSpan, this, _languageHelper.LanguageServiceGuid, pszRelativePath: null, out _state._expansionSession) == VSConstants.S_OK) { Debug.Assert(_state._expansionSession != null); _state._methodNameForInsertFullMethodCall = methodSymbols.First().Name; @@ -585,7 +582,7 @@ private bool TryInsertArgumentCompletionSnippet(SnapshotSpan triggerSpan, Snapsh return false; // Local function - static void EnsureRegisteredForModelUpdatedEvents(AbstractSnippetExpansionClient client, Controller controller) + static void EnsureRegisteredForModelUpdatedEvents(SnippetExpansionClient client, Controller controller) { // Access to _registeredForSignatureHelpEvents is synchronized on the main thread client.ThreadingContext.ThrowIfNotOnUIThread(); @@ -915,7 +912,7 @@ public void MoveToSpecificMethod(IMethodSymbol method, CancellationToken cancell } // If we still have no value, fill in the default - value ??= FallbackDefaultLiteral; + value ??= _languageHelper.FallbackDefaultLiteral; newArguments = newArguments.SetItem(parameter.Name, value); } @@ -924,7 +921,7 @@ public void MoveToSpecificMethod(IMethodSymbol method, CancellationToken cancell var doc = (DOMDocument)new DOMDocumentClass(); if (doc.loadXML(snippet.ToString(SaveOptions.OmitDuplicateNamespaces))) { - if (expansion.InsertSpecificExpansion(doc, adjustedTextSpan, this, LanguageServiceGuid, pszRelativePath: null, out _state._expansionSession) == VSConstants.S_OK) + if (expansion.InsertSpecificExpansion(doc, adjustedTextSpan, this, _languageHelper.LanguageServiceGuid, pszRelativePath: null, out _state._expansionSession) == VSConstants.S_OK) { Debug.Assert(_state._expansionSession != null); _state._methodNameForInsertFullMethodCall = method.Name; @@ -1014,7 +1011,7 @@ public int OnItemChosen(string pszTitle, string pszPath) _earlyEndExpansionHappened = false; - hr = expansion.InsertNamedExpansion(pszTitle, pszPath, textSpan, this, LanguageServiceGuid, fShowDisambiguationUI: 0, pSession: out _state._expansionSession); + hr = expansion.InsertNamedExpansion(pszTitle, pszPath, textSpan, this, _languageHelper.LanguageServiceGuid, fShowDisambiguationUI: 0, pSession: out _state._expansionSession); if (_earlyEndExpansionHappened) { @@ -1068,7 +1065,7 @@ private void AddReferencesAndImports( var addImportOptions = SubjectBuffer.GetAddImportPlacementOptions(EditorOptionsService, languageServices, documentWithImports.AllowImportsInHiddenRegions()); var formattingOptions = SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, languageServices, explicitFormat: false); - documentWithImports = AddImports(documentWithImports, addImportOptions, formattingOptions, position, snippetNode, cancellationToken); + documentWithImports = _languageHelper.AddImports(documentWithImports, addImportOptions, formattingOptions, position, snippetNode, cancellationToken); AddReferences(documentWithImports.Project, snippetNode); } @@ -1117,28 +1114,6 @@ private static void AddReferences(Project originalProject, XElement snippetNode) } } - protected static bool TryAddImportsToContainedDocument(Document document, IEnumerable memberImportsNamespaces) - { - var containedDocument = ContainedDocument.TryGetContainedDocument(document.Id); - if (containedDocument == null) - { - return false; - } - - if (containedDocument.ContainedLanguageHost is IVsContainedLanguageHostInternal containedLanguageHost) - { - foreach (var importClause in memberImportsNamespaces) - { - if (containedLanguageHost.InsertImportsDirective(importClause) != VSConstants.S_OK) - { - return false; - } - } - } - - return true; - } - internal bool TryGetSubjectBufferSpan(VsTextSpan surfaceBufferTextSpan, out SnapshotSpan subjectBufferSpan) { var snapshotSpan = TextView.TextSnapshot.GetSpan(surfaceBufferTextSpan); @@ -1210,5 +1185,14 @@ public void Clear() _arguments = _arguments.Clear(); } } + + internal TestAccessor GetTestAccessor() + => new TestAccessor(this); + + internal readonly struct TestAccessor(SnippetExpansionClient instance) + { + internal ISnippetExpansionLanguageHelper LanguageHelper + => instance._languageHelper; + } } } diff --git a/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClientFactory.cs b/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClientFactory.cs new file mode 100644 index 0000000000000..7877a1ab0bfc6 --- /dev/null +++ b/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClientFactory.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.LanguageServices.Snippets; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.Commanding; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.Snippets; + +[ExportWorkspaceService(typeof(ISnippetExpansionClientFactory))] +[Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class SnippetExpansionClientFactory( + IThreadingContext threadingContext, + SignatureHelpControllerProvider signatureHelpControllerProvider, + IEditorCommandHandlerServiceFactory editorCommandHandlerServiceFactory, + IVsEditorAdaptersFactoryService editorAdaptersFactoryService, + [ImportMany] IEnumerable> argumentProviders, + EditorOptionsService editorOptionsService) + : ISnippetExpansionClientFactory +{ + private readonly IThreadingContext _threadingContext = threadingContext; + private readonly SignatureHelpControllerProvider _signatureHelpControllerProvider = signatureHelpControllerProvider; + private readonly IEditorCommandHandlerServiceFactory _editorCommandHandlerServiceFactory = editorCommandHandlerServiceFactory; + private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService = editorAdaptersFactoryService; + private readonly ImmutableArray> _argumentProviders = argumentProviders.ToImmutableArray(); + private readonly EditorOptionsService _editorOptionsService = editorOptionsService; + + public SnippetExpansionClient? TryGetSnippetExpansionClient(ITextView textView) + { + Contract.ThrowIfFalse(_threadingContext.JoinableTaskContext.IsOnMainThread); + + _ = textView.Properties.TryGetProperty(typeof(SnippetExpansionClient), out SnippetExpansionClient? expansionClient); + return expansionClient; + } + + public SnippetExpansionClient GetOrCreateSnippetExpansionClient(Document document, ITextView textView, ITextBuffer subjectBuffer) + { + Contract.ThrowIfFalse(_threadingContext.JoinableTaskContext.IsOnMainThread); + + if (!textView.Properties.TryGetProperty(typeof(SnippetExpansionClient), out SnippetExpansionClient? expansionClient)) + { + expansionClient = CreateSnippetExpansionClient(document, textView, subjectBuffer); + textView.Properties.AddProperty(typeof(SnippetExpansionClient), expansionClient); + } + + return expansionClient!; + } + + protected virtual SnippetExpansionClient CreateSnippetExpansionClient(Document document, ITextView textView, ITextBuffer subjectBuffer) + { + return new SnippetExpansionClient( + _threadingContext, + document.GetRequiredLanguageService(), + textView, + subjectBuffer, + _signatureHelpControllerProvider, + _editorCommandHandlerServiceFactory, + _editorAdaptersFactoryService, + _argumentProviders, + _editorOptionsService); + } +} diff --git a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.IVsExpansionFunction.cs b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.IVsExpansionFunction.cs index 7d48c0a353b7a..46a7d2482e939 100644 --- a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.IVsExpansionFunction.cs +++ b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.IVsExpansionFunction.cs @@ -6,7 +6,6 @@ using System.Runtime.InteropServices; using System.Threading; -using Microsoft.VisualStudio; using Microsoft.VisualStudio.TextManager.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Snippets @@ -42,7 +41,6 @@ int IVsExpansionFunction.GetListText(int index, out string text) int IVsExpansionFunction.ReleaseFunction() { - snippetExpansionClient = null; return VSConstants.S_OK; } } diff --git a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.cs b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.cs index 2118db6f96f98..2d50c8b8a1337 100644 --- a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.cs +++ b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.TextManager.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Snippets { @@ -20,9 +19,9 @@ internal abstract partial class AbstractSnippetFunction : IVsExpansionFunction private readonly ITextBuffer _subjectBuffer; private readonly IThreadingContext _threadingContext; - protected AbstractSnippetExpansionClient snippetExpansionClient; + protected readonly SnippetExpansionClient snippetExpansionClient; - public AbstractSnippetFunction(AbstractSnippetExpansionClient snippetExpansionClient, ITextBuffer subjectBuffer, IThreadingContext threadingContext) + public AbstractSnippetFunction(SnippetExpansionClient snippetExpansionClient, ITextBuffer subjectBuffer, IThreadingContext threadingContext) { this.snippetExpansionClient = snippetExpansionClient; _subjectBuffer = subjectBuffer; diff --git a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionClassName.cs b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionClassName.cs index d0c4c1630cf65..4c663af8f424f 100644 --- a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionClassName.cs +++ b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionClassName.cs @@ -18,7 +18,7 @@ internal class SnippetFunctionClassName : AbstractSnippetFunction { protected readonly string FieldName; - public SnippetFunctionClassName(AbstractSnippetExpansionClient snippetExpansionClient, ITextBuffer subjectBuffer, string fieldName, IThreadingContext threadingContext) + public SnippetFunctionClassName(SnippetExpansionClient snippetExpansionClient, ITextBuffer subjectBuffer, string fieldName, IThreadingContext threadingContext) : base(snippetExpansionClient, subjectBuffer, threadingContext) { this.FieldName = fieldName; diff --git a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionGenerateSwitchCases.cs b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionGenerateSwitchCases.cs index 1eebf36c44091..eb145163e2d6e 100644 --- a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionGenerateSwitchCases.cs +++ b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionGenerateSwitchCases.cs @@ -22,7 +22,7 @@ internal class SnippetFunctionGenerateSwitchCases : AbstractSnippetFunction protected readonly string SwitchExpressionField; public SnippetFunctionGenerateSwitchCases( - AbstractSnippetExpansionClient snippetExpansionClient, + SnippetExpansionClient snippetExpansionClient, ITextBuffer subjectBuffer, string caseGenerationLocationField, string switchExpressionField, diff --git a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionSimpleTypeName.cs b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionSimpleTypeName.cs index 4c5f2db8b61a7..6ae22d3545ab6 100644 --- a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionSimpleTypeName.cs +++ b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/SnippetFunctionSimpleTypeName.cs @@ -20,7 +20,7 @@ internal class SnippetFunctionSimpleTypeName : AbstractSnippetFunction private readonly string _fullyQualifiedName; public SnippetFunctionSimpleTypeName( - AbstractSnippetExpansionClient snippetExpansionClient, + SnippetExpansionClient snippetExpansionClient, ITextBuffer subjectBuffer, string fieldName, string fullyQualifiedName, diff --git a/src/VisualStudio/Core/Test/Snippets/CSharpSnippetExpansionClientTests.vb b/src/VisualStudio/Core/Test/Snippets/CSharpSnippetExpansionClientTests.vb index a9654193c0246..17ff5c571085e 100644 --- a/src/VisualStudio/Core/Test/Snippets/CSharpSnippetExpansionClientTests.vb +++ b/src/VisualStudio/Core/Test/Snippets/CSharpSnippetExpansionClientTests.vb @@ -2,20 +2,15 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.AddImport -Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.CSharp.Formatting -Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces -Imports Microsoft.CodeAnalysis.Formatting -Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Test.Utilities -Imports Microsoft.VisualStudio.LanguageServices.CSharp.Snippets +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.VisualStudio.LanguageServices.Implementation.Snippets Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.Text.Projection Imports Roslyn.Test.Utilities @@ -311,7 +306,7 @@ using G= H.I; } } - Using workspace = EditorTestWorkspace.Create(workspaceXml, openDocuments:=False) + Using workspace = EditorTestWorkspace.Create(workspaceXml, openDocuments:=False, composition:=VisualStudioTestCompositions.LanguageServices) Dim document = workspace.Documents.Single() Dim textBuffer = document.GetTextBuffer() Dim editorOptionsService = workspace.GetService(Of EditorOptionsService)() @@ -321,23 +316,18 @@ using G= H.I; editorOptions.SetOptionValue(DefaultOptions.TabSizeOptionId, tabSize) editorOptions.SetOptionValue(DefaultOptions.IndentSizeOptionId, tabSize) - Dim snippetExpansionClient = New SnippetExpansionClient( - workspace.ExportProvider.GetExportedValue(Of IThreadingContext), - Guids.CSharpLanguageServiceId, + Dim expansionClientFactory = workspace.Services.GetRequiredService(Of ISnippetExpansionClientFactory)() + Dim snippetExpansionClient = expansionClientFactory.GetOrCreateSnippetExpansionClient( + textBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(), document.GetTextView(), - textBuffer, - signatureHelpControllerProvider:=Nothing, - editorCommandHandlerServiceFactory:=Nothing, - editorAdaptersFactoryService:=Nothing, - workspace.ExportProvider.GetExports(Of ArgumentProvider, OrderableLanguageMetadata)().ToImmutableArray(), - editorOptionsService) + textBuffer) SnippetExpansionClientTestsHelper.TestFormattingAndCaretPosition(snippetExpansionClient, document, expectedResult, tabSize * 3) End Using End Sub Public Sub TestProjectionFormatting(workspaceXmlWithSubjectBufferDocument As XElement, surfaceBufferDocumentXml As XElement, expectedSurfaceBuffer As XElement) - Using workspace = EditorTestWorkspace.Create(workspaceXmlWithSubjectBufferDocument) + Using workspace = EditorTestWorkspace.Create(workspaceXmlWithSubjectBufferDocument, composition:=VisualStudioTestCompositions.LanguageServices) Dim subjectBufferDocument = workspace.Documents.Single() Dim surfaceBufferDocument = workspace.CreateProjectionBufferDocument( @@ -345,16 +335,11 @@ using G= H.I; {subjectBufferDocument}, options:=ProjectionBufferOptions.WritableLiteralSpans) - Dim snippetExpansionClient = New SnippetExpansionClient( - workspace.ExportProvider.GetExportedValue(Of IThreadingContext), - Guids.CSharpLanguageServiceId, + Dim expansionClientFactory = workspace.Services.GetRequiredService(Of ISnippetExpansionClientFactory)() + Dim snippetExpansionClient = expansionClientFactory.GetOrCreateSnippetExpansionClient( + subjectBufferDocument.GetTextBuffer().AsTextContainer().GetOpenDocumentInCurrentContext(), surfaceBufferDocument.GetTextView(), - subjectBufferDocument.GetTextBuffer(), - signatureHelpControllerProvider:=Nothing, - editorCommandHandlerServiceFactory:=Nothing, - editorAdaptersFactoryService:=Nothing, - workspace.ExportProvider.GetExports(Of ArgumentProvider, OrderableLanguageMetadata)().ToImmutableArray(), - workspace.GetService(Of EditorOptionsService)()) + subjectBufferDocument.GetTextBuffer()) SnippetExpansionClientTestsHelper.TestProjectionBuffer(snippetExpansionClient, surfaceBufferDocument, expectedSurfaceBuffer) End Using @@ -387,17 +372,12 @@ using G= H.I; ) Next - Using workspace = EditorTestWorkspace.CreateCSharp(originalCode) - Dim expansionClient = New SnippetExpansionClient( - workspace.ExportProvider.GetExportedValue(Of IThreadingContext), - Guids.VisualBasicDebuggerLanguageId, + Using workspace = EditorTestWorkspace.CreateCSharp(originalCode, composition:=VisualStudioTestCompositions.LanguageServices) + Dim expansionClientFactory = workspace.Services.GetRequiredService(Of ISnippetExpansionClientFactory)() + Dim expansionClient = expansionClientFactory.GetOrCreateSnippetExpansionClient( + workspace.Documents.Single().GetTextBuffer().AsTextContainer().GetOpenDocumentInCurrentContext(), workspace.Documents.Single().GetTextView(), - workspace.Documents.Single().GetTextBuffer(), - signatureHelpControllerProvider:=Nothing, - editorCommandHandlerServiceFactory:=Nothing, - editorAdaptersFactoryService:=Nothing, - workspace.ExportProvider.GetExports(Of ArgumentProvider, OrderableLanguageMetadata)().ToImmutableArray(), - workspace.GetService(Of EditorOptionsService)()) + workspace.Documents.Single().GetTextBuffer()) Dim document = workspace.CurrentSolution.Projects.Single().Documents.Single() Dim addImportOptions = New AddImportPlacementOptions() With @@ -407,7 +387,7 @@ using G= H.I; Dim formattingOptions = CSharpSyntaxFormattingOptions.Default - Dim updatedDocument = expansionClient.AddImports( + Dim updatedDocument = expansionClient.GetTestAccessor().LanguageHelper.AddImports( document, addImportOptions, formattingOptions, diff --git a/src/VisualStudio/Core/Test/Snippets/SnippetExpansionClientTestsHelper.vb b/src/VisualStudio/Core/Test/Snippets/SnippetExpansionClientTestsHelper.vb index 12cf195eb36a8..2b35604d72376 100644 --- a/src/VisualStudio/Core/Test/Snippets/SnippetExpansionClientTestsHelper.vb +++ b/src/VisualStudio/Core/Test/Snippets/SnippetExpansionClientTestsHelper.vb @@ -3,14 +3,13 @@ ' See the LICENSE file in the project root for more information. Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.VisualStudio.LanguageServices.Implementation.Snippets Imports Microsoft.VisualStudio.TextManager.Interop Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets Friend Class SnippetExpansionClientTestsHelper - Public Shared Sub TestProjectionBuffer(snippetExpansionClient As AbstractSnippetExpansionClient, + Public Shared Sub TestProjectionBuffer(snippetExpansionClient As SnippetExpansionClient, surfaceBufferDocument As EditorTestHostDocument, expectedSurfaceBuffer As XElement) @@ -48,7 +47,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets End Sub Friend Shared Sub TestFormattingAndCaretPosition( - snippetExpansionClient As AbstractSnippetExpansionClient, + snippetExpansionClient As SnippetExpansionClient, document As EditorTestHostDocument, expectedResult As XElement, expectedVirtualSpacing As Integer) diff --git a/src/VisualStudio/Core/Test/Snippets/SnippetTestState.vb b/src/VisualStudio/Core/Test/Snippets/SnippetTestState.vb index 744ce08193d6f..dc52a84921156 100644 --- a/src/VisualStudio/Core/Test/Snippets/SnippetTestState.vb +++ b/src/VisualStudio/Core/Test/Snippets/SnippetTestState.vb @@ -3,13 +3,13 @@ ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable +Imports System.Composition Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.AddImport Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Editor Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp -Imports Microsoft.CodeAnalysis.Editor.Shared.Options Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests Imports Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense @@ -17,17 +17,18 @@ Imports Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.Shared.Extensions Imports Microsoft.CodeAnalysis.Snippets +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.VisualStudio.Commanding Imports Microsoft.VisualStudio.Editor Imports Microsoft.VisualStudio.Language.Intellisense Imports Microsoft.VisualStudio.LanguageServices.Implementation.Snippets -Imports Microsoft.VisualStudio.Shell +Imports Microsoft.VisualStudio.LanguageServices.Snippets Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.Text.Editor.Commanding Imports Microsoft.VisualStudio.TextManager.Interop -Imports Moq -Imports MSXML Imports Roslyn.Utilities Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets @@ -38,48 +39,57 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets ' Remove the default completion presenters to prevent them from conflicting with the test one ' that we are adding. MyBase.New(workspaceElement, - extraExportedTypes:={GetType(TestSignatureHelpPresenter), GetType(IntelliSenseTestState), GetType(MockCompletionPresenterProvider), GetType(StubVsEditorAdaptersFactoryService)}.Concat(If(extraParts, {})).ToList(), + extraExportedTypes:=AugmentExtraTypesForSnippetTests(extraParts), workspaceKind:=workspaceKind, - excludedTypes:={GetType(IIntelliSensePresenter(Of ISignatureHelpPresenterSession, ISignatureHelpSession)), GetType(FormatCommandHandler)}.Concat(If(excludedTypes, {})).ToList(), + excludedTypes:=AugmentExcludedTypesForSnippetTests(excludedTypes), includeFormatCommandHandler:=False) Workspace.GlobalOptions.SetGlobalOption(SnippetsOptionsStorage.Snippets, True) - Dim mockSVsServiceProvider = New Mock(Of SVsServiceProvider)(MockBehavior.Strict) - mockSVsServiceProvider.Setup(Function(s) s.GetService(GetType(SVsTextManager))).Returns(Nothing) + Dim contentType = If(languageName = LanguageNames.CSharp, ContentTypeNames.CSharpContentType, ContentTypeNames.VisualBasicContentType) + Dim name = If(languageName = LanguageNames.CSharp, "CSharp Snippets", "VB Snippets") + Dim snippetCommandHandler = Workspace.GetService(Of ICommandHandler)(contentType, name) + + Me.SnippetCommandHandler = DirectCast(snippetCommandHandler, AbstractSnippetCommandHandler) - Dim globalOptions = Workspace.GetService(Of IGlobalOptionService) Dim editorOptionsService = Workspace.GetService(Of EditorOptionsService)() - Dim indentationManager = Workspace.GetService(Of IIndentationManagerService)() - - SnippetCommandHandler = If(languageName = LanguageNames.CSharp, - DirectCast(New CSharp.Snippets.SnippetCommandHandler( - Workspace.ExportProvider.GetExportedValue(Of IThreadingContext), - Workspace.ExportProvider.GetExportedValue(Of SignatureHelpControllerProvider)(), - Workspace.ExportProvider.GetExportedValue(Of IEditorCommandHandlerServiceFactory)(), - Workspace.ExportProvider.GetExportedValue(Of IVsEditorAdaptersFactoryService)(), - mockSVsServiceProvider.Object, - Workspace.ExportProvider.GetExports(Of ArgumentProvider, OrderableLanguageMetadata)(), - editorOptionsService), AbstractSnippetCommandHandler), - New VisualBasic.Snippets.SnippetCommandHandler( - Workspace.ExportProvider.GetExportedValue(Of IThreadingContext), - Workspace.ExportProvider.GetExportedValue(Of SignatureHelpControllerProvider)(), - Workspace.ExportProvider.GetExportedValue(Of IEditorCommandHandlerServiceFactory)(), - Workspace.ExportProvider.GetExportedValue(Of IVsEditorAdaptersFactoryService)(), - mockSVsServiceProvider.Object, - Workspace.ExportProvider.GetExports(Of ArgumentProvider, OrderableLanguageMetadata)(), - editorOptionsService)) - - SnippetExpansionClient = New MockSnippetExpansionClient( - Workspace.ExportProvider.GetExportedValue(Of IThreadingContext), - startActiveSession, - If(languageName Is LanguageNames.CSharp, Guids.CSharpLanguageServiceId, Guids.VisualBasicLanguageServiceId), - TextView, - SubjectBuffer, - editorOptionsService) - TextView.Properties.AddProperty(GetType(AbstractSnippetExpansionClient), SnippetExpansionClient) + Dim snippetExpansionClientFactory = Workspace.Services.GetRequiredService(Of ISnippetExpansionClientFactory)() + SnippetExpansionClient = CType(snippetExpansionClientFactory.GetOrCreateSnippetExpansionClient(SubjectBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(), TextView, SubjectBuffer), MockSnippetExpansionClient) + + If startActiveSession Then + SnippetExpansionClient.TryHandleTabReturnValue = True + SnippetExpansionClient.TryHandleBackTabReturnValue = True + SnippetExpansionClient.TryHandleEscapeReturnValue = True + SnippetExpansionClient.TryHandleReturnReturnValue = True + End If End Sub + Private Shared Function AugmentExtraTypesForSnippetTests(extraParts As IEnumerable(Of Type)) As IEnumerable(Of Type) + Return If(extraParts, Type.EmptyTypes).Concat( + { + GetType(TestSignatureHelpPresenter), + GetType(IntelliSenseTestState), + GetType(MockCompletionPresenterProvider), + GetType(StubVsEditorAdaptersFactoryService), + GetType(CSharp.Snippets.SnippetCommandHandler), + GetType(VisualBasic.Snippets.SnippetCommandHandler), + GetType(MockCSharpSnippetLanguageHelper), + GetType(MockVisualBasicSnippetLanguageHelper), + GetType(MockSnippetExpansionClientFactory), + GetType(MockServiceProvider), + GetType(StubVsServiceExporter(Of )), + GetType(StubVsServiceExporter(Of ,)) + }) + End Function + + Private Shared Function AugmentExcludedTypesForSnippetTests(excludedTypes As IEnumerable(Of Type)) As IEnumerable(Of Type) + Return If(excludedTypes, Type.EmptyTypes).Concat( + { + GetType(IIntelliSensePresenter(Of ISignatureHelpPresenterSession, ISignatureHelpSession)), + GetType(FormatCommandHandler) + }) + End Function + Public ReadOnly SnippetCommandHandler As AbstractSnippetCommandHandler Public Property SnippetExpansionClient As MockSnippetExpansionClient @@ -149,31 +159,129 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets End Sub End Class + + <[Shared]> + + Friend Class MockSnippetExpansionClientFactory + Inherits SnippetExpansionClientFactory + + Private ReadOnly _threadingContext As IThreadingContext + Private ReadOnly _signatureHelpControllerProvider As SignatureHelpControllerProvider + Private ReadOnly _editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory + Private ReadOnly _editorAdaptersFactoryService As IVsEditorAdaptersFactoryService + Private ReadOnly _argumentProviders As ImmutableArray(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)) + Private ReadOnly _editorOptionsService As EditorOptionsService + + + + Public Sub New( + threadingContext As IThreadingContext, + signatureHelpControllerProvider As SignatureHelpControllerProvider, + editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory, + editorAdaptersFactoryService As IVsEditorAdaptersFactoryService, + argumentProviders As IEnumerable(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)), + editorOptionsService As EditorOptionsService) + MyBase.New( + threadingContext, + signatureHelpControllerProvider, + editorCommandHandlerServiceFactory, + editorAdaptersFactoryService, + argumentProviders.ToImmutableArray(), + editorOptionsService) + + _threadingContext = threadingContext + _signatureHelpControllerProvider = signatureHelpControllerProvider + _editorCommandHandlerServiceFactory = editorCommandHandlerServiceFactory + _editorAdaptersFactoryService = editorAdaptersFactoryService + _argumentProviders = argumentProviders.ToImmutableArray() + _editorOptionsService = editorOptionsService + End Sub + + Protected Overrides Function CreateSnippetExpansionClient(document As Document, textView As ITextView, subjectBuffer As ITextBuffer) As SnippetExpansionClient + Return New MockSnippetExpansionClient( + _threadingContext, + document.GetRequiredLanguageService(Of ISnippetExpansionLanguageHelper)(), + textView, + subjectBuffer, + _signatureHelpControllerProvider, + _editorCommandHandlerServiceFactory, + _editorAdaptersFactoryService, + _argumentProviders, + _editorOptionsService) + End Function + End Class + + + <[Shared]> + + Friend NotInheritable Class MockCSharpSnippetLanguageHelper + Inherits MockSnippetLanguageHelper + + + + Public Sub New() + MyBase.New(Guids.CSharpLanguageServiceId) + End Sub + End Class + + + <[Shared]> + + Friend NotInheritable Class MockVisualBasicSnippetLanguageHelper + Inherits MockSnippetLanguageHelper + + + + Public Sub New() + MyBase.New(Guids.VisualBasicDebuggerLanguageId) + End Sub + End Class + + Friend MustInherit Class MockSnippetLanguageHelper + Inherits AbstractSnippetExpansionLanguageHelper + + Protected Sub New(languageServiceGuid As Guid) + Me.LanguageServiceGuid = languageServiceGuid + End Sub + + Public Overrides ReadOnly Property LanguageServiceGuid As Guid + + Public Overrides ReadOnly Property FallbackDefaultLiteral As String + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function AddImports(document As Document, addImportOptions As AddImportPlacementOptions, formattingOptions As SyntaxFormattingOptions, position As Integer, snippetNode As XElement, cancellationToken As CancellationToken) As Document + Return document + End Function + + Public Overrides Function InsertEmptyCommentAndGetEndPositionTrackingSpan(expansionSession As IVsExpansionSession, textView As ITextView, subjectBuffer As ITextBuffer) As ITrackingSpan + Throw New NotImplementedException() + End Function + End Class + Friend Class MockSnippetExpansionClient - Inherits AbstractSnippetExpansionClient + Inherits SnippetExpansionClient Public Sub New(threadingContext As IThreadingContext, - startActiveSession As Boolean, - languageServiceGuid As Guid, + languageHelper As ISnippetExpansionLanguageHelper, textView As ITextView, subjectBuffer As ITextBuffer, + signatureHelpControllerProvider As SignatureHelpControllerProvider, + editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory, + editorAdaptersFactoryService As IVsEditorAdaptersFactoryService, + argumentProviders As ImmutableArray(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)), editorOptionsService As EditorOptionsService) MyBase.New(threadingContext, - languageServiceGuid, + languageHelper, textView, subjectBuffer, - signatureHelpControllerProvider:=Nothing, - editorCommandHandlerServiceFactory:=Nothing, - Nothing, - ImmutableArray(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)).Empty, + signatureHelpControllerProvider, + editorCommandHandlerServiceFactory, + editorAdaptersFactoryService, + argumentProviders, editorOptionsService) - - If startActiveSession Then - TryHandleTabReturnValue = True - TryHandleBackTabReturnValue = True - TryHandleEscapeReturnValue = True - TryHandleReturnReturnValue = True - End If End Sub Public Property TryHandleReturnCalled As Boolean @@ -218,20 +326,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets InsertExpansionSpan = New Span(startPosition, endPosition - startPosition) Return TryInsertExpansionReturnValue End Function - - Protected Overrides Function InsertEmptyCommentAndGetEndPositionTrackingSpan() As ITrackingSpan - Throw New NotImplementedException() - End Function - - Protected Overrides ReadOnly Property FallbackDefaultLiteral As String - Get - Throw New NotImplementedException() - End Get - End Property - - Friend Overrides Function AddImports(document As Document, addImportOptions As AddImportPlacementOptions, formattingOptions As SyntaxFormattingOptions, position As Integer, snippetNode As XElement, cancellationToken As CancellationToken) As Document - Return document - End Function End Class End Class End Namespace diff --git a/src/VisualStudio/Core/Test/Snippets/VisualBasicSnippetExpansionClientTests.vb b/src/VisualStudio/Core/Test/Snippets/VisualBasicSnippetExpansionClientTests.vb index c62c9483098db..752282bc42a08 100644 --- a/src/VisualStudio/Core/Test/Snippets/VisualBasicSnippetExpansionClientTests.vb +++ b/src/VisualStudio/Core/Test/Snippets/VisualBasicSnippetExpansionClientTests.vb @@ -2,20 +2,15 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.AddImport -Imports Microsoft.CodeAnalysis.Completion -Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces -Imports Microsoft.CodeAnalysis.Formatting -Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Formatting -Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets +Imports Microsoft.VisualStudio.LanguageServices.Implementation.Snippets Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.Text.Projection Imports Roslyn.Test.Utilities @@ -352,7 +347,7 @@ End Class End Sub End Class - Using workspace = EditorTestWorkspace.Create(workspaceXml, openDocuments:=False) + Using workspace = EditorTestWorkspace.Create(workspaceXml, openDocuments:=False, composition:=VisualStudioTestCompositions.LanguageServices) Dim document = workspace.Documents.Single() Dim optionsService = workspace.GetService(Of EditorOptionsService)() Dim textBuffer = document.GetTextBuffer() @@ -362,16 +357,8 @@ End Class editorOptions.SetOptionValue(DefaultOptions.TabSizeOptionId, tabSize) editorOptions.SetOptionValue(DefaultOptions.IndentSizeOptionId, tabSize) - Dim snippetExpansionClient = New SnippetExpansionClient( - workspace.ExportProvider.GetExportedValue(Of IThreadingContext), - Guids.CSharpLanguageServiceId, - document.GetTextView(), - textBuffer, - signatureHelpControllerProvider:=Nothing, - editorCommandHandlerServiceFactory:=Nothing, - Nothing, - workspace.ExportProvider.GetExports(Of ArgumentProvider, OrderableLanguageMetadata)().ToImmutableArray(), - optionsService) + Dim expansionClientFactory = workspace.Services.GetRequiredService(Of ISnippetExpansionClientFactory)() + Dim snippetExpansionClient = expansionClientFactory.GetOrCreateSnippetExpansionClient(textBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(), document.GetTextView(), textBuffer) SnippetExpansionClientTestsHelper.TestFormattingAndCaretPosition(snippetExpansionClient, document, expectedResult, tabSize * 3) End Using @@ -405,17 +392,12 @@ End Class ) Next - Using workspace = EditorTestWorkspace.Create(workspaceXml) - Dim expansionClient = New SnippetExpansionClient( - workspace.ExportProvider.GetExportedValue(Of IThreadingContext), - Guids.VisualBasicDebuggerLanguageId, + Using workspace = EditorTestWorkspace.Create(workspaceXml, composition:=VisualStudioTestCompositions.LanguageServices) + Dim expansionClientFactory = workspace.Services.GetRequiredService(Of ISnippetExpansionClientFactory)() + Dim expansionClient = expansionClientFactory.GetOrCreateSnippetExpansionClient( + workspace.Documents.Single().GetTextBuffer().AsTextContainer().GetOpenDocumentInCurrentContext(), workspace.Documents.Single().GetTextView(), - workspace.Documents.Single().GetTextBuffer(), - signatureHelpControllerProvider:=Nothing, - editorCommandHandlerServiceFactory:=Nothing, - Nothing, - workspace.ExportProvider.GetExports(Of ArgumentProvider, OrderableLanguageMetadata)().ToImmutableArray(), - workspace.GetService(Of EditorOptionsService)()) + workspace.Documents.Single().GetTextBuffer()) Dim document = workspace.CurrentSolution.Projects.Single().Documents.Single() @@ -426,7 +408,7 @@ End Class Dim formattingOptions = VisualBasicSyntaxFormattingOptions.Default - Dim updatedDocument = expansionClient.AddImports( + Dim updatedDocument = expansionClient.GetTestAccessor().LanguageHelper.AddImports( document, addImportOptions, formattingOptions, @@ -440,7 +422,7 @@ End Class End Function Public Sub TestFormatting(workspaceXmlWithSubjectBufferDocument As XElement, surfaceBufferDocumentXml As XElement, expectedSurfaceBuffer As XElement) - Using workspace = EditorTestWorkspace.Create(workspaceXmlWithSubjectBufferDocument) + Using workspace = EditorTestWorkspace.Create(workspaceXmlWithSubjectBufferDocument, composition:=VisualStudioTestCompositions.LanguageServices) Dim subjectBufferDocument = workspace.Documents.Single() Dim surfaceBufferDocument = workspace.CreateProjectionBufferDocument( @@ -448,16 +430,11 @@ End Class {subjectBufferDocument}, options:=ProjectionBufferOptions.WritableLiteralSpans) - Dim snippetExpansionClient = New SnippetExpansionClient( - workspace.ExportProvider.GetExportedValue(Of IThreadingContext), - Guids.CSharpLanguageServiceId, + Dim expansionClientFactory = workspace.Services.GetRequiredService(Of ISnippetExpansionClientFactory)() + Dim snippetExpansionClient = expansionClientFactory.GetOrCreateSnippetExpansionClient( + subjectBufferDocument.GetTextBuffer().AsTextContainer().GetOpenDocumentInCurrentContext(), surfaceBufferDocument.GetTextView(), - subjectBufferDocument.GetTextBuffer(), - signatureHelpControllerProvider:=Nothing, - editorCommandHandlerServiceFactory:=Nothing, - Nothing, - workspace.ExportProvider.GetExports(Of ArgumentProvider, OrderableLanguageMetadata)().ToImmutableArray(), - workspace.GetService(Of EditorOptionsService)()) + subjectBufferDocument.GetTextBuffer()) SnippetExpansionClientTestsHelper.TestProjectionBuffer(snippetExpansionClient, surfaceBufferDocument, expectedSurfaceBuffer) End Using diff --git a/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCommandHandler.vb b/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCommandHandler.vb index 362c5d40db877..57f71b1a2c1c4 100644 --- a/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCommandHandler.vb +++ b/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCommandHandler.vb @@ -2,14 +2,10 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports System.Collections.Immutable Imports System.ComponentModel.Composition -Imports System.Diagnostics.CodeAnalysis Imports System.Threading Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Editor -Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp Imports Microsoft.CodeAnalysis.Editor.Shared.Extensions Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Host.Mef @@ -21,10 +17,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.VisualStudio.Editor Imports Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion Imports Microsoft.VisualStudio.LanguageServices.Implementation.Snippets -Imports Microsoft.VisualStudio.Shell Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor -Imports Microsoft.VisualStudio.Text.Editor.Commanding Imports Microsoft.VisualStudio.TextManager.Interop Imports Microsoft.VisualStudio.Utilities @@ -38,20 +32,17 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets Friend NotInheritable Class SnippetCommandHandler Inherits AbstractSnippetCommandHandler - Private ReadOnly _argumentProviders As ImmutableArray(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)) + Private ReadOnly _editorAdaptersFactoryService As IVsEditorAdaptersFactoryService - + Public Sub New( threadingContext As IThreadingContext, - signatureHelpControllerProvider As SignatureHelpControllerProvider, - editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory, editorAdaptersFactoryService As IVsEditorAdaptersFactoryService, - serviceProvider As SVsServiceProvider, - argumentProviders As IEnumerable(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)), + textManager As IVsService(Of SVsTextManager, IVsTextManager2), editorOptionsService As EditorOptionsService) - MyBase.New(threadingContext, signatureHelpControllerProvider, editorCommandHandlerServiceFactory, editorAdaptersFactoryService, editorOptionsService, serviceProvider) - _argumentProviders = argumentProviders.ToImmutableArray() + MyBase.New(threadingContext, editorOptionsService, textManager) + _editorAdaptersFactoryService = editorAdaptersFactoryService End Sub Protected Overrides Function IsSnippetExpansionContext(document As Document, startPosition As Integer, cancellationToken As CancellationToken) As Boolean @@ -62,17 +53,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets Not syntaxTree.FindTokenOnRightOfPosition(startPosition, cancellationToken).HasAncestor(Of XmlElementSyntax)() End Function - Protected Overrides Function GetSnippetExpansionClient(textView As ITextView, subjectBuffer As ITextBuffer) As AbstractSnippetExpansionClient - Return SnippetExpansionClient.GetSnippetExpansionClient(ThreadingContext, - textView, - subjectBuffer, - SignatureHelpControllerProvider, - EditorCommandHandlerServiceFactory, - EditorAdaptersFactoryService, - _argumentProviders, - EditorOptionsService) - End Function - Protected Overrides Function TryInvokeInsertionUI(textView As ITextView, subjectBuffer As ITextBuffer, Optional surroundWith As Boolean = False) As Boolean Debug.Assert(Not surroundWith) @@ -81,9 +61,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets Return False End If + Dim document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() + If document Is Nothing Then + Return False + End If + expansionManager.InvokeInsertionUI( - EditorAdaptersFactoryService.GetViewAdapter(textView), - GetSnippetExpansionClient(textView, subjectBuffer), + _editorAdaptersFactoryService.GetViewAdapter(textView), + GetSnippetExpansionClientFactory(document).GetOrCreateSnippetExpansionClient(document, textView, subjectBuffer), Guids.VisualBasicDebuggerLanguageId, bstrTypes:=Nothing, iCountTypes:=0, diff --git a/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCompletionProvider.vb b/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCompletionProvider.vb index 31a6f0e902c2e..b035a0d8dea3e 100644 --- a/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCompletionProvider.vb +++ b/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCompletionProvider.vb @@ -7,23 +7,18 @@ Imports System.ComponentModel.Composition Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Completion -Imports Microsoft.CodeAnalysis.Completion.Providers Imports Microsoft.CodeAnalysis.Editor -Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp -Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Shared.Extensions Imports Microsoft.CodeAnalysis.Snippets Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.Text.Shared.Extensions Imports Microsoft.CodeAnalysis.VisualBasic.Extensions Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery -Imports Microsoft.VisualStudio.Editor +Imports Microsoft.VisualStudio.LanguageServices.Implementation.Snippets Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor -Imports Microsoft.VisualStudio.Text.Editor.Commanding Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets @@ -31,29 +26,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets Inherits LSPCompletionProvider Implements ICustomCommitCompletionProvider - Private ReadOnly _threadingContext As IThreadingContext - Private ReadOnly _signatureHelpControllerProvider As SignatureHelpControllerProvider - Private ReadOnly _editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory - Private ReadOnly _editorAdaptersFactoryService As IVsEditorAdaptersFactoryService - Private ReadOnly _argumentProviders As ImmutableArray(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)) - Private ReadOnly _editorOptionsService As EditorOptionsService - - Public Sub New( - threadingContext As IThreadingContext, - signatureHelpControllerProvider As SignatureHelpControllerProvider, - editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory, - editorAdaptersFactoryService As IVsEditorAdaptersFactoryService, - argumentProviders As IEnumerable(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)), - editorOptionsService As EditorOptionsService) - - _threadingContext = threadingContext - _signatureHelpControllerProvider = signatureHelpControllerProvider - _editorCommandHandlerServiceFactory = editorCommandHandlerServiceFactory - _editorAdaptersFactoryService = editorAdaptersFactoryService - _argumentProviders = argumentProviders.ToImmutableArray() - _editorOptionsService = editorOptionsService + Public Sub New() End Sub Friend Overrides ReadOnly Property Language As String @@ -119,19 +94,13 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets Public Overrides ReadOnly Property TriggerCharacters As ImmutableHashSet(Of Char) = ImmutableHashSet(Of Char).Empty Public Sub Commit(completionItem As CompletionItem, + document As Document, textView As ITextView, subjectBuffer As ITextBuffer, triggerSnapshot As ITextSnapshot, commitChar As Char?) Implements ICustomCommitCompletionProvider.Commit - Dim snippetClient = SnippetExpansionClient.GetSnippetExpansionClient( - _threadingContext, - textView, - subjectBuffer, - _signatureHelpControllerProvider, - _editorCommandHandlerServiceFactory, - _editorAdaptersFactoryService, - _argumentProviders, - _editorOptionsService) + Dim expansionClientFactory = document.Project.Services.SolutionServices.GetRequiredService(Of ISnippetExpansionClientFactory)() + Dim snippetClient = expansionClientFactory.GetOrCreateSnippetExpansionClient(document, textView, subjectBuffer) Dim trackingSpan = triggerSnapshot.CreateTrackingSpan(completionItem.Span.ToSpan(), SpanTrackingMode.EdgeInclusive) Dim currentSpan = trackingSpan.GetSpan(subjectBuffer.CurrentSnapshot) diff --git a/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetExpansionClient.vb b/src/VisualStudio/VisualBasic/Impl/Snippets/VisualBasicSnippetExpansionLanguageHelper.vb similarity index 69% rename from src/VisualStudio/VisualBasic/Impl/Snippets/SnippetExpansionClient.vb rename to src/VisualStudio/VisualBasic/Impl/Snippets/VisualBasicSnippetExpansionLanguageHelper.vb index ad062410bd692..01edd5338e9f2 100644 --- a/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetExpansionClient.vb +++ b/src/VisualStudio/VisualBasic/Impl/Snippets/VisualBasicSnippetExpansionLanguageHelper.vb @@ -2,116 +2,47 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports System.Collections.Immutable +Imports System.Composition Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.AddImport -Imports Microsoft.CodeAnalysis.Completion -Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp Imports Microsoft.CodeAnalysis.Editor.Shared.Extensions -Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Host.Mef -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Shared.Extensions Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.Extensions Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Imports Microsoft.VisualStudio.Editor -Imports Microsoft.VisualStudio.LanguageServices.Implementation.Snippets +Imports Microsoft.VisualStudio.LanguageServices.Snippets Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor -Imports Microsoft.VisualStudio.Text.Editor.Commanding +Imports Microsoft.VisualStudio.TextManager.Interop Imports VsTextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets - Friend NotInheritable Class SnippetExpansionClient - Inherits AbstractSnippetExpansionClient - - Public Sub New( - threadingContext As IThreadingContext, - languageServiceId As Guid, - textView As ITextView, - subjectBuffer As ITextBuffer, - signatureHelpControllerProvider As SignatureHelpControllerProvider, - editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory, - editorAdaptersFactoryService As IVsEditorAdaptersFactoryService, - argumentProviders As ImmutableArray(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)), - editorOptionsService As EditorOptionsService) - MyBase.New(threadingContext, - languageServiceId, - textView, - subjectBuffer, - signatureHelpControllerProvider, - editorCommandHandlerServiceFactory, - editorAdaptersFactoryService, - argumentProviders, - editorOptionsService) + + <[Shared]> + Friend NotInheritable Class VisualBasicSnippetExpansionLanguageHelper + Inherits AbstractSnippetExpansionLanguageHelper + + + + Public Sub New() End Sub - Public Shared Function GetSnippetExpansionClient( - threadingContext As IThreadingContext, - textView As ITextView, - subjectBuffer As ITextBuffer, - signatureHelpControllerProvider As SignatureHelpControllerProvider, - editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory, - editorAdaptersFactoryService As IVsEditorAdaptersFactoryService, - argumentProviders As ImmutableArray(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata)), - editorOptionsService As EditorOptionsService) As AbstractSnippetExpansionClient - - Dim expansionClient As AbstractSnippetExpansionClient = Nothing - - If Not textView.Properties.TryGetProperty(GetType(AbstractSnippetExpansionClient), expansionClient) Then - expansionClient = New SnippetExpansionClient( - threadingContext, - Guids.VisualBasicDebuggerLanguageId, - textView, - subjectBuffer, - signatureHelpControllerProvider, - editorCommandHandlerServiceFactory, - editorAdaptersFactoryService, - argumentProviders, - editorOptionsService) - textView.Properties.AddProperty(GetType(AbstractSnippetExpansionClient), expansionClient) - End If - - Return expansionClient - End Function - - Protected Overrides Function InsertEmptyCommentAndGetEndPositionTrackingSpan() As ITrackingSpan - Dim endSpanInSurfaceBuffer(1) As VsTextSpan - If ExpansionSession.GetEndSpan(endSpanInSurfaceBuffer) <> VSConstants.S_OK Then - Return Nothing - End If - - Dim endSpan As SnapshotSpan = Nothing - If Not TryGetSubjectBufferSpan(endSpanInSurfaceBuffer(0), endSpan) Then - Return Nothing - End If - - Dim endPositionLine = SubjectBuffer.CurrentSnapshot.GetLineFromPosition(endSpan.Start.Position) - Dim endLineText = endPositionLine.GetText() - - If endLineText.Trim() = String.Empty Then - Dim commentString = "'" - SubjectBuffer.Insert(endSpan.Start.Position, commentString) - - Dim commentSpan = New Span(endSpan.Start.Position, commentString.Length) - Return SubjectBuffer.CurrentSnapshot.CreateTrackingSpan(commentSpan, SpanTrackingMode.EdgeExclusive) - End If - - Return Nothing - End Function + Public Overrides ReadOnly Property LanguageServiceGuid As Guid + Get + Return Guids.VisualBasicDebuggerLanguageId + End Get + End Property - Protected Overrides ReadOnly Property FallbackDefaultLiteral As String = "Nothing" + Public Overrides ReadOnly Property FallbackDefaultLiteral As String + Get + Return "Nothing" + End Get + End Property - Friend Overrides Function AddImports( - document As Document, - addImportOptions As AddImportPlacementOptions, - formattingOptions As SyntaxFormattingOptions, - position As Integer, - snippetNode As XElement, - cancellationToken As CancellationToken) As Document + Public Overrides Function AddImports(document As Document, addImportOptions As AddImportPlacementOptions, formattingOptions As SyntaxFormattingOptions, position As Integer, snippetNode As XElement, cancellationToken As CancellationToken) As Document Dim importsNode = snippetNode.Element(XName.Get("Imports", snippetNode.Name.NamespaceName)) If importsNode Is Nothing OrElse Not importsNode.HasElements() Then @@ -142,6 +73,31 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets Return formattedDocument End Function + Public Overrides Function InsertEmptyCommentAndGetEndPositionTrackingSpan(expansionSession As IVsExpansionSession, textView As ITextView, subjectBuffer As ITextBuffer) As ITrackingSpan + Dim endSpanInSurfaceBuffer(1) As VsTextSpan + If expansionSession.GetEndSpan(endSpanInSurfaceBuffer) <> VSConstants.S_OK Then + Return Nothing + End If + + Dim endSpan As SnapshotSpan = Nothing + If Not TryGetSubjectBufferSpan(textView, subjectBuffer, endSpanInSurfaceBuffer(0), endSpan) Then + Return Nothing + End If + + Dim endPositionLine = subjectBuffer.CurrentSnapshot.GetLineFromPosition(endSpan.Start.Position) + Dim endLineText = endPositionLine.GetText() + + If endLineText.Trim() = String.Empty Then + Dim commentString = "'" + subjectBuffer.Insert(endSpan.Start.Position, commentString) + + Dim commentSpan = New Span(endSpan.Start.Position, commentString.Length) + Return subjectBuffer.CurrentSnapshot.CreateTrackingSpan(commentSpan, SpanTrackingMode.EdgeExclusive) + End If + + Return Nothing + End Function + Private Shared Function GetImportsStatementsToAdd(document As Document, snippetNode As XElement, importsNode As XElement, cancellationToken As CancellationToken) As IList(Of ImportsStatementSyntax) Dim root = document.GetSyntaxRootSynchronously(cancellationToken) Dim localImportsClauses = CType(root, CompilationUnitSyntax).Imports.SelectMany(Function(x) x.ImportsClauses) @@ -246,4 +202,3 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets End Function End Class End Namespace -