Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid repeated switches to main thread to check if in command line mode #9531

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ public LanguageServiceHost(
_isEnabled = new(
async () =>
{
await threadingService.JoinableTaskFactory.SwitchToMainThreadAsync();

// If VS is running in command line mode (e.g. "devenv.exe /build my.sln"),
// language services should not be enabled. The one exception to this is
// when we're populating a solution cache via "/populateSolutionCache".
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.VS;
using Microsoft.VisualStudio.ProjectSystem.VS.Interop;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Threading;

Expand All @@ -11,65 +12,54 @@ namespace Microsoft.VisualStudio.Shell;
[AppliesTo(ProjectCapabilities.AlwaysApplicable)]
internal class VSShellServices : IVsShellServices
{

private readonly AsyncLazy<(bool IsCommandLineMode, bool IsPopulateSolutionCacheMode)> _initialization;
private static readonly object s_lockObject = new();
private static Task<bool>? s_isCommandLine;
private static Task<bool>? s_isPopulateCache;
drewnoakes marked this conversation as resolved.
Show resolved Hide resolved

[ImportingConstructor]
public VSShellServices(
IVsUIService<SVsShell, IVsShell> vsShellService,
IVsUIService<SVsAppCommandLine, IVsAppCommandLine> commandLineService,
JoinableTaskContext joinableTaskContext)
IVsService<IVsAppId> vsAppId,
IVsService<SVsAppCommandLine, IVsAppCommandLine> commandLine)
{
_initialization = new(
async () =>
{
// Initialisation must occur on the main thread.
await joinableTaskContext.Factory.SwitchToMainThreadAsync();

IVsShell? vsShell = vsShellService.Value;
IVsAppCommandLine? commandLine = commandLineService.Value;

bool isCommandLineMode = IsCommandLineMode();
lock (s_lockObject)
{
s_isCommandLine ??= IsCommandLineModeAsync(vsAppId);
s_isPopulateCache ??= IsPopulateSolutionCacheModeAsync(commandLine);

if (isCommandLineMode)
{
return (IsCommandLineMode: true, IsPopulateSolutionCacheMode());
}
}

return (false, false);

bool IsCommandLineMode()
{
Assumes.Present(vsShell);

int hr = vsShell.GetProperty((int)__VSSPROPID.VSSPROPID_IsInCommandLineMode, out object result);
static async Task<bool> IsCommandLineModeAsync(IVsService<IVsAppId> vsAppIdService)
{
IVsAppId? vsAppId = await vsAppIdService.GetValueOrNullAsync();
if (vsAppId is not null)
{
const int VSAPROPID_IsInCommandLineMode = (int)Microsoft.Internal.VisualStudio.AppId.Interop.__VSAPROPID10.VSAPROPID_IsInCommandLineMode;
return ErrorHandler.Succeeded(vsAppId.GetProperty(VSAPROPID_IsInCommandLineMode, out object o)) && o is bool isInCommandLineMode && isInCommandLineMode;
drewnoakes marked this conversation as resolved.
Show resolved Hide resolved
}

return ErrorHandler.Succeeded(hr)
&& result is bool isCommandLineMode
&& isCommandLineMode;
}
return false;
}

bool IsPopulateSolutionCacheMode()
{
if (commandLine is null)
return false;
static async Task<bool> IsPopulateSolutionCacheModeAsync(IVsService<SVsAppCommandLine, IVsAppCommandLine> commandLineService)
{
IVsAppCommandLine? commandLine = await commandLineService.GetValueOrNullAsync();
if (commandLine is null)
return false;

int hr = commandLine.GetOption("populateSolutionCache", out int populateSolutionCache, out string commandValue);
int hr = commandLine.GetOption("populateSolutionCache", out int populateSolutionCache, out string _);

return ErrorHandler.Succeeded(hr)
&& Convert.ToBoolean(populateSolutionCache);
}
},
joinableTaskContext.Factory);
return ErrorHandler.Succeeded(hr)
&& Convert.ToBoolean(populateSolutionCache);
}
}

public async Task<bool> IsCommandLineModeAsync(CancellationToken cancellationToken)
{
return (await _initialization.GetValueAsync(cancellationToken)).IsCommandLineMode;
return s_isCommandLine is not null && await s_isCommandLine;
drewnoakes marked this conversation as resolved.
Show resolved Hide resolved
}

public async Task<bool> IsPopulateSolutionCacheModeAsync(CancellationToken cancellationToken)
{
return (await _initialization.GetValueAsync(cancellationToken)).IsPopulateSolutionCacheMode;
return s_isPopulateCache is not null && await s_isPopulateCache;
}
}
Loading