using System; using System.Collections.Generic; using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { public enum BlockNavigationDirection { First = 0, Prev, Next, Last } public class BlockNavigation : ObservableObject { public record Block(int Start, int End) { public bool Contains(int line) { return line >= Start && line <= End; } } public string Indicator { get { if (_blocks.Count == 0) return "-/-"; if (_current >= 0 && _current < _blocks.Count) return $"{_current + 1}/{_blocks.Count}"; return $"-/{_blocks.Count}"; } } public BlockNavigation(List lines, int cur) { _blocks.Clear(); if (lines.Count == 0) { _current = -1; return; } var lineIdx = 0; var blockStartIdx = 0; var isReadingBlock = false; var blocks = new List(); foreach (var line in lines) { lineIdx++; if (line.Type is Models.TextDiffLineType.Added or Models.TextDiffLineType.Deleted or Models.TextDiffLineType.None) { if (!isReadingBlock) { isReadingBlock = true; blockStartIdx = lineIdx; } } else { if (isReadingBlock) { blocks.Add(new Block(blockStartIdx, lineIdx - 1)); isReadingBlock = false; } } } if (isReadingBlock) blocks.Add(new Block(blockStartIdx, lines.Count)); _blocks.AddRange(blocks); _current = Math.Min(_blocks.Count - 1, cur); } public int GetCurrentBlockIndex() { return _current; } public Block GetCurrentBlock() { if (_current >= 0 && _current < _blocks.Count) return _blocks[_current]; return null; } public Block Goto(BlockNavigationDirection direction) { if (_blocks.Count == 0) return null; _current = direction switch { BlockNavigationDirection.First => 0, BlockNavigationDirection.Prev => _current <= 0 ? 0 : _current - 1, BlockNavigationDirection.Next => _current >= _blocks.Count - 1 ? _blocks.Count - 1 : _current + 1, BlockNavigationDirection.Last => _blocks.Count - 1, _ => _current }; OnPropertyChanged(nameof(Indicator)); return _blocks[_current]; } public void UpdateByChunk(TextDiffSelectedChunk chunk) { _current = -1; var chunkStart = chunk.StartIdx + 1; var chunkEnd = chunk.EndIdx + 1; for (var i = 0; i < _blocks.Count; i++) { var block = _blocks[i]; if (chunkStart > block.End) continue; if (chunkEnd < block.Start) { _current = i - 1; break; } _current = i; } } public void UpdateByCaretPosition(int caretLine) { if (_current >= 0 && _current < _blocks.Count) { var block = _blocks[_current]; if (block.Contains(caretLine)) return; } _current = -1; for (var i = 0; i < _blocks.Count; i++) { var block = _blocks[i]; if (block.Start > caretLine) break; _current = i; if (block.End >= caretLine) break; } OnPropertyChanged(nameof(Indicator)); } private int _current; private readonly List _blocks = []; } }