/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Core/Def/InheritanceMargin/InheritanceGlyphManager.cs
229 строк
9 KB
Cyrus Najmabadi
Rename top level types
05 июн 2024, 21:20
05 июн 2024, 21:20
22e9dfa
Код
Авторство
О чём код?
// 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.Collections.Immutable; using System.Linq; using System.Windows.Controls; using System.Windows.Media; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMargin.MarginGlyph; using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Formatting; using Microsoft.VisualStudio.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMargin; /// <summary> /// Manager controls all the glyphs of Inheritance Margin in <see cref="InheritanceMarginViewMargin"/>. /// </summary> internal sealed partial class InheritanceGlyphManager : IDisposable { // We want to our glyphs to have the same background color as the glyphs in GlyphMargin. private const string GlyphMarginName = "Indicator Margin"; private readonly Workspace _workspace; private readonly double _heightAndWidthOfTheGlyph; private readonly IWpfTextView _textView; private readonly IThreadingContext _threadingContext; private readonly IStreamingFindUsagesPresenter _streamingFindUsagesPresenter; private readonly ClassificationTypeMap _classificationTypeMap; private readonly IClassificationFormatMap _classificationFormatMap; private readonly IUIThreadOperationExecutor _operationExecutor; private readonly IEditorFormatMap _editorFormatMap; private readonly IAsynchronousOperationListener _listener; private readonly Canvas _glyphsContainer; /// <summary> /// Mutable. Must only be accessed from the UI thread. /// </summary> private SimpleMutableIntervalTree<GlyphData, GlyphDataIntrospector> _glyphDataTree; public InheritanceGlyphManager( Workspace workspace, IWpfTextView textView, IThreadingContext threadingContext, IStreamingFindUsagesPresenter streamingFindUsagesPresenter, IClassificationFormatMap classificationFormatMap, ClassificationTypeMap classificationTypeMap, IUIThreadOperationExecutor operationExecutor, IEditorFormatMap editorFormatMap, IAsynchronousOperationListener listener, Canvas canvas, double heightAndWidthOfTheGlyph) { _workspace = workspace; _textView = textView; _threadingContext = threadingContext; _streamingFindUsagesPresenter = streamingFindUsagesPresenter; _classificationTypeMap = classificationTypeMap; _classificationFormatMap = classificationFormatMap; _operationExecutor = operationExecutor; _editorFormatMap = editorFormatMap; _glyphsContainer = canvas; _listener = listener; _heightAndWidthOfTheGlyph = heightAndWidthOfTheGlyph; _editorFormatMap.FormatMappingChanged += FormatMappingChanged; _glyphDataTree = new(new GlyphDataIntrospector(), values: null); UpdateBackgroundColor(); } void IDisposable.Dispose() { _editorFormatMap.FormatMappingChanged -= FormatMappingChanged; } /// <summary> /// Generate the glyph by the given <paramref name="tag"/>, and add it to the margin. /// It should only be called by UI thread because UI elements are manipulated by this method. /// </summary> public void AddGlyph(InheritanceMarginTag tag, SnapshotSpan span) { _threadingContext.ThrowIfNotOnUIThread(); var lines = _textView.TextViewLines; if (lines.IntersectsBufferSpan(span) && GetStartingLine(lines, span) is IWpfTextViewLine line) { var glyph = CreateNewGlyph(tag); SetTop(line, glyph); _glyphDataTree.AddIntervalInPlace(new GlyphData(span, glyph)); _glyphsContainer.Children.Add(glyph); } } /// <summary> /// Remove the glyphs covered by <paramref name="snapshotSpan"/>. /// It should only be called by UI thread because UI elements are manipulated by this method. /// </summary> public void RemoveGlyphs(SnapshotSpan snapshotSpan) { _threadingContext.ThrowIfNotOnUIThread(); var glyphDataToRemove = _glyphDataTree.GetIntervalsThatIntersectWith(snapshotSpan.Start, snapshotSpan.Length); foreach (var (_, glyph) in glyphDataToRemove) { _glyphsContainer.Children.Remove(glyph); } var remainingGlyphData = _glyphDataTree.Except(glyphDataToRemove).ToImmutableArray(); _glyphDataTree = new(new GlyphDataIntrospector(), values: null); foreach (var glyphData in remainingGlyphData) { _glyphDataTree.AddIntervalInPlace(glyphData); } } /// <summary> /// Remove the glyphs that are no long visible or covered by the <paramref name="newOrReformattedLines"/>. /// Refresh all the other the existing glyphs with the <paramref name="snapshot"/>. /// It should only be called by UI thread because UI elements are manipulated by this method. /// </summary> public void SetSnapshotAndUpdate(ITextSnapshot snapshot, IList<ITextViewLine> newOrReformattedLines, IList<ITextViewLine> translatedLines) { _threadingContext.ThrowIfNotOnUIThread(); if (!_glyphDataTree.IsEmpty()) { // Go through all the existing visuals and invalidate or transform as appropriate. var allGlyphData = _glyphDataTree.ToImmutableArray(); _glyphDataTree = new(new GlyphDataIntrospector(), values: null); foreach (var (span, glyph) in allGlyphData) { var newSpan = span.TranslateTo(snapshot, SpanTrackingMode.EdgeInclusive); if (!_textView.TextViewLines.IntersectsBufferSpan(newSpan) || GetStartingLine(newOrReformattedLines, newSpan) != null) { //Either visual is no longer visible or it crosses a line //that was reformatted. _glyphsContainer.Children.Remove(glyph); } else { _glyphDataTree.AddIntervalInPlace(new GlyphData(newSpan, glyph)); var line = GetStartingLine(translatedLines, newSpan); if (line != null) { SetTop(line, glyph); } } } } } private void SetTop(ITextViewLine line, InheritanceMarginGlyph glyph) => Canvas.SetTop(glyph, line.TextTop - _textView.ViewportTop); private static ITextViewLine? GetStartingLine(IList<ITextViewLine> lines, Span span) { if (lines.Count > 0) { var index = lines.ToImmutableArray().BinarySearch(span.Start, CompareWithLineStartAndEnd); if (index >= 0) { return lines[index]; } var lastLine = lines[^1]; if (lastLine.EndIncludingLineBreak == lastLine.Snapshot.Length && span.Start == lastLine.EndIncludingLineBreak) { // As a special case, if the last line ends at the end of the buffer and the span starts at the end of the buffer // as well, treat is as crossing the last line in the buffer. return lastLine; } } return null; } private static int CompareWithLineStartAndEnd(ITextViewLine line, int value) { if (value < line.Start) { return 1; } // EndIncludingLineBreak usually equals the start of next line (the exclusion is if this is the last line, which will be handled separately), // and we always prefer to use the line start, so still return -1 when value == line.EndIncludingLineBreak. if (value >= line.EndIncludingLineBreak) { return -1; } return 0; } private InheritanceMarginGlyph CreateNewGlyph(InheritanceMarginTag tag) => new( _workspace, _threadingContext, _streamingFindUsagesPresenter, _classificationTypeMap, _classificationFormatMap, _operationExecutor, tag, _textView, _listener) { Height = _heightAndWidthOfTheGlyph, Width = _heightAndWidthOfTheGlyph }; private void FormatMappingChanged(object sender, FormatItemsEventArgs e) => UpdateBackgroundColor(); private void UpdateBackgroundColor() { _threadingContext.ThrowIfNotOnUIThread(); var resourceDictionary = _editorFormatMap.GetProperties(GlyphMarginName); if (resourceDictionary.Contains(EditorFormatDefinition.BackgroundColorId)) { var backgroundColor = (Color)resourceDictionary[EditorFormatDefinition.BackgroundColorId]; // Set background color for all the glyphs ImageThemingUtilities.SetImageBackgroundColor(_glyphsContainer, backgroundColor); } } }