Appearance
| 1 | namespace Semantic.Symbols is | |
| 2 | use Collections.LIST; | |
| 3 | ||
| 4 | use Semantic.Types.Type; | |
| 5 | ||
| 6 | union SignaturePart is | |
| 7 | NIL; | |
| 8 | LITERAL(text: string); | |
| 9 | NAME(symbol: Symbol); | |
| 10 | TYPE_REF(type: Type); | |
| 11 | SEQUENCE(parts: LIST[SignaturePart]); | |
| 12 | // A break point for the layout engine. Flat form: `open` + | |
| 13 | // items joined by (`separator` + " " when `tight` is false, | |
| 14 | // `separator` alone when true) + `close`. Broken form: `open` | |
| 15 | // + newline + 4-space-indented items joined by `separator` + | |
| 16 | // newline + indent + newline + `close` at the outer indent. | |
| 17 | // `separator` is punctuation-only (no trailing space); `tight` | |
| 18 | // controls whether a flat rendering inserts a space after each | |
| 19 | // one. Argument lists use `separator=","`, `tight=false`; | |
| 20 | // generic-argument brackets use `separator=","`, `tight=true`. | |
| 21 | WRAPPABLE( | |
| 22 | open: string, | |
| 23 | separator: string, | |
| 24 | tight: bool, | |
| 25 | close: string, | |
| 26 | items: LIST[SignaturePart] | |
| 27 | ); | |
| 28 | // A forced line break introducing an indented continuation: | |
| 29 | // a newline, `indent` spaces, the `head` text, then `body`. | |
| 30 | // `body`'s own break points nest a further `head.length` columns | |
| 31 | // so a wrappable body's continuation lines hang under the text | |
| 32 | // after `head` rather than under the break. Used for the narrowed | |
| 33 | // `► <type>` line beneath a symbol's declared type. | |
| 34 | HANGING( | |
| 35 | indent: int, | |
| 36 | head: string, | |
| 37 | body: SignaturePart | |
| 38 | ); | |
| 39 | si | |
| 40 | ||
| 41 | // Convenience builders that fold the `Collections.LIST[SignaturePart]` | |
| 42 | // wrapping into a single call so `describe` overrides read as data. | |
| 43 | class PARTS is | |
| 44 | literal(text: string) -> SignaturePart static => | |
| 45 | SignaturePart.LITERAL(text); | |
| 46 | ||
| 47 | name(symbol: Symbol) -> SignaturePart static => | |
| 48 | SignaturePart.NAME(symbol); | |
| 49 | ||
| 50 | type_ref(type: Type) -> SignaturePart static => | |
| 51 | SignaturePart.TYPE_REF(type); | |
| 52 | ||
| 53 | nil() -> SignaturePart static => | |
| 54 | SignaturePart.NIL(); | |
| 55 | ||
| 56 | sequence(items: SignaturePart[]) -> SignaturePart static => | |
| 57 | SignaturePart.SEQUENCE(Collections.LIST[SignaturePart](items)); | |
| 58 | ||
| 59 | wrappable( | |
| 60 | open: string, | |
| 61 | separator: string, | |
| 62 | tight: bool, | |
| 63 | close: string, | |
| 64 | items: SignaturePart[] | |
| 65 | ) -> SignaturePart static => | |
| 66 | SignaturePart.WRAPPABLE(open, separator, tight, close, Collections.LIST[SignaturePart](items)); | |
| 67 | ||
| 68 | hanging(indent: int, head: string, body: SignaturePart) -> SignaturePart static => | |
| 69 | SignaturePart.HANGING(indent, head, body); | |
| 70 | si | |
| 71 | ||
| 72 | class DESCRIBE_CONTEXT is | |
| 73 | _default: DESCRIBE_CONTEXT? static; | |
| 74 | _observed_types: Collections.Map[Symbol, Type]?; | |
| 75 | ||
| 76 | instance: DESCRIBE_CONTEXT static is | |
| 77 | if !_default? then | |
| 78 | _default = DESCRIBE_CONTEXT(); | |
| 79 | fi | |
| 80 | return _default; | |
| 81 | si | |
| 82 | ||
| 83 | init() is | |
| 84 | si | |
| 85 | ||
| 86 | with_observed_types(observed_types: Collections.Map[Symbol, Type]) -> DESCRIBE_CONTEXT static is | |
| 87 | let result = DESCRIBE_CONTEXT(); | |
| 88 | result._observed_types = observed_types; | |
| 89 | return result; | |
| 90 | si | |
| 91 | ||
| 92 | observed_type_for(symbol: Symbol) -> Type? is | |
| 93 | let map = _observed_types; | |
| 94 | if !map? then | |
| 95 | return null; | |
| 96 | fi | |
| 97 | if !map.contains_key(symbol) then | |
| 98 | return null; | |
| 99 | fi | |
| 100 | return map[symbol]; | |
| 101 | si | |
| 102 | si | |
| 103 | ||
| 104 | class TEXT_RENDERER is | |
| 105 | _context: DESCRIBE_CONTEXT; | |
| 106 | ||
| 107 | init(context: DESCRIBE_CONTEXT) is | |
| 108 | _context = context; | |
| 109 | si | |
| 110 | ||
| 111 | render(part: SignaturePart) -> string is | |
| 112 | let buffer = System.Text.StringBuilder(); | |
| 113 | _emit(buffer, part); | |
| 114 | return buffer.to_string(); | |
| 115 | si | |
| 116 | ||
| 117 | _emit(buffer: System.Text.StringBuilder, part: SignaturePart) is | |
| 118 | if isa SignaturePart.NIL(part) then | |
| 119 | return; | |
| 120 | fi | |
| 121 | ||
| 122 | if let literal: SignaturePart.LITERAL = part then | |
| 123 | buffer.append(literal.text); | |
| 124 | return; | |
| 125 | fi | |
| 126 | ||
| 127 | if let name: SignaturePart.NAME = part then | |
| 128 | buffer.append(IoC.CONTAINER.instance.name_display.name_for(name.symbol)); | |
| 129 | return; | |
| 130 | fi | |
| 131 | ||
| 132 | if let type_ref: SignaturePart.TYPE_REF = part then | |
| 133 | buffer.append("{type_ref.type}"); | |
| 134 | return; | |
| 135 | fi | |
| 136 | ||
| 137 | if let sequence: SignaturePart.SEQUENCE = part then | |
| 138 | for item in sequence.parts do | |
| 139 | _emit(buffer, item); | |
| 140 | od | |
| 141 | return; | |
| 142 | fi | |
| 143 | ||
| 144 | if let wrappable: SignaturePart.WRAPPABLE = part then | |
| 145 | buffer.append(wrappable.open); | |
| 146 | let first mut = true; | |
| 147 | for item in wrappable.items do | |
| 148 | if !first then | |
| 149 | buffer.append(wrappable.separator); | |
| 150 | if !wrappable.tight then | |
| 151 | buffer.append(' '); | |
| 152 | fi | |
| 153 | fi | |
| 154 | first = false; | |
| 155 | _emit(buffer, item); | |
| 156 | od | |
| 157 | buffer.append(wrappable.close); | |
| 158 | return; | |
| 159 | fi | |
| 160 | ||
| 161 | if let hanging: SignaturePart.HANGING = part then | |
| 162 | buffer.append('\n'); | |
| 163 | let i mut = 0; | |
| 164 | while i < hanging.indent do | |
| 165 | buffer.append(' '); | |
| 166 | i = i + 1; | |
| 167 | od | |
| 168 | buffer.append(hanging.head); | |
| 169 | _emit(buffer, hanging.body); | |
| 170 | return; | |
| 171 | fi | |
| 172 | si | |
| 173 | si | |
| 174 | si |