| 1 | namespace CascadeIDE.Features.Editor.Application.Monaco; |
| 2 | |
| 3 | /// <summary>Line/column ↔ offset mapping for <see cref="EditorNavigationService"/> (testable).</summary> |
| 4 | public static class EditorNavigationLineMapping |
| 5 | { |
| 6 | public static (int start, int length) SelectionOffsetsFromLines( |
| 7 | string text, |
| 8 | int startLine, |
| 9 | int startColumn, |
| 10 | int endLine, |
| 11 | int? endColumn) |
| 12 | { |
| 13 | var start = OffsetFromLineColumn(text, startLine, startColumn); |
| 14 | var endCol = endColumn ?? int.MaxValue; |
| 15 | var end = OffsetFromLineColumn(text, endLine, endCol, endOfLineIfOverflow: true); |
| 16 | return (start, Math.Max(0, end - start)); |
| 17 | } |
| 18 | |
| 19 | public static int OffsetFromLineColumn( |
| 20 | string text, |
| 21 | int lineOneBased, |
| 22 | int columnOneBased, |
| 23 | bool endOfLineIfOverflow = false) |
| 24 | { |
| 25 | if (string.IsNullOrEmpty(text) || lineOneBased < 1) |
| 26 | return 0; |
| 27 | |
| 28 | var lineStart = 0; |
| 29 | var line = 1; |
| 30 | for (var i = 0; i < text.Length && line < lineOneBased; i++) |
| 31 | { |
| 32 | if (text[i] == '\n') |
| 33 | { |
| 34 | line++; |
| 35 | lineStart = i + 1; |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | if (line != lineOneBased) |
| 40 | return text.Length; |
| 41 | |
| 42 | var lineEnd = text.IndexOf('\n', lineStart); |
| 43 | if (lineEnd < 0) |
| 44 | lineEnd = text.Length; |
| 45 | var lineLen = lineEnd - lineStart; |
| 46 | var col = Math.Max(1, columnOneBased); |
| 47 | if (endOfLineIfOverflow && col > lineLen + 1) |
| 48 | return lineEnd; |
| 49 | var offset = lineStart + Math.Min(col - 1, lineLen); |
| 50 | return Math.Min(offset, text.Length); |
| 51 | } |
| 52 | } |
| 53 | |