Appearance
| 1 | namespace Syntax.Process.Printer is | |
| 2 | use Collections.LIST; | |
| 3 | ||
| 4 | use IoC; | |
| 5 | ||
| 6 | use Semantic.Symbols.SignaturePart; | |
| 7 | ||
| 8 | // Lays a SignaturePart tree out against a column budget. WRAPPABLE | |
| 9 | // parts drive the break points: they render flat when the whole thing | |
| 10 | // fits, otherwise their items break onto per-item indented lines. A | |
| 11 | // ONE_OF TYPE_REF expands to a wrappable so a long alternative set | |
| 12 | // breaks one alternative per line. Everything else is verbatim. | |
| 13 | // | |
| 14 | // Shared by the hover pretty-printer (Analysis.SIGNATURE_DOC) and the | |
| 15 | // narrowing-inlay type renderer, so both wrap identically. | |
| 16 | class SIGNATURE_RENDERER is | |
| 17 | // Render a part at `width`. | |
| 18 | render(part: SignaturePart, width: int) -> string static is | |
| 19 | let b = DOC_BUILDER(); | |
| 20 | _emit(b, part); | |
| 21 | return DOC_RENDERER(width).render(b.build()); | |
| 22 | si | |
| 23 | ||
| 24 | // Render a part whose broken continuation lines hang `base_indent` | |
| 25 | // columns in from the left, so it sits correctly beneath a prefix | |
| 26 | // of that width (the `► ` sigil of a narrowing inlay) supplied by | |
| 27 | // the caller. The first line stays flush left; the caller prepends | |
| 28 | // its prefix to it. | |
| 29 | render_nested(part: SignaturePart, base_indent: int, width: int) -> string static is | |
| 30 | let b = DOC_BUILDER(); | |
| 31 | b.begin_nest(base_indent); | |
| 32 | _emit(b, part); | |
| 33 | b.end_nest(); | |
| 34 | return DOC_RENDERER(width).render(b.build()); | |
| 35 | si | |
| 36 | ||
| 37 | _emit(b: DOC_BUILDER, part: SignaturePart) static is | |
| 38 | if isa SignaturePart.NIL(part) then | |
| 39 | return; | |
| 40 | fi | |
| 41 | ||
| 42 | if let literal: SignaturePart.LITERAL = part then | |
| 43 | b.text(literal.text); | |
| 44 | return; | |
| 45 | fi | |
| 46 | ||
| 47 | if let name: SignaturePart.NAME = part then | |
| 48 | b.text(IoC.CONTAINER.instance.name_display.name_for(name.symbol)); | |
| 49 | return; | |
| 50 | fi | |
| 51 | ||
| 52 | if let type_ref: SignaturePart.TYPE_REF = part then | |
| 53 | if isa Semantic.Types.ONE_OF(type_ref.type) then | |
| 54 | _emit_wrappable(b, _one_of_wrappable(cast Semantic.Types.ONE_OF(type_ref.type))); | |
| 55 | else | |
| 56 | b.text("{type_ref.type}"); | |
| 57 | fi | |
| 58 | return; | |
| 59 | fi | |
| 60 | ||
| 61 | if let sequence: SignaturePart.SEQUENCE = part then | |
| 62 | for item in sequence.parts do | |
| 63 | _emit(b, item); | |
| 64 | od | |
| 65 | return; | |
| 66 | fi | |
| 67 | ||
| 68 | if let wrappable: SignaturePart.WRAPPABLE = part then | |
| 69 | _emit_wrappable(b, wrappable); | |
| 70 | return; | |
| 71 | fi | |
| 72 | ||
| 73 | if let hanging: SignaturePart.HANGING = part then | |
| 74 | b.hard_line(); | |
| 75 | b.begin_nest(hanging.indent); | |
| 76 | b.text(hanging.head); | |
| 77 | b.begin_nest(hanging.head.length); | |
| 78 | _emit(b, hanging.body); | |
| 79 | b.end_nest(); | |
| 80 | b.end_nest(); | |
| 81 | return; | |
| 82 | fi | |
| 83 | si | |
| 84 | ||
| 85 | // Expand a one-of type into a wrappable so a long alternative set | |
| 86 | // breaks one alternative per line, matching how argument and | |
| 87 | // generic-argument lists wrap. Flat form reproduces the type's own | |
| 88 | // `{underlying}{a|b|c}` rendering. | |
| 89 | _one_of_wrappable(one_of: Semantic.Types.ONE_OF) -> SignaturePart.WRAPPABLE static is | |
| 90 | let items = LIST[SignaturePart](); | |
| 91 | ||
| 92 | for subtype in one_of.subtypes do | |
| 93 | items.add(Semantic.Symbols.PARTS.literal(subtype.name)); | |
| 94 | od | |
| 95 | ||
| 96 | let close = if one_of.is_optional then "}}?" else "}}" fi; | |
| 97 | ||
| 98 | return SignaturePart.WRAPPABLE( | |
| 99 | "{one_of.underlying_type.short_description}{{", | |
| 100 | "|", | |
| 101 | true, | |
| 102 | close, | |
| 103 | items | |
| 104 | ); | |
| 105 | si | |
| 106 | ||
| 107 | // WRAPPABLE lays out as: `open` + GROUP( NEST 4( SOFT_LINE + | |
| 108 | // items joined by `separator` + LINE-or-SOFT_LINE ) ) + SOFT_LINE | |
| 109 | // + `close`. Group renders flat when the whole thing fits (SOFT_LINE | |
| 110 | // drops out, LINE becomes a space after `separator` — so args get | |
| 111 | // "a, b" while `tight` generics get "T,U"); otherwise breaks with | |
| 112 | // each item on its own 4-space-indented line and the close returning | |
| 113 | // to the outer indent. | |
| 114 | _emit_wrappable(b: DOC_BUILDER, w: SignaturePart.WRAPPABLE) static is | |
| 115 | b.text(w.open); | |
| 116 | ||
| 117 | if w.items.count == 0 then | |
| 118 | b.text(w.close); | |
| 119 | return; | |
| 120 | fi | |
| 121 | ||
| 122 | b.begin_group(); | |
| 123 | b.begin_nest(4); | |
| 124 | b.soft_line(); | |
| 125 | ||
| 126 | let first mut = true; | |
| 127 | for item in w.items do | |
| 128 | if !first then | |
| 129 | b.text(w.separator); | |
| 130 | if w.tight then | |
| 131 | b.soft_line(); | |
| 132 | else | |
| 133 | b.line(); | |
| 134 | fi | |
| 135 | fi | |
| 136 | first = false; | |
| 137 | _emit(b, item); | |
| 138 | od | |
| 139 | ||
| 140 | b.end_nest(); | |
| 141 | b.soft_line(); | |
| 142 | b.end_group(); | |
| 143 | b.text(w.close); | |
| 144 | si | |
| 145 | si | |
| 146 | si |