Skip to content

Commit

Permalink
Merge pull request #73351 from CyrusNajmabadi/bgLanguageService
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi authored May 6, 2024
2 parents a20e169 + 3e35578 commit 01a23ff
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand All @@ -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))
{
Expand All @@ -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))
{
Expand All @@ -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))
{
Expand All @@ -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))
{
Expand All @@ -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))
{
Expand All @@ -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))
{
Expand Down
113 changes: 34 additions & 79 deletions src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -36,7 +37,8 @@ internal abstract partial class AbstractLanguageService<TPackage, TLanguageServi
where TLanguageService : AbstractLanguageService<TPackage, TLanguageService>
{
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
Expand All @@ -56,9 +58,9 @@ internal abstract partial class AbstractLanguageService<TPackage, TLanguageServi
// Note: The lifetime for state in this class is carefully managed. For every bit of state
// we set up, there is a corresponding tear down phase which deconstructs the state in the
// reverse order it was created in.
internal EditorOptionsService EditorOptionsService { get; private set; }
internal VisualStudioWorkspaceImpl Workspace { get; private set; }
internal IVsEditorAdaptersFactoryService EditorAdaptersFactoryService { get; private set; }
internal readonly EditorOptionsService EditorOptionsService;
internal readonly VisualStudioWorkspaceImpl Workspace;
internal readonly IVsEditorAdaptersFactoryService EditorAdaptersFactoryService;

/// <summary>
/// Whether or not we have been set up. This is set once everything is wired up and cleared once tear down has begun.
Expand All @@ -70,9 +72,21 @@ internal abstract partial class AbstractLanguageService<TPackage, TLanguageServi
/// </remarks>
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<EditorOptionsService>();
this.Workspace = this.Package.ComponentModel.GetService<VisualStudioWorkspaceImpl>();
this.EditorAdaptersFactoryService = this.Package.ComponentModel.GetService<IVsEditorAdaptersFactoryService>();
this._languageDebugInfo = CreateLanguageDebugInfo();
}

public override IServiceProvider SystemServiceProvider
Expand All @@ -81,32 +95,28 @@ public override IServiceProvider SystemServiceProvider
/// <summary>
/// Setup and TearDown go in reverse order.
/// </summary>
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<ISyntaxFormattingService>();
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)
Expand All @@ -116,9 +126,6 @@ internal void TearDown()

_isSetUp = false;
GC.SuppressFinalize(this);

this.Uninitialize();
this.RemoveServices();
}

~AbstractLanguageService()
Expand All @@ -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<EditorOptionsService>();
this.Workspace = this.Package.ComponentModel.GetService<VisualStudioWorkspaceImpl>();
this.EditorAdaptersFactoryService = this.Package.ComponentModel.GetService<IVsEditorAdaptersFactoryService>();
}

protected virtual void RemoveServices()
{
this.EditorAdaptersFactoryService = null;
this.Workspace = null;
}

/// <summary>
/// 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
/// </summary>
protected virtual void Initialize()
{
InitializeLanguageDebugInfo();
}

protected virtual void Uninitialize()
{
UninitializeLanguageDebugInfo();
}

private void PrimeLanguageServiceComponentsOnBackground()
{
var formatter = this.Workspace.Services.GetLanguageServices(RoslynLanguageName).GetService<ISyntaxFormattingService>();
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);
Expand Down Expand Up @@ -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,
Expand All @@ -271,9 +229,6 @@ private VsLanguageDebugInfo CreateLanguageDebugInfo()
this.Package.ComponentModel.GetService<IUIThreadOperationExecutor>());
}

private void UninitializeLanguageDebugInfo()
=> this.LanguageDebugInfo = null;

protected virtual IVsContainedLanguage CreateContainedLanguage(
IVsTextBufferCoordinator bufferCoordinator, ProjectSystemProject project,
IVsHierarchy hierarchy, uint itemid)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});

Expand Down

0 comments on commit 01a23ff

Please sign in to comment.