/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs
309 строк
13 KB
Jason Malinowski
Remove the eager fetch of IVsEditorAdaptersFactoryService
29 авг 2025, 00:35
29 авг 2025, 00:35
4294749
Код
Авторство
О чём код?
// 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.Diagnostics; using System.Linq; using System.Windows.Forms; using System.Windows.Forms.Integration; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Options; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.LanguageServices.DocumentOutline; using Microsoft.VisualStudio.LanguageServices.Implementation.NavigationBar; using Microsoft.VisualStudio.LanguageServices.Utilities; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text.Outlining; using Microsoft.VisualStudio.TextManager.Interop; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; internal abstract partial class AbstractLanguageService<TPackage, TLanguageService> { internal sealed class VsCodeWindowManager : IVsCodeWindowManager, IVsCodeWindowEvents, IVsDocOutlineProvider { private readonly TLanguageService _languageService; private readonly IVsCodeWindow _codeWindow; private readonly ComEventSink _sink; private readonly IGlobalOptionService _globalOptions; private IDisposable? _navigationBarController; private IVsDropdownBarClient? _dropdownBarClient; private ElementHost? _documentOutlineViewHost; private DocumentOutlineView? _documentOutlineView; public VsCodeWindowManager(TLanguageService languageService, IVsCodeWindow codeWindow) { _languageService = languageService; _codeWindow = codeWindow; _globalOptions = languageService.Package.ComponentModel.GetService<IGlobalOptionService>(); _sink = ComEventSink.Advise<IVsCodeWindowEvents>(codeWindow, this); _globalOptions.AddOptionChangedHandler(this, GlobalOptionChanged); } private void SetupView(IVsTextView view) => _languageService.SetupNewTextView(view); private void GlobalOptionChanged(object sender, object target, OptionChangedEventArgs e) { if (e.ChangedOptions.Any(item => item.key.Language == _languageService.RoslynLanguageName && item.key.Option.Equals(NavigationBarViewOptionsStorage.ShowNavigationBar))) { AddOrRemoveDropdown(); } } private void AddOrRemoveDropdown() { if (_codeWindow is not IVsDropdownBarManager dropdownManager) { return; } if (ErrorHandler.Failed(_codeWindow.GetBuffer(out var buffer)) || buffer == null) { return; } var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService<IVsEditorAdaptersFactoryService>(); var textBuffer = editorAdaptersFactoryService.GetDataBuffer(buffer); var document = textBuffer?.AsTextContainer()?.GetRelatedDocuments().FirstOrDefault(); // TODO - Remove the TS check once they move the liveshare navbar to LSP. Then we can also switch to LSP // for the local navbar implementation. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1163360 if (textBuffer?.IsInLspEditorContext() == true && document!.Project!.Language != InternalLanguageNames.TypeScript) { // Remove the existing dropdown bar if it is ours. if (IsOurDropdownBar(dropdownManager, out var _)) { RemoveDropdownBar(dropdownManager); } return; } var enabled = _globalOptions.GetOption(NavigationBarViewOptionsStorage.ShowNavigationBar, _languageService.RoslynLanguageName); if (enabled) { if (IsOurDropdownBar(dropdownManager, out var existingDropdownBar)) { // The dropdown bar is already one of ours, do nothing. return; } if (existingDropdownBar != null) { // Not ours, so remove the old one so that we can add ours. RemoveDropdownBar(dropdownManager); } else { Contract.ThrowIfFalse(_navigationBarController == null, "We shouldn't have a controller manager if there isn't a dropdown"); Contract.ThrowIfFalse(_dropdownBarClient == null, "We shouldn't have a dropdown client if there isn't a dropdown"); } AddDropdownBar(dropdownManager); } else { RemoveDropdownBar(dropdownManager); } bool IsOurDropdownBar(IVsDropdownBarManager dropdownBarManager, out IVsDropdownBar? existingDropdownBar) { existingDropdownBar = GetDropdownBar(dropdownBarManager); if (existingDropdownBar != null) { if (_dropdownBarClient != null && _dropdownBarClient == GetDropdownBarClient(existingDropdownBar)) { return true; } } return false; } } private static IVsDropdownBar GetDropdownBar(IVsDropdownBarManager dropdownManager) { ErrorHandler.ThrowOnFailure(dropdownManager.GetDropdownBar(out var existingDropdownBar)); return existingDropdownBar; } private static IVsDropdownBarClient GetDropdownBarClient(IVsDropdownBar dropdownBar) { ErrorHandler.ThrowOnFailure(dropdownBar.GetClient(out var dropdownBarClient)); return dropdownBarClient; } private void AddDropdownBar(IVsDropdownBarManager dropdownManager) { if (ErrorHandler.Failed(_codeWindow.GetBuffer(out var buffer))) { return; } var navigationBarClient = new NavigationBarClient(dropdownManager, _codeWindow, _languageService.SystemServiceProvider, _languageService.Workspace.Value); var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService<IVsEditorAdaptersFactoryService>(); var textBuffer = editorAdaptersFactoryService.GetDataBuffer(buffer); var controllerFactoryService = _languageService.Package.ComponentModel.GetService<INavigationBarControllerFactoryService>(); var newController = controllerFactoryService.CreateController(navigationBarClient, textBuffer!); var hr = dropdownManager.AddDropdownBar(cCombos: 3, pClient: navigationBarClient); if (ErrorHandler.Failed(hr)) { newController.Dispose(); ErrorHandler.ThrowOnFailure(hr); } _navigationBarController = newController; _dropdownBarClient = navigationBarClient; return; } private void RemoveDropdownBar(IVsDropdownBarManager dropdownManager) { if (ErrorHandler.Succeeded(dropdownManager.RemoveDropdownBar())) { if (_navigationBarController != null) { _navigationBarController.Dispose(); _navigationBarController = null; } _dropdownBarClient = null; } } public int AddAdornments() { int hr; if (ErrorHandler.Failed(hr = _codeWindow.GetPrimaryView(out var primaryView))) { Debug.Fail("GetPrimaryView failed in IVsCodeWindowManager.AddAdornments"); return hr; } SetupView(primaryView); if (ErrorHandler.Succeeded(_codeWindow.GetSecondaryView(out var secondaryView))) { SetupView(secondaryView); } AddOrRemoveDropdown(); return VSConstants.S_OK; } public int OnCloseView(IVsTextView view) { return VSConstants.S_OK; } public int OnNewView(IVsTextView view) { SetupView(view); return VSConstants.S_OK; } public int RemoveAdornments() { _sink.Unadvise(); _globalOptions.RemoveOptionChangedHandler(this, GlobalOptionChanged); if (_codeWindow is IVsDropdownBarManager dropdownManager) { RemoveDropdownBar(dropdownManager); } return VSConstants.S_OK; } // GetOutline is called every time a new code window is created. Whenever we switch to a different window, it is guaranteed // that ReleaseOutline will be called on the old window before GetOutline is called for the new window. int IVsDocOutlineProvider.GetOutline(out IntPtr phwnd, out IOleCommandTarget? pCmdTarget) { pCmdTarget = null; GetOutline(out phwnd); return VSConstants.S_OK; } private void GetOutline(out IntPtr phwnd) { var threadingContext = _languageService.Package.ComponentModel.GetService<IThreadingContext>(); threadingContext.ThrowIfNotOnUIThread(); var uiShell = (IVsUIShell4)_languageService.SystemServiceProvider.GetService(typeof(SVsUIShell)); var windowSearchHostFactory = (IVsWindowSearchHostFactory)_languageService.SystemServiceProvider.GetService(typeof(SVsWindowSearchHostFactory)); var languageServiceBroker = _languageService.Package.ComponentModel.GetService<ILanguageServiceBroker2>(); var asyncListenerProvider = _languageService.Package.ComponentModel.GetService<IAsynchronousOperationListenerProvider>(); var asyncListener = asyncListenerProvider.GetListener(FeatureAttribute.DocumentOutline); var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService<IVsEditorAdaptersFactoryService>(); var outliningManagerService = _languageService.Package.ComponentModel.GetService<IOutliningManagerService>(); // Assert that the previous Document Outline Control and host have been freed. Contract.ThrowIfFalse(_documentOutlineView is null); Contract.ThrowIfFalse(_documentOutlineViewHost is null); var viewTracker = new VsCodeWindowViewTracker(_codeWindow, threadingContext, editorAdaptersFactoryService); _documentOutlineView = new DocumentOutlineView( uiShell, windowSearchHostFactory, threadingContext, _globalOptions, outliningManagerService, viewTracker, new DocumentOutlineViewModel(threadingContext, viewTracker, languageServiceBroker, asyncListener)); _documentOutlineViewHost = new ElementHost { Dock = DockStyle.Fill, Child = _documentOutlineView }; phwnd = _documentOutlineViewHost.Handle; Logger.Log(FunctionId.DocumentOutline_WindowOpen, logLevel: LogLevel.Information); } int IVsDocOutlineProvider.ReleaseOutline(IntPtr hwnd, IOleCommandTarget pCmdTarget) { var threadingContext = _languageService.Package.ComponentModel.GetService<IThreadingContext>(); threadingContext.ThrowIfNotOnUIThread(); if (_documentOutlineView is not null && _documentOutlineViewHost is not null) { _documentOutlineViewHost.SuspendLayout(); _documentOutlineView.Dispose(); _documentOutlineView = null; _documentOutlineViewHost.Child = null; _documentOutlineViewHost.Parent = null; _documentOutlineViewHost.Dispose(); _documentOutlineViewHost = null; } return VSConstants.S_OK; } int IVsDocOutlineProvider.GetOutlineCaption(VSOUTLINECAPTION nCaptionType, out string pbstrCaption) { pbstrCaption = ServicesVSResources.Document_Outline; return VSConstants.S_OK; } int IVsDocOutlineProvider.OnOutlineStateChange(uint dwMask, uint dwState) { return VSConstants.S_OK; } } }