File: InProcess\EditorInProcess.cs
Web Access
Project: src\src\Razor\src\Razor\test\Microsoft.VisualStudio.Razor.IntegrationTests\Microsoft.VisualStudio.Razor.IntegrationTests.csproj (Microsoft.VisualStudio.Razor.IntegrationTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.Settings;
using Microsoft.CodeAnalysis.Razor.Workspaces.Settings;
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Razor.IntegrationTests.Extensions;
using Microsoft.VisualStudio.Razor.IntegrationTests.InProcess;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
 
namespace Microsoft.VisualStudio.Extensibility.Testing;
 
internal partial class EditorInProcess
{
    public async Task<ITextSnapshot> GetActiveSnapshotAsync(CancellationToken cancellationToken)
        => (await GetActiveTextViewAsync(cancellationToken)).TextSnapshot;
 
    public async Task ActivateAsync(CancellationToken cancellationToken)
    {
        await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
 
        var dte = await GetRequiredGlobalServiceAsync<SDTE, EnvDTE.DTE>(cancellationToken);
        dte.ActiveDocument.Activate();
    }
 
    public async Task<bool> IsUseSuggestionModeOnAsync(CancellationToken cancellationToken)
    {
        await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
 
        var textView = await GetActiveTextViewAsync(cancellationToken);
 
        var subjectBuffer = textView.GetBufferContainingCaret();
        Assumes.Present(subjectBuffer);
 
        var options = textView.Options.GlobalOptions;
        EditorOptionKey<bool> optionKey;
        bool defaultOption;
        if (IsDebuggerTextView(textView))
        {
            optionKey = new EditorOptionKey<bool>(PredefinedCompletionNames.SuggestionModeInDebuggerCompletionOptionName);
            defaultOption = true;
        }
        else
        {
            optionKey = new EditorOptionKey<bool>(PredefinedCompletionNames.SuggestionModeInCompletionOptionName);
            defaultOption = false;
        }
 
        if (!options.IsOptionDefined(optionKey, localScopeOnly: false))
        {
            return defaultOption;
        }
 
        return options.GetOptionValue(optionKey);
 
        static bool IsDebuggerTextView(IWpfTextView textView)
        {
            return textView.Roles.Contains("DEBUGVIEW");
        }
    }
 
    public async Task SetUseSuggestionModeAsync(bool value, CancellationToken cancellationToken)
    {
        if (await IsUseSuggestionModeOnAsync(cancellationToken) != value)
        {
            var dispatcher = await GetRequiredGlobalServiceAsync<SUIHostCommandDispatcher, IOleCommandTarget>(cancellationToken);
            ErrorHandler.ThrowOnFailure(dispatcher.Exec(typeof(VSConstants.VSStd2KCmdID).GUID, (uint)VSConstants.VSStd2KCmdID.ToggleConsumeFirstCompletionMode, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero));
 
            if (await IsUseSuggestionModeOnAsync(cancellationToken) != value)
            {
                throw new InvalidOperationException($"Edit.ToggleCompletionMode did not leave the editor in the expected state.");
            }
        }
 
        if (!value)
        {
            // For blocking completion mode, make sure we don't have responsive completion interfering when
            // integration tests run slowly.
            await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
 
            var view = await GetActiveTextViewAsync(cancellationToken);
            var options = view.Options.GlobalOptions;
            options.SetOptionValue(DefaultOptions.ResponsiveCompletionOptionId, false);
 
            var latencyGuardOptionKey = new EditorOptionKey<bool>("EnableTypingLatencyGuard");
            options.SetOptionValue(latencyGuardOptionKey, false);
        }
    }
 
    public async Task SetAdvancedSettingsAsync(ClientAdvancedSettings clientAdvancedSettings, CancellationToken cancellationToken)
    {
        var clientSettingsManager = await GetComponentModelServiceAsync<IClientSettingsManager>(cancellationToken);
 
        clientSettingsManager.Update(clientAdvancedSettings);
    }
 
    public async Task<ImmutableArray<TagSpan<TTag>>> WaitForTagsAsync<TTag>(CancellationToken cancellationToken)
        where TTag : ITag
    {
        await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
 
        return await Helper.RetryAsync(async ct =>
        {
            var tags = await GetTagsAsync<TTag>(cancellationToken);
            if (tags.Length == 0)
            {
                return default;
            }
 
            return tags;
        }, TimeSpan.FromMilliseconds(50), cancellationToken);
    }
 
    public async Task<ImmutableArray<TagSpan<TTag>>> GetTagsAsync<TTag>(CancellationToken cancellationToken)
        where TTag : ITag
    {
        await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
 
        var view = await GetActiveTextViewAsync(cancellationToken);
        var viewTagAggregatorFactory = await GetComponentModelServiceAsync<IViewTagAggregatorFactoryService>(cancellationToken);
 
        var aggregator = viewTagAggregatorFactory.CreateTagAggregator<TTag>(view);
        var tags = aggregator
            .GetTags(new SnapshotSpan(view.TextSnapshot, 0, view.TextSnapshot.Length))
            .Cast<IMappingTagSpan<ITag>>();
 
        return tags.SelectAsArray(tag => (new TagSpan<TTag>(tag.Span.GetSpans(view.TextBuffer).Single(), (TTag)tag.Tag)));
    }
}