-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLineMap.cs
More file actions
71 lines (63 loc) · 2.63 KB
/
Copy pathLineMap.cs
File metadata and controls
71 lines (63 loc) · 2.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using System;
namespace Heddle.LanguageServices
{
/// <summary>
/// <para>Immutable line-start index over one document text (phase 6 D11). All offsets and characters count
/// UTF-16 code units — exactly LSP's default <c>utf-16</c> position encoding and <c>BlockPosition</c>'s
/// storage — so mapping is line-splitting only.</para>
/// <para><c>\n</c> terminates a line; a <c>\r</c> immediately before it belongs to the terminated line
/// (matching the engine's line splitting).</para>
/// </summary>
public sealed class LineMap
{
private readonly int[] _lineStarts;
private readonly int _length;
internal LineMap(string text)
{
if (text == null)
throw new ArgumentNullException(nameof(text));
_length = text.Length;
var starts = new System.Collections.Generic.List<int> { 0 };
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\n')
starts.Add(i + 1);
}
_lineStarts = starts.ToArray();
}
/// <summary>Number of lines (always at least one).</summary>
public int LineCount => _lineStarts.Length;
/// <summary>Maps an absolute UTF-16 offset to a zero-based (line, character) position (clamped in range).</summary>
public (int Line, int Character) OffsetToPosition(int offset)
{
if (offset < 0)
offset = 0;
if (offset > _length)
offset = _length;
int lo = 0, hi = _lineStarts.Length - 1;
while (lo < hi)
{
int mid = (lo + hi + 1) / 2;
if (_lineStarts[mid] <= offset)
lo = mid;
else
hi = mid - 1;
}
return (lo, offset - _lineStarts[lo]);
}
/// <summary>Maps a zero-based (line, character) position to an absolute UTF-16 offset (leniently clamped).</summary>
public int PositionToOffset(int line, int character)
{
if (line < 0)
return 0;
if (line >= _lineStarts.Length)
return _length;
int start = _lineStarts[line];
// Content end excludes the terminating '\n' so an over-long character clamps to the line, not the
// next line's start (LSP's lenient interpretation).
int contentEnd = line + 1 < _lineStarts.Length ? _lineStarts[line + 1] - 1 : _length;
int offset = start + Math.Max(0, character);
return offset > contentEnd ? contentEnd : offset;
}
}
}