/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Core/Def/Utilities/VsCodeWindowViewTracker.cs
142 строки
5 KB
Tomáš Matoušek
Add Microsoft.CodeAnalysis.Contracts source package (#76997)
11 фев 2025, 21:45
Не верифицирован
11 фев 2025, 21:45
eeff8ea
Код
Авторство
О чём код?
// 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.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; namespace Microsoft.VisualStudio.LanguageServices.Utilities; /// <summary> /// A helper class that makes getting the "current" position of the cursor in an <see cref="IVsCodeWindow"/> easier to do. This is necessary /// because a <see cref="IVsCodeWindow"/> can have more than one view if there's a split involved. Watching for cursor changes also requires /// tracking the lifetime of the views as appropriate. /// </summary> /// <remarks> /// All members of this class are UI thread affinitized, including the constructor. /// </remarks> internal sealed class VsCodeWindowViewTracker : IDisposable, IVsCodeWindowEvents { private readonly IVsCodeWindow _codeWindow; private readonly IThreadingContext _threadingContext; private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService; private readonly ComEventSink _codeWindowEventsSink; /// <summary> /// The map from <see cref="IVsTextView"/> and corresponding <see cref="ITextView"/> for views that we are currently watching for caret movements. /// </summary> private readonly Dictionary<IVsTextView, ITextView> _trackedTextViews = []; public VsCodeWindowViewTracker(IVsCodeWindow codeWindow, IThreadingContext threadingContext, IVsEditorAdaptersFactoryService editorAdaptersFactoryService) { _codeWindow = codeWindow; _threadingContext = threadingContext; _editorAdaptersFactoryService = editorAdaptersFactoryService; _threadingContext.ThrowIfNotOnUIThread(); _codeWindowEventsSink = ComEventSink.Advise<IVsCodeWindowEvents>(codeWindow, this); if (ErrorHandler.Succeeded(_codeWindow.GetPrimaryView(out var pTextView)) && pTextView != null) StartTrackingView(pTextView); // If there is no secondary view, GetSecondaryView will return null if (ErrorHandler.Succeeded(_codeWindow.GetSecondaryView(out pTextView)) && pTextView != null) StartTrackingView(pTextView); } private void StartTrackingView(IVsTextView pTextView) { _threadingContext.ThrowIfNotOnUIThread(); if (!_trackedTextViews.ContainsKey(pTextView)) { var wpfTextView = _editorAdaptersFactoryService.GetWpfTextView(pTextView); if (wpfTextView != null) { _trackedTextViews.Add(pTextView, wpfTextView); wpfTextView.Caret.PositionChanged += OnCaretPositionChanged; wpfTextView.GotAggregateFocus += OnViewGotAggregateFocus; } } } private void StopTrackingView(IVsTextView pView) { _threadingContext.ThrowIfNotOnUIThread(); if (_trackedTextViews.TryGetValue(pView, out var view)) { view.Caret.PositionChanged -= OnCaretPositionChanged; view.GotAggregateFocus -= OnViewGotAggregateFocus; _trackedTextViews.Remove(pView); } } public ITextView GetActiveView() { _threadingContext.ThrowIfNotOnUIThread(); ErrorHandler.ThrowOnFailure(_codeWindow.GetLastActiveView(out var pView)); Contract.ThrowIfNull(pView, $"{nameof(IVsCodeWindow.GetLastActiveView)} returned success, but did not provide a view."); var view = _editorAdaptersFactoryService.GetWpfTextView(pView); Contract.ThrowIfNull(view, "The active view should be initialized."); return view; } int IVsCodeWindowEvents.OnNewView(IVsTextView pView) { _threadingContext.ThrowIfNotOnUIThread(); StartTrackingView(pView); return VSConstants.S_OK; } int IVsCodeWindowEvents.OnCloseView(IVsTextView pView) { _threadingContext.ThrowIfNotOnUIThread(); StopTrackingView(pView); return VSConstants.S_OK; } /// <summary> /// Raised when the a caret has moved in a view, or when the current view has changed. /// </summary> /// <remarks> /// This is combined into one event since in practice consumers need to respond to either the same way, by refreshing what the current /// symbol or token or whatever is. /// </remarks> public event EventHandler<EventArgs>? CaretMovedOrActiveViewChanged; private void OnCaretPositionChanged(object sender, CaretPositionChangedEventArgs e) => CaretMovedOrActiveViewChanged?.Invoke(this, e); private void OnViewGotAggregateFocus(object sender, EventArgs e) => CaretMovedOrActiveViewChanged?.Invoke(this, e); public void Dispose() { // StopTrackingView will update _trackedTextViews; the ToList() avoids modification during enumeration foreach (var view in _trackedTextViews.Keys.ToList()) { StopTrackingView(view); } Debug.Assert(_trackedTextViews.Count == 0); _codeWindowEventsSink.Unadvise(); } }