Skip to content
← Back

src/analysis/narrowing_inlay_merger.ghul

1
namespace Analysis is
2
use Logging;
3
use Collections;
4
5
// Merges editor-only narrowing-introduction inlays that share a source
6
// location and construct into a single hint. The stored carriers hold
7
// only the narrowed-to type in `detail`; the hover body is assembled
8
// here so a true/false edge pair renders as one marker rather than two
9
// stacked at one column. The body is presentation-neutral text — one
10
// `► <type>` line for the matched narrow, plus a `> <type>` line for
11
// the complementary else narrow when there is one. A client wraps it
12
// for display (the VS Code hover fences it as a ghul code block so the
13
// types syntax-highlight and the sigils stay neutral).
14
//
15
// The matched edge carries the bare `narrowing-<kind>` slug; the
16
// complementary edge carries `narrowing-<kind>-complement`, at the same
17
// location. Kill hints (a different sigil) and any other carrier pass
18
// through unchanged.
19
class NARROWING_INLAY_MERGER is
20
init() is si
21
22
merge(carriers: Iterable[DIAGNOSTIC_MESSAGE]) -> LIST[DIAGNOSTIC_MESSAGE] is
23
let groups = MAP[string, LIST[DIAGNOSTIC_MESSAGE]]();
24
let order = LIST[string]();
25
26
let other_index mut = 0;
27
28
for c in carriers do
29
let key: string mut = "";
30
31
if _is_introduction(c) then
32
key = "I\t{c.location.start_line}\t{c.location.start_column}\t{_base_code(c.code)}";
33
else
34
key = "X\t{other_index}";
35
other_index = other_index + 1;
36
fi
37
38
if !groups.contains_key(key) then
39
order.add(key);
40
groups[key] = LIST[DIAGNOSTIC_MESSAGE]();
41
fi
42
43
groups[key].add(c);
44
od
45
46
let result = LIST[DIAGNOSTIC_MESSAGE]();
47
48
for key in order do
49
let group = groups[key];
50
51
if group.count > 0 /\ _is_introduction(group[0]) then
52
result.add(_render(group));
53
else
54
for c in group do
55
result.add(c);
56
od
57
fi
58
od
59
60
return result;
61
si
62
63
_render(group: LIST[DIAGNOSTIC_MESSAGE]) -> DIAGNOSTIC_MESSAGE is
64
let head = group[0];
65
let base = _base_code(head.code);
66
67
let true_detail mut = "";
68
let false_detail: string? mut = null;
69
70
for g in group do
71
if _is_complement(g.code) then
72
false_detail = g.detail;
73
else
74
true_detail = g.detail ?? "";
75
fi
76
od
77
78
let body = System.Text.StringBuilder();
79
80
body.append("► ").append(_body_text(true_detail));
81
82
if false_detail? then
83
body.append("\n> ").append(_body_text(false_detail));
84
fi
85
86
return DIAGNOSTIC_MESSAGE(
87
head.is_analysis,
88
head.is_compile_expressions,
89
head.severity,
90
head.location,
91
base,
92
head.text,
93
body.to_string()
94
);
95
si
96
97
// A path narrow whose static type couldn't be resolved stores an
98
// empty type; fall back to a bare phrase so the line is not left
99
// dangling after its sigil.
100
_body_text(detail: string?) -> string =>
101
if detail? /\ detail.length > 0 then detail else "holds a value" fi;
102
103
_is_introduction(c: DIAGNOSTIC_MESSAGE) -> bool =>
104
c.is_inlay /\ c.text =~ "►";
105
106
_is_complement(code: string?) -> bool =>
107
code? /\ code.ends_with("-complement");
108
109
_base_code(code: string?) -> string =>
110
if !code? then
111
""
112
elif code.ends_with("-complement") then
113
code.substring(0, code.length - "-complement".length)
114
else
115
code
116
fi;
117
si
118
si