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