diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs index 9b5be14eb0845..9820e02d3087e 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs @@ -20,7 +20,7 @@ int IVsLanguageDebugInfo.GetLanguageID(IVsTextBuffer pBuffer, int iLine, int iCo { try { - return LanguageDebugInfo.GetLanguageID(pBuffer, iLine, iCol, out pguidLanguageID); + return _languageDebugInfo.GetLanguageID(pBuffer, iLine, iCol, out pguidLanguageID); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -32,7 +32,7 @@ int IVsLanguageDebugInfo.GetLocationOfName(string pszName, out string pbstrMkDoc { try { - return LanguageDebugInfo.GetLocationOfName(pszName, out pbstrMkDoc, out pspanLocation); + return _languageDebugInfo.GetLocationOfName(pszName, out pbstrMkDoc, out pspanLocation); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -44,7 +44,7 @@ int IVsLanguageDebugInfo.GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int { try { - return LanguageDebugInfo.GetNameOfLocation(pBuffer, iLine, iCol, out pbstrName, out piLineOffset); + return _languageDebugInfo.GetNameOfLocation(pBuffer, iLine, iCol, out pbstrName, out piLineOffset); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -56,7 +56,7 @@ int IVsLanguageDebugInfo.GetProximityExpressions(IVsTextBuffer pBuffer, int iLin { try { - return LanguageDebugInfo.GetProximityExpressions(pBuffer, iLine, iCol, cLines, out ppEnum); + return _languageDebugInfo.GetProximityExpressions(pBuffer, iLine, iCol, cLines, out ppEnum); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -68,7 +68,7 @@ int IVsLanguageDebugInfo.IsMappedLocation(IVsTextBuffer pBuffer, int iLine, int { try { - return LanguageDebugInfo.IsMappedLocation(pBuffer, iLine, iCol); + return _languageDebugInfo.IsMappedLocation(pBuffer, iLine, iCol); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -80,7 +80,7 @@ int IVsLanguageDebugInfo.ResolveName(string pszName, uint dwFlags, out IVsEnumDe { try { - return LanguageDebugInfo.ResolveName(pszName, dwFlags, out ppNames); + return _languageDebugInfo.ResolveName(pszName, dwFlags, out ppNames); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -92,7 +92,7 @@ int IVsLanguageDebugInfo.ValidateBreakpointLocation(IVsTextBuffer pBuffer, int i { try { - return LanguageDebugInfo.ValidateBreakpointLocation(pBuffer, iLine, iCol, pCodeSpan); + return _languageDebugInfo.ValidateBreakpointLocation(pBuffer, iLine, iCol, pCodeSpan); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs index 6709e478ce7ac..1ab4251807f9f 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs @@ -7,6 +7,8 @@ using System; using System.Diagnostics; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -19,13 +21,12 @@ using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; -using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.LanguageServices.Implementation.Venus; -using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Outlining; using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; @@ -36,7 +37,8 @@ internal abstract partial class AbstractLanguageService { internal TPackage Package { get; } - internal VsLanguageDebugInfo LanguageDebugInfo { get; private set; } + + private readonly VsLanguageDebugInfo _languageDebugInfo; // DevDiv 753309: // We've redefined some VS interfaces that had incorrect PIAs. When @@ -56,9 +58,9 @@ internal abstract partial class AbstractLanguageService /// Whether or not we have been set up. This is set once everything is wired up and cleared once tear down has begun. @@ -70,9 +72,21 @@ internal abstract partial class AbstractLanguageService private bool _isSetUp; + protected abstract string ContentTypeName { get; } + protected abstract string LanguageName { get; } + protected abstract string RoslynLanguageName { get; } + protected abstract Guid DebuggerLanguageId { get; } + protected AbstractLanguageService(TPackage package) { Package = package; + + Debug.Assert(!this.Package.JoinableTaskFactory.Context.IsOnMainThread, "Language service should be instantiated on background thread"); + + this.EditorOptionsService = this.Package.ComponentModel.GetService(); + this.Workspace = this.Package.ComponentModel.GetService(); + this.EditorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + this._languageDebugInfo = CreateLanguageDebugInfo(); } public override IServiceProvider SystemServiceProvider @@ -81,32 +95,28 @@ public override IServiceProvider SystemServiceProvider /// /// Setup and TearDown go in reverse order. /// - internal void Setup() + public async Task SetupAsync(CancellationToken cancellationToken) { - this.ComAggregate = CreateComAggregate(); - // First, acquire any services we need throughout our lifetime. - this.GetServices(); + // This method should only contain calls to acquire services off of the component model + // or service providers. Anything else which is more complicated should go in Initialize + // instead. - // TODO: Is the below access to component model required or can be removed? - _ = this.Package.ComponentModel; + // Start off a background task to prime some components we'll need for editing. + Task.Run(() => + { + var formatter = this.Workspace.Services.GetLanguageServices(RoslynLanguageName).GetService(); + formatter?.GetDefaultFormattingRules(); + }, cancellationToken).Forget(); - // Start off a background task to prime some components we'll need for editing - VsTaskLibraryHelper.CreateAndStartTask(VsTaskLibraryHelper.ServiceInstance, VsTaskRunContext.BackgroundThread, - () => PrimeLanguageServiceComponentsOnBackground()); + await this.Package.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - // Finally, once our connections are established, set up any initial state that we need. - // Note: we may be instantiated at any time (including when the IDE is already - // debugging). We must not assume anything about our initial state and must instead - // query for all the information we need at this point. - this.Initialize(); + // Creating the com aggregate has to happen on the UI thread. + this.ComAggregate = Interop.ComAggregate.CreateAggregatedObject(this); _isSetUp = true; } - private object CreateComAggregate() - => Interop.ComAggregate.CreateAggregatedObject(this); - internal void TearDown() { if (!_isSetUp) @@ -116,9 +126,6 @@ internal void TearDown() _isSetUp = false; GC.SuppressFinalize(this); - - this.Uninitialize(); - this.RemoveServices(); } ~AbstractLanguageService() @@ -129,49 +136,6 @@ internal void TearDown() } } - protected virtual void GetServices() - { - // This method should only contain calls to acquire services off of the component model - // or service providers. Anything else which is more complicated should go in Initialize - // instead. - this.EditorOptionsService = this.Package.ComponentModel.GetService(); - this.Workspace = this.Package.ComponentModel.GetService(); - this.EditorAdaptersFactoryService = this.Package.ComponentModel.GetService(); - } - - protected virtual void RemoveServices() - { - this.EditorAdaptersFactoryService = null; - this.Workspace = null; - } - - /// - /// Called right after we instantiate the language service. Used to set up any internal - /// state we need. - /// - /// Try to keep this method fairly clean. Any complicated logic should go in methods called - /// from this one. Initialize and Uninitialize go in reverse order - /// - protected virtual void Initialize() - { - InitializeLanguageDebugInfo(); - } - - protected virtual void Uninitialize() - { - UninitializeLanguageDebugInfo(); - } - - private void PrimeLanguageServiceComponentsOnBackground() - { - var formatter = this.Workspace.Services.GetLanguageServices(RoslynLanguageName).GetService(); - formatter?.GetDefaultFormattingRules(); - } - - protected abstract string ContentTypeName { get; } - protected abstract string LanguageName { get; } - protected abstract string RoslynLanguageName { get; } - protected virtual void SetupNewTextView(IVsTextView textView) { Contract.ThrowIfNull(textView); @@ -253,15 +217,9 @@ private void ConditionallyCollapseOutliningRegions(IVsTextView textView, IWpfTex } } - private void InitializeLanguageDebugInfo() - => this.LanguageDebugInfo = this.CreateLanguageDebugInfo(); - - protected abstract Guid DebuggerLanguageId { get; } - private VsLanguageDebugInfo CreateLanguageDebugInfo() { - var workspace = this.Workspace; - var languageServices = workspace.Services.GetLanguageServices(RoslynLanguageName); + var languageServices = this.Workspace.Services.GetLanguageServices(RoslynLanguageName); return new VsLanguageDebugInfo( this.DebuggerLanguageId, @@ -271,9 +229,6 @@ private VsLanguageDebugInfo CreateLanguageDebugInfo() this.Package.ComponentModel.GetService()); } - private void UninitializeLanguageDebugInfo() - => this.LanguageDebugInfo = null; - protected virtual IVsContainedLanguage CreateContainedLanguage( IVsTextBufferCoordinator bufferCoordinator, ProjectSystemProject project, IVsHierarchy hierarchy, uint itemid) diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs index b4c28e70037d4..d53c66478e03d 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs @@ -51,14 +51,16 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke RegisterEditorFactory(editorFactory); } - RegisterLanguageService(typeof(TLanguageService), async ct => + RegisterLanguageService(typeof(TLanguageService), async cancellationToken => { - await JoinableTaskFactory.SwitchToMainThreadAsync(ct); + // Ensure we're on the BG when creating the language service. + await TaskScheduler.Default; // Create the language service, tell it to set itself up, then store it in a field // so we can notify it that it's time to clean up. _languageService = CreateLanguageService(); - _languageService.Setup(); + await _languageService.SetupAsync(cancellationToken).ConfigureAwait(false); + return _languageService.ComAggregate; });