From a09dc9b458c2b247b816709aca96718a5cdc4376 Mon Sep 17 00:00:00 2001 From: nosami Date: Fri, 7 May 2021 22:37:45 +0100 Subject: [PATCH 1/6] Add first draft of relative numbers --- .../RelativeLineNumbers/CocoaExtensions.cs | 124 +++++ .../CocoaLineNumberMarginDrawingVisual.cs | 94 ++++ .../RelativeLineNumbers/CocoaTextManager.cs | 64 +++ Src/VimMac/RelativeLineNumbers/Line.cs | 27 ++ .../LineNumbersCalculator.cs | 135 ++++++ .../MacRuntimeEnvironment.cs | 12 + .../RelativeLineNumbersMargin.cs | 456 ++++++++++++++++++ .../RelativeLineNumbersMarginFactory.cs | 59 +++ .../Util/TextViewExtensions.cs | 22 + .../Util/TextViewLineExtensions.cs | 12 + Src/VimMac/VimMac.csproj | 4 + 11 files changed, 1009 insertions(+) create mode 100644 Src/VimMac/RelativeLineNumbers/CocoaExtensions.cs create mode 100644 Src/VimMac/RelativeLineNumbers/CocoaLineNumberMarginDrawingVisual.cs create mode 100644 Src/VimMac/RelativeLineNumbers/CocoaTextManager.cs create mode 100644 Src/VimMac/RelativeLineNumbers/Line.cs create mode 100644 Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs create mode 100644 Src/VimMac/RelativeLineNumbers/MacRuntimeEnvironment.cs create mode 100644 Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs create mode 100644 Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMarginFactory.cs create mode 100644 Src/VimMac/RelativeLineNumbers/Util/TextViewExtensions.cs create mode 100644 Src/VimMac/RelativeLineNumbers/Util/TextViewLineExtensions.cs diff --git a/Src/VimMac/RelativeLineNumbers/CocoaExtensions.cs b/Src/VimMac/RelativeLineNumbers/CocoaExtensions.cs new file mode 100644 index 0000000000..bc1b169e3b --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/CocoaExtensions.cs @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Linq; +using System.Windows; +using System.Windows.Media; + +using AppKit; +using CoreAnimation; +using CoreGraphics; +using CoreText; +using Foundation; + +namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers +{ + public static class CocoaExtensions + { + public static double WidthIncludingTrailingWhitespace(this CTLine line) + => line.GetBounds(0).Width; + + //Before "optimising" this to `new CGColor(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);` + //notice that doesn't handle colorspace correctly + public static CGColor AsCGColor(this Color color) + => NSColor.FromRgba(color.R, color.G, color.B, color.A).CGColor; + + public static bool IsDarkColor(this CGColor color) + { + double red; + double green; + double blue; + + var components = color?.Components; + + if (components == null || components.Length == 0) + return false; + + if (components.Length >= 3) + { + red = components[0]; + green = components[1]; + blue = components[2]; + } + else + { + red = green = blue = components[0]; + } + + // https://www.w3.org/WAI/ER/WD-AERT/#color-contrast + var brightness = (red * 255 * 299 + green * 255 * 587 + blue * 255 * 114) / 1000; + return brightness <= 155; + } + + public static bool IsMouseOver(this NSView view) + { + var mousePoint = NSEvent.CurrentMouseLocation; + return IsMouseOver(view, mousePoint); + } + + public static bool IsMouseOver(this NSView view, CGPoint mousePoint) + { + var window = view.Window; + if (window == null) + { + return false; + } + var viewScreenRect = window.ConvertRectToScreen( + view.ConvertRectToView(view.Frame, null)); + return viewScreenRect.Contains(mousePoint); + } + + public static Rect AsRect(this CGRect rect) + { + return new Rect(rect.X, rect.Y, rect.Width, rect.Height); + } + + public static CGPoint AsCGPoint(this Point point) + { + return new CGPoint(point.X, point.Y); + } + + public static Point PointToScreen(this NSView view, Point point) + { + var p = view.ConvertPointToView(new CGPoint(point.X, point.Y), null); + if (view.Window == null) + return new Point(p.X, p.Y); + p = view.Window.ConvertPointToScreen(p); + return new Point(p.X, p.Y); + } + + public static CGPoint PointToScreen(this NSView view, CGPoint point) + { + var p = view.ConvertPointToView(point, null); + if (view.Window == null) + return p; + p = view.Window.ConvertPointToScreen(p); + return p; + } + + static readonly NSDictionary disableAnimations = new NSDictionary( + "actions", NSNull.Null, + "contents", NSNull.Null, + "hidden", NSNull.Null, + "onLayout", NSNull.Null, + "onOrderIn", NSNull.Null, + "onOrderOut", NSNull.Null, + "position", NSNull.Null, + "sublayers", NSNull.Null, + "transform", NSNull.Null, + "bounds", NSNull.Null); + + public static void DisableImplicitAnimations(this CALayer layer) + => layer.Actions = disableAnimations; + } +} + +namespace CoreGraphics +{ + static class CoreGraphicsExtensions + { + public static CGSize InflateToIntegral(this CGSize size) + => new CGSize(NMath.Ceiling(size.Width), NMath.Ceiling(size.Height)); + } +} diff --git a/Src/VimMac/RelativeLineNumbers/CocoaLineNumberMarginDrawingVisual.cs b/Src/VimMac/RelativeLineNumbers/CocoaLineNumberMarginDrawingVisual.cs new file mode 100644 index 0000000000..979c3ebaf5 --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/CocoaLineNumberMarginDrawingVisual.cs @@ -0,0 +1,94 @@ +using System; +using System.Globalization; + +using AppKit; +using CoreAnimation; +using CoreGraphics; +using CoreText; +using Foundation; +using Vim.UI.Wpf.Implementation.RelativeLineNumbers; + +namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers +{ + internal sealed class CocoaLineNumberMarginDrawingVisual : CALayer, ICALayerDelegate + { + NSStringAttributes stringAttributes; + CGRect lineBounds; + nfloat lineAscent; + CTLine ctLine; + + public int LineNumber { get; private set; } + public int DisplayNumber { get; private set; } + + public bool InUse + { + get => !Hidden; + set => Hidden = !value; + } + + public CocoaLineNumberMarginDrawingVisual() + { + Delegate = this; + NeedsDisplayOnBoundsChange = true; + this.DisableImplicitAnimations(); + } + + internal void Update( + NSStringAttributes stringAttributes, + Line line, + nfloat lineWidth) + { + // NOTE: keep this in sync with CocoaRenderedLineVisual regarding any font + // metric handling, transforms, etc. Ensure that line numbers are always + // exactly aligned with the actual editor text lines. Test with Fluent + // Calibri and many other fonts at various sizes. + + if (DisplayNumber != line.DisplayNumber || this.stringAttributes != stringAttributes) + { + DisplayNumber = line.DisplayNumber; + this.stringAttributes = stringAttributes; + + ctLine?.Dispose(); + ctLine = new CTLine(new NSAttributedString( + line.DisplayNumber.ToString(CultureInfo.CurrentUICulture.NumberFormat), + stringAttributes)); + + lineBounds = ctLine.GetBounds(0); + ctLine.GetTypographicBounds(out lineAscent, out _, out _); + + SetNeedsDisplay(); + } + + AffineTransform = new CGAffineTransform( + 1, 0, + 0, 1, + 0, (nfloat)line.TextTop); + + var transformRect = AffineTransform.TransformRect(new CGRect( + line.IsCaretLine ? 0 : lineWidth - lineBounds.Width, // right justify + line.Baseline - lineAscent, + lineBounds.Width, + lineBounds.Height)); + + Frame = new CGRect( + NMath.Floor(transformRect.X), + NMath.Floor(transformRect.Y), + NMath.Ceiling(transformRect.Width), + NMath.Ceiling(transformRect.Height)); + } + + [Export("drawLayer:inContext:")] + void Draw(CALayer _, CGContext context) + { + if (ctLine == null) + return; + + CocoaTextManager.ConfigureContext(context); + + context.TextPosition = new CGPoint(0, -lineAscent); + context.ScaleCTM(1, -1); + + ctLine.Draw(context); + } + } +} diff --git a/Src/VimMac/RelativeLineNumbers/CocoaTextManager.cs b/Src/VimMac/RelativeLineNumbers/CocoaTextManager.cs new file mode 100644 index 0000000000..7722034f26 --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/CocoaTextManager.cs @@ -0,0 +1,64 @@ +using System; + +using CoreGraphics; +using Foundation; + +namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers +{ + static class CocoaTextManager + { + static readonly NSString AppleFontSmoothing = new NSString(nameof(AppleFontSmoothing)); + static readonly IDisposable smoothingObserver; + static int smoothing; + + public static event EventHandler DefaultsChanged; + + static CocoaTextManager() + { + UpdateSmoothing(NSUserDefaults.StandardUserDefaults.ValueForKey(AppleFontSmoothing)); + smoothingObserver = NSUserDefaults.StandardUserDefaults.AddObserver( + AppleFontSmoothing, + NSKeyValueObservingOptions.New, + change => UpdateSmoothing(change?.NewValue)); + } + + static void UpdateSmoothing(NSObject value) + { + var newSmoothing = value is NSNumber number + ? number.Int32Value + : -1; + + if (newSmoothing == smoothing) + return; + + smoothing = newSmoothing; + + DefaultsChanged?.Invoke(null, EventArgs.Empty); + } + + public static void ConfigureContext(CGContext context) + { + if (context == null) + return; + + context.SetShouldAntialias(true); + context.SetShouldSubpixelPositionFonts(true); + + if (MacRuntimeEnvironment.MojaveOrNewer) + { + context.SetAllowsFontSmoothing(smoothing < 0); + } + else + { + // NOTE: we cannot do proper subpixel AA because the layer/context + // needs a background color which is not available in the text layer. + // Selections and highlights are separate layers in the editor. If + // we had reliable background colors, we could enable subpixel AA + // "smoothing" by setting this to true and ensuring the context + // had a background color (by way of the target layer or calling + // the private CGContextSetFontSmoothingBackgroundColor. + context.SetShouldSmoothFonts(false); + } + } + } +} diff --git a/Src/VimMac/RelativeLineNumbers/Line.cs b/Src/VimMac/RelativeLineNumbers/Line.cs new file mode 100644 index 0000000000..85d5dccec6 --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/Line.cs @@ -0,0 +1,27 @@ +using System.Diagnostics; + +namespace Vim.UI.Wpf.Implementation.RelativeLineNumbers +{ + [DebuggerDisplay("{LineNumber} {DisplayNumber} {Baseline}")] + internal readonly struct Line + { + public int LineNumber { get; } + + public int DisplayNumber { get; } + + public double Baseline { get; } + + public double TextTop { get; } + + public bool IsCaretLine { get; } + + public Line(int lineNumber, int displayNumber, double verticalBaseline, double textTop, bool isCaretLine) + { + LineNumber = lineNumber; + DisplayNumber = displayNumber; + Baseline = verticalBaseline; + TextTop = textTop; + IsCaretLine = isCaretLine; + } + } +} \ No newline at end of file diff --git a/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs b/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs new file mode 100644 index 0000000000..635b28394f --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Formatting; +using Vim.UI.Wpf.Implementation.RelativeLineNumbers.Util; + +namespace Vim.UI.Wpf.Implementation.RelativeLineNumbers +{ + internal sealed class LineNumbersCalculator + { + private static readonly ICollection s_empty = new ITextViewLine[0]; + + private readonly ICocoaTextView _textView; + private readonly IVimLocalSettings _localSettings; + + internal LineNumbersCalculator(ICocoaTextView textView, IVimLocalSettings localSettings) + { + _textView = textView + ?? throw new ArgumentNullException(nameof(textView)); + + _localSettings = localSettings + ?? throw new ArgumentNullException(nameof(localSettings)); + } + + public ICollection CalculateLineNumbers() + { + bool hasValidCaret = TryGetCaretIndex(out int caretIndex); + + var result = GetLinesWithNumbers() + .Select((line, idx) => + { + var distanceToCaret = Math.Abs(idx - caretIndex); + return MakeLine(line, distanceToCaret, hasValidCaret); + }) + .ToList(); + + return result; + } + + private ICollection TextViewLines + { + get + { + if (!_textView.IsClosed && !_textView.InLayout) + { + var textViewLines = _textView.TextViewLines; + if (textViewLines != null && textViewLines.IsValid) + { + return textViewLines; + } + } + return s_empty; + } + } + + private IEnumerable GetLinesWithNumbers() + { + return TextViewLines.Where(x => x.IsValid && x.IsFirstTextViewLineForSnapshotLine); + } + + private bool TryGetCaretIndex(out int caretIndex) + { + var caretLine = _textView.Caret.Position.BufferPosition.GetContainingLine(); + + var firstVisibleLine = + TextViewLines.FirstOrDefault(x => x.IsValid && x.IsFirstTextViewLineForSnapshotLine); + + if (firstVisibleLine != null && + TryGetVisualLineNumber(caretLine.Start, out int caretVisualLineNumber) && + TryGetVisualLineNumber(firstVisibleLine.Start, out int referenceVisualLineNumber)) + { + caretIndex = caretVisualLineNumber - referenceVisualLineNumber; + return true; + } + + caretIndex = -1; + return false; + } + + private bool TryGetVisualLineNumber(SnapshotPoint point, out int visualLineNumber) + { + var visualSnapshot = _textView.VisualSnapshot; + + var position = _textView.BufferGraph.MapUpToSnapshot( + point, + PointTrackingMode.Negative, + PositionAffinity.Successor, + visualSnapshot)?.Position; + + visualLineNumber = position.HasValue + ? visualSnapshot.GetLineNumberFromPosition(position.Value) + : 0; + + return position.HasValue; + } + + private Line MakeLine(ITextViewLine wpfLine, int distanceToCaret, bool hasValidCaret) + { + int numberToDisplay = GetNumberToDisplay(wpfLine, distanceToCaret, hasValidCaret); + + //double verticalBaseline = wpfLine.TextTop - _textView.ViewportTop + wpfLine.Baseline; + + bool isCaretLine = hasValidCaret && distanceToCaret == 0; + + bool caretLineStyle = isCaretLine && _localSettings.RelativeNumber; + return new Line(wpfLine.GetLineNumber(), numberToDisplay, wpfLine.TextTop, wpfLine.Baseline, caretLineStyle); + } + + private int GetNumberToDisplay(ITextViewLine wpfLine, int distanceToCaret, bool hasValidCaret) + { + //// Detect the phantom line. + //if (wpfLine.Start.Position == wpfLine.End.Position && + // wpfLine.Start.Position == wpfLine.Snapshot.Length) + //{ + // return -1; + //} + + var absoluteCaretLineNumber = + + /*_localSettings.Number &&*/ hasValidCaret && distanceToCaret == 0; + + var absoluteLineNumbers = + !hasValidCaret || !_localSettings.RelativeNumber; + + if (absoluteCaretLineNumber || absoluteLineNumbers) + { + return wpfLine.GetLineNumber(); + } + + return distanceToCaret; + } + } +} diff --git a/Src/VimMac/RelativeLineNumbers/MacRuntimeEnvironment.cs b/Src/VimMac/RelativeLineNumbers/MacRuntimeEnvironment.cs new file mode 100644 index 0000000000..efa6ccda06 --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/MacRuntimeEnvironment.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Foundation +{ + public static class MacRuntimeEnvironment + { + static readonly NSOperatingSystemVersion mojave = new NSOperatingSystemVersion(10, 14, 0); + + public static bool MojaveOrNewer { get; } = NSProcessInfo.ProcessInfo.IsOperatingSystemAtLeastVersion(mojave); + } +} diff --git a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs new file mode 100644 index 0000000000..c6833eaae2 --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs @@ -0,0 +1,456 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Windows; +using AppKit; +using CoreAnimation; +using CoreGraphics; +using CoreText; +using Foundation; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.Implementation; +using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; +using Microsoft.VisualStudio.Text.Formatting; +using Vim.UI.Wpf.Implementation.RelativeLineNumbers; +using Vim.UI.Wpf.Implementation.RelativeLineNumbers.Util; + +namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers +{ + internal sealed class RelativeLineNumbersMargin : NSView, ICocoaTextViewMargin + { + // Large enough that we shouldn't expect the view to reallocate the array. + private const int ExpectedNumberOfVisibleLines = 100; + + #region Private Members + + readonly List recyclableVisuals + = new List(ExpectedNumberOfVisibleLines); + + readonly Queue unusedChildIndicies = new Queue(ExpectedNumberOfVisibleLines); + + ICocoaTextView _textView; + private readonly ICocoaTextViewMargin _marginContainer; + ICocoaClassificationFormatMap _classificationFormatMap; + IClassificationTypeRegistryService _classificationTypeRegistry; + internal NSStringAttributes _formatting; + + int _visibleDigits = 5; + + internal bool _updateNeeded = false; + bool _isDisposed = false; + + NSView _translatedCanvas; + + double _oldViewportTop; + LineNumbersCalculator _lineNumbersCalculator; + int lastLineNumber; + + #endregion // Private Members + + /// + /// Creates a default Line Number Provider for a Text Editor + /// + /// + /// The Text Editor with which this line number provider is associated + /// + /// Used for getting/setting the format of the line number classification + /// Used for retrieving the "line number" classification + public RelativeLineNumbersMargin( + ICocoaTextView textView, + ICocoaTextViewMargin marginContainer, + ICocoaClassificationFormatMap classificationFormatMap, + IClassificationTypeRegistryService classificationTypeRegistry, + IVimLocalSettings vimLocalSettings) + { + _textView = textView; + _marginContainer = marginContainer; + _classificationFormatMap = classificationFormatMap; + _classificationTypeRegistry = classificationTypeRegistry; + _translatedCanvas = this; + _oldViewportTop = 0.0; + + _lineNumbersCalculator = new LineNumbersCalculator(textView, vimLocalSettings); + WantsLayer = true; + Hidden = false; + SetVisualStudioMarginVisibility(hidden: true); + } + + public override bool IsFlipped => true; + + public override bool Hidden + { + get => base.Hidden; + set + { + base.Hidden = value; + + if (!value) + { + // Sign up for layout changes on the Text Editor + _textView.LayoutChanged += OnEditorLayoutChanged; + + // Sign up for classification format change events + _classificationFormatMap.ClassificationFormatMappingChanged += OnClassificationFormatChanged; + + _textView.ZoomLevelChanged += _textView_ZoomLevelChanged; + _textView.Caret.PositionChanged += Caret_PositionChanged; + //Fonts might have changed while we were hidden. + this.SetFontFromClassification(); + } + else + { + // Unregister from layout changes on the Text Editor + _textView.LayoutChanged -= OnEditorLayoutChanged; + + // Unregister from classification format change events + _classificationFormatMap.ClassificationFormatMappingChanged -= OnClassificationFormatChanged; + } + } + } + + private void SetVisualStudioMarginVisibility(bool hidden) + { + var visualStudioMargin = + _marginContainer.GetTextViewMargin(PredefinedMarginNames.LineNumber); + + if (visualStudioMargin is ICocoaTextViewMargin lineNumberMargin) + { + var element = lineNumberMargin.VisualElement; + element.Hidden = hidden; + element.RemoveFromSuperview(); + //if(hidden) + //{ + // element.fr + //} + //if (element.Hidden != Hidden) + //{ + // if (!element.Hidden) + // { + // _width = element.wi.Width; + // _minWidth = element.MinWidth; + // _maxWidth = element.MaxWidth; + // element.Width = 0.0; + // element.MinWidth = 0.0; + // element.MaxWidth = 0.0; + // } + // else + // { + // element.Width = _width; + // element.MinWidth = _minWidth; + // element.MaxWidth = _maxWidth; + // } + // element.Visibility = visibility; + // element.UpdateLayout(); + //} + } + } + + private void Caret_PositionChanged(object sender, CaretPositionChangedEventArgs e) + { + var lineNumber = e.TextView.Caret.ContainingTextViewLine.GetLineNumber(); + if (lineNumber != lastLineNumber) + { + lastLineNumber = lineNumber; + _updateNeeded = true; + UpdateLineNumbers(); + } + } + + private void _textView_ZoomLevelChanged(object sender, ZoomLevelChangedEventArgs e) + { + _updateNeeded = true; + UpdateLineNumbers(); + } + + #region Private Helpers + + private void ThrowIfDisposed() + { + if (_isDisposed) + throw new ObjectDisposedException(PredefinedMarginNames.LineNumber); + } + + private void SetFontFromClassification() + { + IClassificationType lineNumberClassificaiton = _classificationTypeRegistry.GetClassificationType("line number"); + + var font = _classificationFormatMap.GetTextProperties(lineNumberClassificaiton); + + _formatting = font; + + this.DetermineMarginWidth(); + Layer.BackgroundColor = (font.BackgroundColor ?? NSColor.Clear).CGColor; + // Reformat all the lines + ClearLineNumbers(); + this.UpdateLineNumbers(); + } + + private void ClearLineNumbers(bool disposing = false) + { + recyclableVisuals.Clear(); + + if (!disposing) + { + foreach (var sublayer in _translatedCanvas.Layer.Sublayers ?? Array.Empty()) + sublayer.RemoveFromSuperLayer(); + } + } + + /// + /// Determine the width of the margin, using the number of visible digits (e.g. 5) to construct + /// a model string (e.g. "88888") + /// + private void DetermineMarginWidth() + { + // Our width should follow the following rules: + // 1) No smaller than wide enough to fit 5 digits + // 2) Increase in size whenever larger numbers are encountered + // 3) _Never_ decrease in size (e.g. if resized to fit 7 digits, + // will not shrink when scrolled up to 3 digit numbers) + + using (var textLine = new CTLine(new NSAttributedString(new string('8', _visibleDigits), _formatting))) + { + intrinsicContentSize = new CGSize(textLine.GetBounds(0).Width, NoIntrinsicMetric); + InvalidateIntrinsicContentSize(); + } + } + + CGSize intrinsicContentSize; + private NSTrackingArea _trackingArea; + + public override CGSize IntrinsicContentSize => intrinsicContentSize; + + /// + /// Resize the width of the margin if the number of digits in the last (largest) visible number + /// is larger than we can currently handle. + /// + /// The last (largest) visible number in the view + internal void ResizeIfNecessary(int lastVisibleLineNumber) + { + // We are looking at lines base 1, not base 0 + lastVisibleLineNumber++; + + if (lastVisibleLineNumber <= 0) + lastVisibleLineNumber = 1; + + int numDigits = (int)Math.Log10(lastVisibleLineNumber) + 1; + + if (numDigits > _visibleDigits) + { + _visibleDigits = numDigits; + this.DetermineMarginWidth(); + + // Clear existing children so they are all regenerated in + // UpdateLineNumbers + ClearLineNumbers(); + } + } + + (Dictionary lines, int maxLineNumber) CalculateLineNumbers() + { + //TODO: return dictionary here + var lines = _lineNumbersCalculator.CalculateLineNumbers(); + var dict = new Dictionary(); + Line lastLine; + foreach(var line in lines) + { + dict.Add(line.LineNumber, line); + } + return (dict, 100 /* TODO */); + } + + internal void UpdateLineNumbers() + { + _updateNeeded = false; + + if (_textView.IsClosed) + { + return; + } + + // If the text view is in the middle of performing a layout, don't proceed. Otherwise an exception will be thrown when trying to access TextViewLines. + // If we are in layout, then a LayoutChangedEvent is expected to be raised which will in turn cause this method to be invoked. + if (_textView.InLayout) + { + return; + } + + var (lines, maxLineNumber) = CalculateLineNumbers(); + + // If there are no line numbers to display, quit + if (lines == null || lines.Count == 0) + { + return; + } + + // Check to see if we need to resize the margin width + this.ResizeIfNecessary(maxLineNumber); + + var layer = _translatedCanvas.Layer; + unusedChildIndicies.Clear(); + + // Update the existing visuals. + for (int iChild = 0, nChildren = recyclableVisuals.Count; iChild < nChildren; iChild++) + { + var child = recyclableVisuals[iChild]; + var lineNumber = child.LineNumber; + + if (lines.TryGetValue(lineNumber, out var line)) + { + lines.Remove(lineNumber); + UpdateVisual(child, line); + } + else + { + child.InUse = false; + unusedChildIndicies.Enqueue(iChild); + } + } + + // For any leftover lines, repurpose any existing visuals or create new ones. + foreach (var line in lines.Values) + { + CocoaLineNumberMarginDrawingVisual visual; + + if (unusedChildIndicies.Count > 0) + { + var childIndex = unusedChildIndicies.Dequeue(); + visual = recyclableVisuals[childIndex]; + Debug.Assert(!visual.InUse); + } + else + { + visual = new CocoaLineNumberMarginDrawingVisual(); + recyclableVisuals.Add(visual); + layer.AddSublayer(visual); + visual.ContentsScale = layer.ContentsScale; + } + + UpdateVisual(visual, line); + } + + if (_oldViewportTop != _textView.ViewportTop) + { + _translatedCanvas.Bounds = new CGRect(_translatedCanvas.Bounds.X, _textView.ViewportTop, _translatedCanvas.Bounds.Width, _translatedCanvas.Bounds.Height); + _oldViewportTop = _textView.ViewportTop; + } + + void UpdateVisual(CocoaLineNumberMarginDrawingVisual visual, Line line) + { + visual.InUse = true; + visual.Update( + _formatting, + line, + intrinsicContentSize.Width); + } + } + + //NSAttributedString MakeTextLine(int lineNumber) + //{ + // string numberAsString = lineNumber.ToString(CultureInfo.CurrentUICulture.NumberFormat); + + // return new NSAttributedString(numberAsString, _formatting); + //} + + #endregion + + #region Event Handlers + + internal void OnEditorLayoutChanged(object sender, EventArgs e) + { + if (!_updateNeeded) + { + _updateNeeded = true; + UpdateLineNumbers(); + } + } + + void OnClassificationFormatChanged(object sender, EventArgs e) + { + this.SetFontFromClassification(); + } + + #endregion // Event Handlers + + #region ICocoaTextViewMargin Members + + public NSView VisualElement + { + get + { + ThrowIfDisposed(); + return this; + } + } + + #endregion + + #region ITextViewMargin Members + + public double MarginSize + { + get + { + ThrowIfDisposed(); + return 10; + } + } + + public bool Enabled + { + get + { + ThrowIfDisposed(); + return _textView.Options.IsLineNumberMarginEnabled(); + } + } + //#region ICocoaTextViewMargin Implementation + + //NSView ICocoaTextViewMargin.VisualElement => this; + + //double ITextViewMargin.MarginSize => throw new NotImplementedException(); + + //bool ITextViewMargin.Enabled => !Hidden; + + //ITextViewMargin ITextViewMargin.GetTextViewMargin(string marginName) + // => string.Compare(marginName, CocoaInfoBarMarginProvider.MarginName, StringComparison.OrdinalIgnoreCase) == 0 ? this : null; + + //#endregion + public override void UpdateTrackingAreas() + { + if (_trackingArea != null) + { + RemoveTrackingArea(_trackingArea); + _trackingArea.Dispose(); + } + _trackingArea = new NSTrackingArea(Bounds, + NSTrackingAreaOptions.MouseMoved | + NSTrackingAreaOptions.ActiveInKeyWindow | + NSTrackingAreaOptions.MouseEnteredAndExited, this, null); + this.AddTrackingArea(_trackingArea); + } + + public ITextViewMargin GetTextViewMargin(string marginName) + { + return string.Compare(marginName, RelativeLineNumbersMarginFactory.LineNumbersMarginName, StringComparison.OrdinalIgnoreCase) == 0 ? this : (ITextViewMargin)null; + } + + protected override void Dispose(bool disposing) + { + if (!_isDisposed && disposing) + { + RemoveFromSuperview(); + ClearLineNumbers(true); + } + + _isDisposed = true; + + base.Dispose(disposing); + } + + #endregion + } +} diff --git a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMarginFactory.cs b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMarginFactory.cs new file mode 100644 index 0000000000..e634fd2c72 --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMarginFactory.cs @@ -0,0 +1,59 @@ +using System; +using System.ComponentModel.Composition; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; + +namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers +{ + [Name(RelativeLineNumbersMarginFactory.LineNumbersMarginName)] + [Export(typeof(ICocoaTextViewMarginProvider))] + [Order(Before = PredefinedMarginNames.Spacer)] + [MarginContainer(PredefinedMarginNames.LeftSelection)] + [ContentType("text")] + [TextViewRole(PredefinedTextViewRoles.Document)] + internal sealed class RelativeLineNumbersMarginFactory : ICocoaTextViewMarginProvider + { + private readonly ICocoaClassificationFormatMapService _formatMapService; + private readonly IClassificationTypeRegistryService _typeRegistryService; + private readonly IProtectedOperations _protectedOperations; + private readonly IVim _vim; + + public const string LineNumbersMarginName = "vsvim_linenumbers2"; + + [ImportingConstructor] + internal RelativeLineNumbersMarginFactory( + ICocoaClassificationFormatMapService formatMapService, + IClassificationTypeRegistryService typeRegistryService, + IVim vim) + { + _formatMapService = formatMapService + ?? throw new ArgumentNullException(nameof(formatMapService)); + + _typeRegistryService = typeRegistryService + ?? throw new ArgumentNullException(nameof(typeRegistryService)); + + _vim = vim + ?? throw new ArgumentNullException(nameof(vim)); + } + + public ICocoaTextViewMargin CreateMargin( + ICocoaTextViewHost wpfTextViewHost, + ICocoaTextViewMargin marginContainer) + { + var textView = wpfTextViewHost?.TextView + ?? throw new ArgumentNullException(nameof(wpfTextViewHost)); + + var vimBuffer = _vim.GetOrCreateVimBuffer(textView); + + var formatMap = _formatMapService.GetClassificationFormatMap(textView); + + return new RelativeLineNumbersMargin( + textView, + marginContainer, + formatMap, + _typeRegistryService, + vimBuffer.LocalSettings); + } + } +} diff --git a/Src/VimMac/RelativeLineNumbers/Util/TextViewExtensions.cs b/Src/VimMac/RelativeLineNumbers/Util/TextViewExtensions.cs new file mode 100644 index 0000000000..c9759ac5bb --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/Util/TextViewExtensions.cs @@ -0,0 +1,22 @@ +using System; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Vim.UI.Wpf.Implementation.RelativeLineNumbers.Util +{ + internal static class TextViewExtensions + { + public static int GetLineCount(this ITextView textView) + { + textView = textView + ?? throw new ArgumentNullException(nameof(textView)); + + return textView.TextSnapshot.LineCount; + } + + public static ITextSnapshotLine GetLine(this CaretPosition caretPosition) + { + return caretPosition.BufferPosition.GetContainingLine(); + } + } +} diff --git a/Src/VimMac/RelativeLineNumbers/Util/TextViewLineExtensions.cs b/Src/VimMac/RelativeLineNumbers/Util/TextViewLineExtensions.cs new file mode 100644 index 0000000000..50f4c79564 --- /dev/null +++ b/Src/VimMac/RelativeLineNumbers/Util/TextViewLineExtensions.cs @@ -0,0 +1,12 @@ +using Microsoft.VisualStudio.Text.Formatting; + +namespace Vim.UI.Wpf.Implementation.RelativeLineNumbers.Util +{ + internal static class TextViewLineExtensions + { + public static int GetLineNumber(this ITextViewLine line) + { + return line.Snapshot.GetLineNumberFromPosition(line.Start.Position) + 1; + } + } +} diff --git a/Src/VimMac/VimMac.csproj b/Src/VimMac/VimMac.csproj index 27f858fd0a..a4a1b00051 100644 --- a/Src/VimMac/VimMac.csproj +++ b/Src/VimMac/VimMac.csproj @@ -6,6 +6,8 @@ Vim.Mac net472 $(DefineConstants);VS_SPECIFIC_MAC + + @@ -25,6 +27,8 @@ + + From 6ff813b61e7334faa7ebe309b2e1217b4f2bca77 Mon Sep 17 00:00:00 2001 From: nosami Date: Sun, 9 May 2021 20:28:25 +0100 Subject: [PATCH 2/6] Tidy up and allow switching modes at runtime --- .../LineNumbersCalculator.cs | 5 +- .../RelativeLineNumbersMargin.cs | 79 +++++++------------ Src/VimMac/VimMac.csproj | 2 - 3 files changed, 30 insertions(+), 56 deletions(-) diff --git a/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs b/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs index 635b28394f..c0a25639a2 100644 --- a/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs +++ b/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs @@ -100,8 +100,6 @@ private Line MakeLine(ITextViewLine wpfLine, int distanceToCaret, bool hasValidC { int numberToDisplay = GetNumberToDisplay(wpfLine, distanceToCaret, hasValidCaret); - //double verticalBaseline = wpfLine.TextTop - _textView.ViewportTop + wpfLine.Baseline; - bool isCaretLine = hasValidCaret && distanceToCaret == 0; bool caretLineStyle = isCaretLine && _localSettings.RelativeNumber; @@ -118,8 +116,7 @@ private int GetNumberToDisplay(ITextViewLine wpfLine, int distanceToCaret, bool //} var absoluteCaretLineNumber = - - /*_localSettings.Number &&*/ hasValidCaret && distanceToCaret == 0; + _localSettings.Number && hasValidCaret && distanceToCaret == 0; var absoluteLineNumbers = !hasValidCaret || !_localSettings.RelativeNumber; diff --git a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs index c6833eaae2..367915dcd4 100644 --- a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs +++ b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs @@ -22,7 +22,7 @@ namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers internal sealed class RelativeLineNumbersMargin : NSView, ICocoaTextViewMargin { // Large enough that we shouldn't expect the view to reallocate the array. - private const int ExpectedNumberOfVisibleLines = 100; + const int ExpectedNumberOfVisibleLines = 100; #region Private Members @@ -32,7 +32,7 @@ internal sealed class RelativeLineNumbersMargin : NSView, ICocoaTextViewMargin readonly Queue unusedChildIndicies = new Queue(ExpectedNumberOfVisibleLines); ICocoaTextView _textView; - private readonly ICocoaTextViewMargin _marginContainer; + readonly ICocoaTextViewMargin _marginContainer; ICocoaClassificationFormatMap _classificationFormatMap; IClassificationTypeRegistryService _classificationTypeRegistry; internal NSStringAttributes _formatting; @@ -46,6 +46,7 @@ internal sealed class RelativeLineNumbersMargin : NSView, ICocoaTextViewMargin double _oldViewportTop; LineNumbersCalculator _lineNumbersCalculator; + IVimLocalSettings _localSettings; int lastLineNumber; #endregion // Private Members @@ -73,6 +74,7 @@ internal sealed class RelativeLineNumbersMargin : NSView, ICocoaTextViewMargin _oldViewportTop = 0.0; _lineNumbersCalculator = new LineNumbersCalculator(textView, vimLocalSettings); + _localSettings = vimLocalSettings; WantsLayer = true; Hidden = false; SetVisualStudioMarginVisibility(hidden: true); @@ -104,7 +106,8 @@ public override bool Hidden { // Unregister from layout changes on the Text Editor _textView.LayoutChanged -= OnEditorLayoutChanged; - + _textView.ZoomLevelChanged -= _textView_ZoomLevelChanged; + _textView.Caret.PositionChanged -= Caret_PositionChanged; // Unregister from classification format change events _classificationFormatMap.ClassificationFormatMappingChanged -= OnClassificationFormatChanged; } @@ -121,30 +124,6 @@ private void SetVisualStudioMarginVisibility(bool hidden) var element = lineNumberMargin.VisualElement; element.Hidden = hidden; element.RemoveFromSuperview(); - //if(hidden) - //{ - // element.fr - //} - //if (element.Hidden != Hidden) - //{ - // if (!element.Hidden) - // { - // _width = element.wi.Width; - // _minWidth = element.MinWidth; - // _maxWidth = element.MaxWidth; - // element.Width = 0.0; - // element.MinWidth = 0.0; - // element.MaxWidth = 0.0; - // } - // else - // { - // element.Width = _width; - // element.MinWidth = _minWidth; - // element.MaxWidth = _maxWidth; - // } - // element.Visibility = visibility; - // element.UpdateLayout(); - //} } } @@ -228,7 +207,7 @@ private void DetermineMarginWidth() /// is larger than we can currently handle. /// /// The last (largest) visible number in the view - internal void ResizeIfNecessary(int lastVisibleLineNumber) + void ResizeIfNecessary(int lastVisibleLineNumber) { // We are looking at lines base 1, not base 0 lastVisibleLineNumber++; @@ -262,10 +241,28 @@ internal void ResizeIfNecessary(int lastVisibleLineNumber) return (dict, 100 /* TODO */); } - internal void UpdateLineNumbers() + void UpdateLineNumbers() { _updateNeeded = false; + if (!Enabled) + { + if (recyclableVisuals.Count > 0) + { + intrinsicContentSize = new CGSize(0, NoIntrinsicMetric); + InvalidateIntrinsicContentSize(); + ClearLineNumbers(); + } + return; + } + else + { + if (intrinsicContentSize.Width == 0) + { + DetermineMarginWidth(); + } + } + if (_textView.IsClosed) { return; @@ -348,13 +345,6 @@ void UpdateVisual(CocoaLineNumberMarginDrawingVisual visual, Line line) } } - //NSAttributedString MakeTextLine(int lineNumber) - //{ - // string numberAsString = lineNumber.ToString(CultureInfo.CurrentUICulture.NumberFormat); - - // return new NSAttributedString(numberAsString, _formatting); - //} - #endregion #region Event Handlers @@ -404,21 +394,12 @@ public bool Enabled get { ThrowIfDisposed(); - return _textView.Options.IsLineNumberMarginEnabled(); + return _localSettings.Number || _localSettings.RelativeNumber; + } } - //#region ICocoaTextViewMargin Implementation - - //NSView ICocoaTextViewMargin.VisualElement => this; - - //double ITextViewMargin.MarginSize => throw new NotImplementedException(); - - //bool ITextViewMargin.Enabled => !Hidden; - - //ITextViewMargin ITextViewMargin.GetTextViewMargin(string marginName) - // => string.Compare(marginName, CocoaInfoBarMarginProvider.MarginName, StringComparison.OrdinalIgnoreCase) == 0 ? this : null; + #endregion - //#endregion public override void UpdateTrackingAreas() { if (_trackingArea != null) @@ -450,7 +431,5 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - - #endregion } } diff --git a/Src/VimMac/VimMac.csproj b/Src/VimMac/VimMac.csproj index a4a1b00051..4bf41f5285 100644 --- a/Src/VimMac/VimMac.csproj +++ b/Src/VimMac/VimMac.csproj @@ -6,8 +6,6 @@ Vim.Mac net472 $(DefineConstants);VS_SPECIFIC_MAC - - From 6268fcdbd04403ba5197de7007eceef437bc8f33 Mon Sep 17 00:00:00 2001 From: nosami Date: Mon, 10 May 2021 14:32:46 +0100 Subject: [PATCH 3/6] clean up --- .../LineNumbersCalculator.cs | 21 +++++++------------ .../RelativeLineNumbersMargin.cs | 15 +------------ 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs b/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs index c0a25639a2..9819f9daa5 100644 --- a/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs +++ b/Src/VimMac/RelativeLineNumbers/LineNumbersCalculator.cs @@ -24,19 +24,21 @@ internal LineNumbersCalculator(ICocoaTextView textView, IVimLocalSettings localS ?? throw new ArgumentNullException(nameof(localSettings)); } - public ICollection CalculateLineNumbers() + public (IDictionary, int) CalculateLineNumbers() { bool hasValidCaret = TryGetCaretIndex(out int caretIndex); - + int maxLineNumber = 0; var result = GetLinesWithNumbers() - .Select((line, idx) => + .Select((editorLine, idx) => { var distanceToCaret = Math.Abs(idx - caretIndex); - return MakeLine(line, distanceToCaret, hasValidCaret); + var line = MakeLine(editorLine, distanceToCaret, hasValidCaret); + maxLineNumber = Math.Max(maxLineNumber, line.LineNumber); + return line; }) - .ToList(); + .ToDictionary(line => line.LineNumber, line => line); - return result; + return (result, maxLineNumber); } private ICollection TextViewLines @@ -108,13 +110,6 @@ private Line MakeLine(ITextViewLine wpfLine, int distanceToCaret, bool hasValidC private int GetNumberToDisplay(ITextViewLine wpfLine, int distanceToCaret, bool hasValidCaret) { - //// Detect the phantom line. - //if (wpfLine.Start.Position == wpfLine.End.Position && - // wpfLine.Start.Position == wpfLine.Snapshot.Length) - //{ - // return -1; - //} - var absoluteCaretLineNumber = _localSettings.Number && hasValidCaret && distanceToCaret == 0; diff --git a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs index 367915dcd4..0b63d5c98e 100644 --- a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs +++ b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs @@ -228,19 +228,6 @@ void ResizeIfNecessary(int lastVisibleLineNumber) } } - (Dictionary lines, int maxLineNumber) CalculateLineNumbers() - { - //TODO: return dictionary here - var lines = _lineNumbersCalculator.CalculateLineNumbers(); - var dict = new Dictionary(); - Line lastLine; - foreach(var line in lines) - { - dict.Add(line.LineNumber, line); - } - return (dict, 100 /* TODO */); - } - void UpdateLineNumbers() { _updateNeeded = false; @@ -275,7 +262,7 @@ void UpdateLineNumbers() return; } - var (lines, maxLineNumber) = CalculateLineNumbers(); + var (lines, maxLineNumber) = _lineNumbersCalculator.CalculateLineNumbers(); // If there are no line numbers to display, quit if (lines == null || lines.Count == 0) From 946e8706e8d687c9c1bb23bc1c6aacb2c813d187 Mon Sep 17 00:00:00 2001 From: nosami Date: Thu, 3 Jun 2021 21:54:10 +0100 Subject: [PATCH 4/6] Add explicit acccesibility modifiers for consistency --- Src/VimMac/RelativeLineNumbers/CocoaExtensions.cs | 4 ++-- .../CocoaLineNumberMarginDrawingVisual.cs | 10 +++++----- Src/VimMac/RelativeLineNumbers/CocoaTextManager.cs | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Src/VimMac/RelativeLineNumbers/CocoaExtensions.cs b/Src/VimMac/RelativeLineNumbers/CocoaExtensions.cs index bc1b169e3b..8368e6e648 100644 --- a/Src/VimMac/RelativeLineNumbers/CocoaExtensions.cs +++ b/Src/VimMac/RelativeLineNumbers/CocoaExtensions.cs @@ -97,7 +97,7 @@ public static CGPoint PointToScreen(this NSView view, CGPoint point) return p; } - static readonly NSDictionary disableAnimations = new NSDictionary( + private static readonly NSDictionary disableAnimations = new NSDictionary( "actions", NSNull.Null, "contents", NSNull.Null, "hidden", NSNull.Null, @@ -116,7 +116,7 @@ public static void DisableImplicitAnimations(this CALayer layer) namespace CoreGraphics { - static class CoreGraphicsExtensions + internal static class CoreGraphicsExtensions { public static CGSize InflateToIntegral(this CGSize size) => new CGSize(NMath.Ceiling(size.Width), NMath.Ceiling(size.Height)); diff --git a/Src/VimMac/RelativeLineNumbers/CocoaLineNumberMarginDrawingVisual.cs b/Src/VimMac/RelativeLineNumbers/CocoaLineNumberMarginDrawingVisual.cs index 979c3ebaf5..4daf52b319 100644 --- a/Src/VimMac/RelativeLineNumbers/CocoaLineNumberMarginDrawingVisual.cs +++ b/Src/VimMac/RelativeLineNumbers/CocoaLineNumberMarginDrawingVisual.cs @@ -12,10 +12,10 @@ namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers { internal sealed class CocoaLineNumberMarginDrawingVisual : CALayer, ICALayerDelegate { - NSStringAttributes stringAttributes; - CGRect lineBounds; - nfloat lineAscent; - CTLine ctLine; + private NSStringAttributes stringAttributes; + private CGRect lineBounds; + private nfloat lineAscent; + private CTLine ctLine; public int LineNumber { get; private set; } public int DisplayNumber { get; private set; } @@ -78,7 +78,7 @@ public CocoaLineNumberMarginDrawingVisual() } [Export("drawLayer:inContext:")] - void Draw(CALayer _, CGContext context) + private void Draw(CALayer _, CGContext context) { if (ctLine == null) return; diff --git a/Src/VimMac/RelativeLineNumbers/CocoaTextManager.cs b/Src/VimMac/RelativeLineNumbers/CocoaTextManager.cs index 7722034f26..a4a51db1ac 100644 --- a/Src/VimMac/RelativeLineNumbers/CocoaTextManager.cs +++ b/Src/VimMac/RelativeLineNumbers/CocoaTextManager.cs @@ -5,11 +5,11 @@ namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers { - static class CocoaTextManager + internal static class CocoaTextManager { - static readonly NSString AppleFontSmoothing = new NSString(nameof(AppleFontSmoothing)); - static readonly IDisposable smoothingObserver; - static int smoothing; + private static readonly NSString AppleFontSmoothing = new NSString(nameof(AppleFontSmoothing)); + private static readonly IDisposable smoothingObserver; + private static int smoothing; public static event EventHandler DefaultsChanged; @@ -22,7 +22,7 @@ static CocoaTextManager() change => UpdateSmoothing(change?.NewValue)); } - static void UpdateSmoothing(NSObject value) + private static void UpdateSmoothing(NSObject value) { var newSmoothing = value is NSNumber number ? number.Int32Value From 71cf05dfe324a963f6eb55ecb0dcb3ec462ebaef Mon Sep 17 00:00:00 2001 From: nosami Date: Sun, 6 Jun 2021 14:48:11 +0100 Subject: [PATCH 5/6] Don't disable VS line number margin automatically Also, adds accessibility modifiers and removes some dead code. --- .../RelativeLineNumbersMargin.cs | 57 ++++++------------- .../RelativeLineNumbersMarginFactory.cs | 6 +- 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs index 0b63d5c98e..1e475953a3 100644 --- a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs +++ b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMargin.cs @@ -1,19 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; -using System.Windows; using AppKit; using CoreAnimation; using CoreGraphics; using CoreText; using Foundation; -using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Editor.Implementation; -using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; -using Microsoft.VisualStudio.Text.Formatting; using Vim.UI.Wpf.Implementation.RelativeLineNumbers; using Vim.UI.Wpf.Implementation.RelativeLineNumbers.Util; @@ -22,32 +16,31 @@ namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers internal sealed class RelativeLineNumbersMargin : NSView, ICocoaTextViewMargin { // Large enough that we shouldn't expect the view to reallocate the array. - const int ExpectedNumberOfVisibleLines = 100; + private const int ExpectedNumberOfVisibleLines = 100; #region Private Members - readonly List recyclableVisuals + private readonly List recyclableVisuals = new List(ExpectedNumberOfVisibleLines); - readonly Queue unusedChildIndicies = new Queue(ExpectedNumberOfVisibleLines); + private readonly Queue unusedChildIndicies = new Queue(ExpectedNumberOfVisibleLines); - ICocoaTextView _textView; - readonly ICocoaTextViewMargin _marginContainer; - ICocoaClassificationFormatMap _classificationFormatMap; - IClassificationTypeRegistryService _classificationTypeRegistry; + private ICocoaTextView _textView; + private ICocoaClassificationFormatMap _classificationFormatMap; + private IClassificationTypeRegistryService _classificationTypeRegistry; internal NSStringAttributes _formatting; - int _visibleDigits = 5; + private int _visibleDigits = 5; internal bool _updateNeeded = false; - bool _isDisposed = false; + private bool _isDisposed = false; - NSView _translatedCanvas; + private NSView _translatedCanvas; - double _oldViewportTop; - LineNumbersCalculator _lineNumbersCalculator; - IVimLocalSettings _localSettings; - int lastLineNumber; + private double _oldViewportTop; + private LineNumbersCalculator _lineNumbersCalculator; + private IVimLocalSettings _localSettings; + private int lastLineNumber; #endregion // Private Members @@ -61,13 +54,11 @@ internal sealed class RelativeLineNumbersMargin : NSView, ICocoaTextViewMargin /// Used for retrieving the "line number" classification public RelativeLineNumbersMargin( ICocoaTextView textView, - ICocoaTextViewMargin marginContainer, ICocoaClassificationFormatMap classificationFormatMap, IClassificationTypeRegistryService classificationTypeRegistry, IVimLocalSettings vimLocalSettings) { _textView = textView; - _marginContainer = marginContainer; _classificationFormatMap = classificationFormatMap; _classificationTypeRegistry = classificationTypeRegistry; _translatedCanvas = this; @@ -77,7 +68,6 @@ internal sealed class RelativeLineNumbersMargin : NSView, ICocoaTextViewMargin _localSettings = vimLocalSettings; WantsLayer = true; Hidden = false; - SetVisualStudioMarginVisibility(hidden: true); } public override bool IsFlipped => true; @@ -114,19 +104,6 @@ public override bool Hidden } } - private void SetVisualStudioMarginVisibility(bool hidden) - { - var visualStudioMargin = - _marginContainer.GetTextViewMargin(PredefinedMarginNames.LineNumber); - - if (visualStudioMargin is ICocoaTextViewMargin lineNumberMargin) - { - var element = lineNumberMargin.VisualElement; - element.Hidden = hidden; - element.RemoveFromSuperview(); - } - } - private void Caret_PositionChanged(object sender, CaretPositionChangedEventArgs e) { var lineNumber = e.TextView.Caret.ContainingTextViewLine.GetLineNumber(); @@ -197,7 +174,7 @@ private void DetermineMarginWidth() } } - CGSize intrinsicContentSize; + private CGSize intrinsicContentSize; private NSTrackingArea _trackingArea; public override CGSize IntrinsicContentSize => intrinsicContentSize; @@ -207,7 +184,7 @@ private void DetermineMarginWidth() /// is larger than we can currently handle. /// /// The last (largest) visible number in the view - void ResizeIfNecessary(int lastVisibleLineNumber) + private void ResizeIfNecessary(int lastVisibleLineNumber) { // We are looking at lines base 1, not base 0 lastVisibleLineNumber++; @@ -228,7 +205,7 @@ void ResizeIfNecessary(int lastVisibleLineNumber) } } - void UpdateLineNumbers() + private void UpdateLineNumbers() { _updateNeeded = false; @@ -345,7 +322,7 @@ internal void OnEditorLayoutChanged(object sender, EventArgs e) } } - void OnClassificationFormatChanged(object sender, EventArgs e) + private void OnClassificationFormatChanged(object sender, EventArgs e) { this.SetFontFromClassification(); } diff --git a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMarginFactory.cs b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMarginFactory.cs index e634fd2c72..2ab20993b1 100644 --- a/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMarginFactory.cs +++ b/Src/VimMac/RelativeLineNumbers/RelativeLineNumbersMarginFactory.cs @@ -8,7 +8,7 @@ namespace Vim.UI.Cocoa.Implementation.RelativeLineNumbers { [Name(RelativeLineNumbersMarginFactory.LineNumbersMarginName)] [Export(typeof(ICocoaTextViewMarginProvider))] - [Order(Before = PredefinedMarginNames.Spacer)] + [Order(Before = PredefinedMarginNames.LineNumber)] [MarginContainer(PredefinedMarginNames.LeftSelection)] [ContentType("text")] [TextViewRole(PredefinedTextViewRoles.Document)] @@ -16,10 +16,9 @@ internal sealed class RelativeLineNumbersMarginFactory : ICocoaTextViewMarginPro { private readonly ICocoaClassificationFormatMapService _formatMapService; private readonly IClassificationTypeRegistryService _typeRegistryService; - private readonly IProtectedOperations _protectedOperations; private readonly IVim _vim; - public const string LineNumbersMarginName = "vsvim_linenumbers2"; + public const string LineNumbersMarginName = "vsvim_linenumbers"; [ImportingConstructor] internal RelativeLineNumbersMarginFactory( @@ -50,7 +49,6 @@ internal sealed class RelativeLineNumbersMarginFactory : ICocoaTextViewMarginPro return new RelativeLineNumbersMargin( textView, - marginContainer, formatMap, _typeRegistryService, vimBuffer.LocalSettings); From 5ef7ca48ce0f5b6555d1260424672fa612bad531 Mon Sep 17 00:00:00 2001 From: nosami Date: Sun, 6 Jun 2021 14:50:38 +0100 Subject: [PATCH 6/6] Bump version to 2.8.0.13 --- Src/VimMac/Properties/AddinInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/VimMac/Properties/AddinInfo.cs b/Src/VimMac/Properties/AddinInfo.cs index 63b4a15eeb..6ed0fc49d0 100644 --- a/Src/VimMac/Properties/AddinInfo.cs +++ b/Src/VimMac/Properties/AddinInfo.cs @@ -5,7 +5,7 @@ [assembly: Addin( "VsVim", Namespace = "Vim.Mac", - Version = "2.8.0.12" + Version = "2.8.0.13" )] [assembly: AddinName("VsVim")]