Skip to content
← Back

src/analysis/inlay_range.ghul

1
namespace Analysis is
2
use Logging
3
use Protocol = Analysis.Protocol
4
5
use Collections.Iterable
6
use Ghul.Pipes
7
8
// Limits inlay hints to the range the client asked about - the
9
// editor's viewport - so a kind with a hint on nearly every line does
10
// not pay encode, transport and parse for a whole file. Any range
11
// field absent on the request means whole file, so an older client's
12
// frame is served exactly as before.
13
//
14
// The filter runs before the mergers, which is sound only because
15
// both group on exact position: a range either contains a whole
16
// group or none of it. A merger that grouped by enclosing scope
17
// instead could see a group cut in half by this filter - the
18
// property is pinned by the unit tests.
19
class INLAY_RANGE is
20
init() is si
21
22
filter(inlays: Iterable[Logging.INLAY], request: Protocol.Request.INLAY_HINTS) -> Collections.List[Logging.INLAY] static is
23
let range = bounds(request)
24
25
if !range? then
26
return inlays |> collect()
27
fi
28
29
let (start_line, start_column, end_line, end_column) = range
30
31
return inlays |> filter(i => within(i, start_line, start_column, end_line, end_column)) |> collect()
32
si
33
34
// A hint's position is inside the range, comparing line then
35
// column. Start-inclusive, end-exclusive, so a hint sitting at
36
// the end position belongs to the next viewport rather than
37
// flickering between two.
38
within(
39
inlay: Logging.INLAY,
40
start_line: int,
41
start_column: int,
42
end_line: int,
43
end_column: int
44
) -> bool static is
45
let line = inlay.location.start_line
46
let column = inlay.location.start_column
47
48
return (line > start_line \/ (line == start_line /\ column >= start_column)) /\
49
(line < end_line \/ (line == end_line /\ column < end_column))
50
si
51
52
// The request's range with absent fields defaulted inwards, so a
53
// partially-specified range behaves as the whole file from that
54
// edge. Null when no range was given at all. The defaults stay
55
// inside what LOCATION's 12-bit packed form can hold: the
56
// terminator boundaries compare as packed ints, and a default
57
// that overflowed the packing would silently match nothing.
58
bounds(request: Protocol.Request.INLAY_HINTS) -> (int, int, int, int)? static is
59
if !request.start_line? then
60
return null
61
fi
62
63
return (
64
request.start_line ?? 1,
65
request.start_column ?? 1,
66
request.end_line ?? _MAX_LINES,
67
request.end_column ?? _MAX_COLUMNS
68
)
69
si
70
71
_MAX_LINES: int static => 524_287
72
_MAX_COLUMNS: int static => 4_095
73
si
74
si