Appearance
| 1 | namespace Semantic is | |
| 2 | use Symbols.Symbol | |
| 3 | use Ghul.Disposable | |
| 4 | ||
| 5 | // Decides the scope that symbol names render relative to. By default | |
| 6 | // that is the scope currently being compiled (read on demand from the | |
| 7 | // symbol table, so it stays correct however the scope stack is | |
| 8 | // unwound), which is why a diagnostic reads relative to where it is | |
| 9 | // reported. An override replaces that default for the duration of a | |
| 10 | // language-service render (targeting the cursor's scope) or a region | |
| 11 | // that needs scope-independent output (`with_scope(null)` forces fully | |
| 12 | // qualified names, e.g. for a canonical ordering). | |
| 13 | class NAME_DISPLAY is | |
| 14 | _override_scope: Scope? | |
| 15 | _has_override: bool | |
| 16 | ||
| 17 | init() is | |
| 18 | si | |
| 19 | ||
| 20 | render_scope: Scope? => | |
| 21 | if _has_override then | |
| 22 | _override_scope | |
| 23 | else | |
| 24 | _current_build_scope | |
| 25 | fi | |
| 26 | ||
| 27 | _current_build_scope: Scope? is | |
| 28 | let symbol_table = IoC.CONTAINER.instance.symbol_table | |
| 29 | ||
| 30 | return symbol_table.current_scope | |
| 31 | si | |
| 32 | ||
| 33 | name_for(symbol: Symbol) -> string => | |
| 34 | symbol.render_name(render_scope) | |
| 35 | ||
| 36 | // The scope-relative name without its type-argument suffix — for | |
| 37 | // rendering a constructed generic's head, which then appends its own | |
| 38 | // actual arguments. | |
| 39 | bare_name_for(symbol: Symbol) -> string => | |
| 40 | symbol._render_scope_relative_name(render_scope) | |
| 41 | ||
| 42 | // Render relative to `scope` (null forces fully qualified) until the | |
| 43 | // returned holder is disposed. Consume with `let use` so the | |
| 44 | // previous render scope is restored on every exit path, including | |
| 45 | // when rendering throws. | |
| 46 | with_scope(scope: Scope?) -> RENDER_SCOPE_HOLDER is | |
| 47 | let holder = RENDER_SCOPE_HOLDER(self, _override_scope, _has_override) | |
| 48 | ||
| 49 | _override_scope = scope | |
| 50 | _has_override = true | |
| 51 | ||
| 52 | return holder | |
| 53 | si | |
| 54 | ||
| 55 | restore_override(scope: Scope?, has_override: bool) is | |
| 56 | _override_scope = scope | |
| 57 | _has_override = has_override | |
| 58 | si | |
| 59 | si | |
| 60 | ||
| 61 | struct RENDER_SCOPE_HOLDER: Disposable is | |
| 62 | _display: NAME_DISPLAY? | |
| 63 | _previous_scope: Scope? | |
| 64 | _previous_has_override: bool | |
| 65 | ||
| 66 | init(display: NAME_DISPLAY, previous_scope: Scope?, previous_has_override: bool) is | |
| 67 | _display = display | |
| 68 | _previous_scope = previous_scope | |
| 69 | _previous_has_override = previous_has_override | |
| 70 | si | |
| 71 | ||
| 72 | dispose() is | |
| 73 | // A value-type disposable must be safe on its default value: | |
| 74 | // `let use` disposal null-checks reference types before calling | |
| 75 | // dispose but not value types, so an early return past the | |
| 76 | // `let use` hands dispose a zero-initialized holder. A null | |
| 77 | // display means the holder was never constructed and so set no | |
| 78 | // override to restore. | |
| 79 | let display = _display | |
| 80 | ||
| 81 | if display? then | |
| 82 | display.restore_override(_previous_scope, _previous_has_override) | |
| 83 | fi | |
| 84 | si | |
| 85 | si | |
| 86 | si |