diff --git a/Src/VimApp/Implementation/SelectedLineNumberFormatDefinition.cs b/Src/VimApp/Implementation/SelectedLineNumberFormatDefinition.cs new file mode 100644 index 0000000000..a8899f8a07 --- /dev/null +++ b/Src/VimApp/Implementation/SelectedLineNumberFormatDefinition.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.Composition; +using System.Windows.Media; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; + +namespace VimApp.Implementation +{ + // VimApp does not have a Selected Line Number FormatDefinition + // so we add it here + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = Name)] + [Name(Name)] + internal class SelectedLineNumberFormatDefinition : ClassificationFormatDefinition + { + internal const string Name = "Selected Line Number"; + + internal SelectedLineNumberFormatDefinition() + { + DisplayName = Name; + ForegroundColor = Colors.Red; + } + } + + internal sealed class SelectedLineNumberClassificationType + { + [Name(SelectedLineNumberFormatDefinition.Name)] + [Export] + internal ClassificationTypeDefinition SelectedLineNumberTypeDefinition { get; set; } + } +} diff --git a/Src/VimWpf/Implementation/RelativeLineNumbers/ILineFormatTracker.cs b/Src/VimWpf/Implementation/RelativeLineNumbers/ILineFormatTracker.cs index 7891af1a4b..bfc138dacb 100644 --- a/Src/VimWpf/Implementation/RelativeLineNumbers/ILineFormatTracker.cs +++ b/Src/VimWpf/Implementation/RelativeLineNumbers/ILineFormatTracker.cs @@ -1,3 +1,4 @@ +using Microsoft.VisualStudio.Text.Editor; using System.Windows.Media; // Disambiguate WPF TextLine with Vim.TextLine @@ -9,7 +10,7 @@ internal interface ILineFormatTracker { Brush Background { get; } - WpfTextLine MakeTextLine(int number); + WpfTextLine MakeTextLine(int number, bool isCurrentLineNumber); double NumberWidth { get; } diff --git a/Src/VimWpf/Implementation/RelativeLineNumbers/Line.cs b/Src/VimWpf/Implementation/RelativeLineNumbers/Line.cs index 5623ba1539..8b9be0d3fb 100644 --- a/Src/VimWpf/Implementation/RelativeLineNumbers/Line.cs +++ b/Src/VimWpf/Implementation/RelativeLineNumbers/Line.cs @@ -17,5 +17,20 @@ public Line(int number, double verticalBaseline, bool isCaretLine) Baseline = verticalBaseline; IsCaretLine = isCaretLine; } + + public override bool Equals(object obj) + { + return obj is Line line && + Number == line.Number && + IsCaretLine == line.IsCaretLine; + } + + public override int GetHashCode() + { + int hashCode = 17; + hashCode = hashCode * 23 + Number.GetHashCode(); + hashCode = hashCode * 23 + IsCaretLine.GetHashCode(); + return hashCode; + } } } diff --git a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberDrawer.cs b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberDrawer.cs index ac2235ab01..d6a31ea224 100644 --- a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberDrawer.cs +++ b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberDrawer.cs @@ -12,7 +12,7 @@ internal sealed class LineNumberDrawer private readonly ILineFormatTracker _formatTracker; private readonly LineNumberVisualStore _store; - internal LineNumberDrawer(Canvas canvas, ILineFormatTracker formatTracker) + internal LineNumberDrawer(Canvas canvas, ILineFormatTracker formatTracker, bool isRelative) { _canvas = canvas ?? throw new ArgumentNullException(nameof(canvas)); @@ -20,7 +20,7 @@ internal LineNumberDrawer(Canvas canvas, ILineFormatTracker formatTracker) _formatTracker = formatTracker ?? throw new ArgumentNullException(nameof(formatTracker)); - _store = new LineNumberVisualStore(formatTracker); + _store = new LineNumberVisualStore(formatTracker, isRelative); } public void UpdateLines(IEnumerable lines) @@ -30,12 +30,12 @@ public void UpdateLines(IEnumerable lines) _canvas.Children.Clear(); double width = _canvas.Width; - foreach (var numberTargets in lines.GroupBy(x => x.Number)) - { - var visual = _store[numberTargets.Key]; - - visual.ReplaceRenderTargets(numberTargets, width); + // Group by line number & caret line + foreach (var line in lines.GroupBy(x => x)) + { + var visual = _store[line.Key]; + visual.ReplaceRenderTargets(line, width); _canvas.Children.Add(visual); } } diff --git a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberFormatTracker.cs b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberFormatTracker.cs index c7866e872d..4c8850ff82 100644 --- a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberFormatTracker.cs +++ b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberFormatTracker.cs @@ -15,13 +15,14 @@ namespace Vim.UI.Wpf.Implementation.RelativeLineNumbers internal sealed class LineNumberFormatTracker : ILineFormatTracker { private readonly IFormattedLineSource _formattedLineSource; + private readonly IWpfTextView _textView; private readonly IClassificationFormatMap _classificationFormatMap; private readonly IClassificationTypeRegistryService _classificationTypeRegistry; private TextFormattingRunProperties _formatting; + private TextFormattingRunProperties _selectedLineNumberFormatting; private TextFormatter _textFormatter; private bool _formatChanged; public Brush Background { get; private set; } - public double NumberWidth { get; private set; } public LineNumberFormatTracker( @@ -29,7 +30,7 @@ internal sealed class LineNumberFormatTracker : ILineFormatTracker IClassificationFormatMap classificationFormatMap, IClassificationTypeRegistryService classificationTypeRegistry) { - textView = textView + _textView = textView ?? throw new ArgumentNullException(nameof(textView)); _classificationFormatMap = classificationFormatMap @@ -56,14 +57,15 @@ public bool TryClearReformatRequest() return false; } - public WpfTextLine MakeTextLine(int lineNumber) + public WpfTextLine MakeTextLine(int lineNumber, bool isCurrentLineNumber) { // Use '~' for the phantom line, otherwise the line number. string text = lineNumber == -1 ? "~" : lineNumber.ToString(CultureInfo.CurrentUICulture.NumberFormat); - var textSource = new LineNumberTextSource(text, _formatting); - var format = new TextFormattingParagraphProperties(_formatting); + var formatting = isCurrentLineNumber ? _selectedLineNumberFormatting : _formatting; + var textSource = new LineNumberTextSource(text, formatting); + var format = new TextFormattingParagraphProperties(formatting); return _textFormatter.FormatLine(textSource, 0, 0, format, null); } @@ -73,13 +75,21 @@ private void UpdateFormat() var lineNumberType = _classificationTypeRegistry.GetClassificationType("line number"); _formatting = _classificationFormatMap.GetTextProperties(lineNumberType); + var selectedLineNumberType = _classificationTypeRegistry.GetClassificationType("Selected Line Number"); + _selectedLineNumberFormatting = + selectedLineNumberType == null ? + _formatting : + _classificationFormatMap.GetTextProperties(selectedLineNumberType); + Background = _formatting.BackgroundBrush; _textFormatter = _formattedLineSource.UseDisplayMode ? TextFormatter.Create(TextFormattingMode.Display) : TextFormatter.Create(TextFormattingMode.Ideal); - NumberWidth = Enumerable.Range(0, 10).Max(x => MakeTextLine(x).Width); + int currentLineNumber = _textView.Caret.Position.BufferPosition.GetContainingLine().LineNumber + 1; + + NumberWidth = Enumerable.Range(0, 10).Max(x => MakeTextLine(x, x == currentLineNumber).Width); _formatChanged = true; } diff --git a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberVisual.cs b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberVisual.cs index 236d2c03f8..b1ee885087 100644 --- a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberVisual.cs +++ b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberVisual.cs @@ -12,14 +12,14 @@ namespace Vim.UI.Wpf.Implementation.RelativeLineNumbers internal class LineNumberVisual : UIElement { private readonly WpfTextLine _textLine; - + private readonly bool _isRelative; private readonly List _renderTargets; - internal LineNumberVisual(WpfTextLine textLine) + internal LineNumberVisual(WpfTextLine textLine, bool isRelative) { _textLine = textLine ?? throw new ArgumentNullException(nameof(textLine)); - + _isRelative = isRelative; _renderTargets = new List(); IsHitTestVisible = false; @@ -49,7 +49,7 @@ private void AddRenderTarget(Line line, double width) { var verticalOffset = line.Baseline - _textLine.TextBaseline; - var horizontalOffset = line.IsCaretLine + var horizontalOffset = line.IsCaretLine || !_isRelative ? 0 : width - _textLine.Width; diff --git a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberVisualStore.cs b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberVisualStore.cs index ce7377efea..2b9773177c 100644 --- a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberVisualStore.cs +++ b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumberVisualStore.cs @@ -5,30 +5,31 @@ namespace Vim.UI.Wpf.Implementation.RelativeLineNumbers { internal sealed class LineNumberVisualStore { - private readonly Dictionary _cache; + private readonly Dictionary _cache; private readonly ILineFormatTracker _formatTracker; + private readonly bool _isRelative; - internal LineNumberVisualStore(ILineFormatTracker formatTracker) + internal LineNumberVisualStore(ILineFormatTracker formatTracker, bool isRelative) { _formatTracker = formatTracker ?? throw new ArgumentNullException(nameof(formatTracker)); - - _cache = new Dictionary(); + _isRelative = isRelative; + _cache = new Dictionary(); } - public LineNumberVisual this[int lineNumber] + public LineNumberVisual this[Line line] { get { - if (_cache.TryGetValue(lineNumber, out var visual)) + if (_cache.TryGetValue(line, out var visual)) { return visual; } - var line = _formatTracker.MakeTextLine(lineNumber); - visual = new LineNumberVisual(line); - _cache[lineNumber] = visual; + var textLine = _formatTracker.MakeTextLine(line.Number, line.IsCaretLine); + visual = new LineNumberVisual(textLine, _isRelative); + _cache[line] = visual; return visual; } diff --git a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumbersCalculator.cs b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumbersCalculator.cs index 6dff1ee211..97e6f91455 100644 --- a/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumbersCalculator.cs +++ b/Src/VimWpf/Implementation/RelativeLineNumbers/LineNumbersCalculator.cs @@ -104,7 +104,7 @@ private Line MakeLine(ITextViewLine wpfLine, int distanceToCaret, bool hasValidC bool isCaretLine = hasValidCaret && distanceToCaret == 0; - bool caretLineStyle = isCaretLine && _localSettings.RelativeNumber; + bool caretLineStyle = isCaretLine; return new Line(numberToDisplay, verticalBaseline, caretLineStyle); } diff --git a/Src/VimWpf/Implementation/RelativeLineNumbers/RelativeLineNumbersMargin.cs b/Src/VimWpf/Implementation/RelativeLineNumbers/RelativeLineNumbersMargin.cs index 0d8201b7bc..8d4829894b 100644 --- a/Src/VimWpf/Implementation/RelativeLineNumbers/RelativeLineNumbersMargin.cs +++ b/Src/VimWpf/Implementation/RelativeLineNumbers/RelativeLineNumbersMargin.cs @@ -19,9 +19,9 @@ internal sealed class RelativeLineNumbersMargin : VerticalCanvasMargin private readonly IProtectedOperations _protectedOperations; private readonly LineNumbersTracker _linesTracker; - private readonly LineNumberDrawer _lineNumberDrawer; private readonly LineNumbersCalculator _lineNumbersCalculator; + private LineNumberDrawer _lineNumberDrawer; private double _width = double.NaN; private double _minWidth = 0.0; private double _maxWidth = double.PositiveInfinity; @@ -56,7 +56,7 @@ internal sealed class RelativeLineNumbersMargin : VerticalCanvasMargin _lineNumbersCalculator = new LineNumbersCalculator(_textView, _localSettings); - _lineNumberDrawer = new LineNumberDrawer(Canvas, _formatTracker); + _lineNumberDrawer = new LineNumberDrawer(Canvas, _formatTracker, _localSettings.RelativeNumber); _linesTracker = new LineNumbersTracker(_textView); @@ -112,7 +112,11 @@ private void UpdateVimNumberSettings(SettingEventArgs eventArgs) SetVisualStudioMarginVisibility(Visibility.Hidden); if (_localSettings.Number || _localSettings.RelativeNumber) { - RedrawLines(); + if (eventArgs.Setting.Name == "relativenumber" || eventArgs.Setting.Name == "number") + { + _lineNumberDrawer = new LineNumberDrawer(Canvas, _formatTracker, _localSettings.RelativeNumber); + RedrawLines(); + } } }