Skip to content
← Back

src/analysis/definition_virtuality_merger.ghul

1
namespace Analysis is
2
use Logging
3
use Collections
4
5
// Folds the two accessor carriers a readable-and-assignable property
6
// produces into a single hint. Both accessors are declared at the
7
// property's own name, so both carriers land at one column; left
8
// alone they would stack two markers there.
9
//
10
// The merged label is the union of the two halves' glyphs, in the
11
// pass's own order, and the hover keeps both halves' notes with the
12
// getter's first — so a property that overrides on one half and is
13
// overridden on the other still says which half did which.
14
//
15
// Anything else — a method carrier, a type carrier, a narrowing
16
// inlay, a kill hint — passes through untouched.
17
class DEFINITION_VIRTUALITY_MERGER is
18
_GET_CODE: string static => "{Syntax.Process.DEFINITION_VIRTUALITY.CODE}-get"
19
_SET_CODE: string static => "{Syntax.Process.DEFINITION_VIRTUALITY.CODE}-set"
20
21
// The pass's emission order, so a union of two halves' glyphs
22
// reads the same way a single half's does.
23
_ORDER: string static =>
24
"{Syntax.Process.DEFINITION_VIRTUALITY.ABSTRACT}"
25
"{Syntax.Process.DEFINITION_VIRTUALITY.OVERRIDES}"
26
"{Syntax.Process.DEFINITION_VIRTUALITY.OVERRIDDEN}"
27
"{Syntax.Process.DEFINITION_VIRTUALITY.OPEN}"
28
29
init() is si
30
31
merge(carriers: Iterable[INLAY]) -> LIST[INLAY] is
32
let groups = MAP[string, LIST[INLAY]]()
33
let order = LIST[string]()
34
35
let other_index mut = 0
36
37
for c in carriers do
38
let key: string mut = ""
39
40
if _is_accessor(c) then
41
key = "A\t{c.location.file_name}\t{c.location.start_line}\t{c.location.start_column}"
42
else
43
key = "X\t{other_index}"
44
other_index = other_index + 1
45
fi
46
47
if !groups.contains_key(key) then
48
order.add(key)
49
groups[key] = LIST[INLAY]()
50
fi
51
52
groups[key].add(c)
53
od
54
55
let result = LIST[INLAY]()
56
57
for key in order do
58
let group = groups[key]
59
60
if group.count > 1 then
61
result.add(_render(group))
62
else
63
for c in group do
64
result.add(c)
65
od
66
fi
67
od
68
69
return result
70
si
71
72
_render(group: LIST[INLAY]) -> INLAY is
73
let head = group[0]
74
75
let glyphs = System.Text.StringBuilder()
76
77
for g in _ORDER do
78
let present mut = false
79
80
for c in group do
81
if c.text.contains(g) then
82
present = true
83
fi
84
od
85
86
if present then
87
glyphs.append(g)
88
fi
89
od
90
91
// The subject line is the property's name, which both halves
92
// carry, so it is taken once from whichever came first.
93
let body = System.Text.StringBuilder()
94
95
body.append(_subject(head))
96
97
for c in _get_first(group) do
98
body.append("\n\n").append(_notes(c))
99
od
100
101
return INLAY(
102
head.is_analysis,
103
head.is_compile_expressions,
104
head.location,
105
Syntax.Process.DEFINITION_VIRTUALITY.CODE,
106
"{glyphs}",
107
"{body}"
108
)
109
si
110
111
// The getter's notes first, whatever order the carriers arrived
112
// in: a property reads before it is assigned.
113
_get_first(group: LIST[INLAY]) -> LIST[INLAY] is
114
let ordered = LIST[INLAY]()
115
116
for c in group do
117
if c.code? /\ c.code =~ _GET_CODE then
118
ordered.add(c)
119
fi
120
od
121
122
for c in group do
123
if !c.code? \/ c.code !~ _GET_CODE then
124
ordered.add(c)
125
fi
126
od
127
128
return ordered
129
si
130
131
_subject(c: INLAY) -> string is
132
let detail = c.detail
133
134
if !detail? then
135
return ""
136
fi
137
138
let separator = detail.index_of("\n\n")
139
140
return if separator >= 0 then detail.substring(0, separator) else detail fi
141
si
142
143
_notes(c: INLAY) -> string is
144
let detail = c.detail
145
146
if !detail? then
147
return ""
148
fi
149
150
let separator = detail.index_of("\n\n")
151
152
return if separator >= 0 then detail.substring(separator + 2) else "" fi
153
si
154
155
_is_accessor(c: INLAY) -> bool =>
156
c.code? /\ (c.code =~ _GET_CODE \/ c.code =~ _SET_CODE)
157
si
158
si