Appearance
| 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 |