@promptctl/rich-js 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/THIRD-PARTY-NOTICES +29 -1
- package/dist/core/anchor.d.ts +5 -0
- package/dist/core/anchor.js +11 -4
- package/dist/core/animation.d.ts +243 -0
- package/dist/core/animation.js +375 -0
- package/dist/core/ansi.js +2 -2
- package/dist/core/box.d.ts +7 -7
- package/dist/core/cell-widths.d.ts +9 -0
- package/dist/core/cell-widths.js +140 -0
- package/dist/core/cells.d.ts +40 -24
- package/dist/core/cells.js +151 -66
- package/dist/core/clock.d.ts +2 -2
- package/dist/core/clock.js +6 -4
- package/dist/core/color.d.ts +27 -10
- package/dist/core/color.js +21 -29
- package/dist/core/console.d.ts +28 -17
- package/dist/core/console.js +48 -43
- package/dist/core/easing.d.ts +140 -10
- package/dist/core/easing.js +328 -50
- package/dist/core/emoji.d.ts +1 -1
- package/dist/core/emoji.js +1 -2
- package/dist/core/export-html.js +2 -1
- package/dist/core/export-lines.js +3 -3
- package/dist/core/highlighter.d.ts +9 -8
- package/dist/core/highlighter.js +3 -2
- package/dist/core/json.js +3 -2
- package/dist/core/markup.js +16 -12
- package/dist/core/measure.js +3 -3
- package/dist/core/memo.d.ts +11 -2
- package/dist/core/memo.js +16 -0
- package/dist/core/oklch.js +17 -6
- package/dist/core/osc8.d.ts +1 -1
- package/dist/core/osc8.js +4 -5
- package/dist/core/paint.d.ts +2 -1
- package/dist/core/paint.js +4 -3
- package/dist/core/pattern.d.ts +21 -0
- package/dist/core/pattern.js +21 -0
- package/dist/core/place.js +2 -1
- package/dist/core/pretty.js +11 -12
- package/dist/core/protocol.d.ts +136 -21
- package/dist/core/protocol.js +203 -16
- package/dist/core/render.d.ts +15 -2
- package/dist/core/render.js +27 -6
- package/dist/core/segment.d.ts +31 -6
- package/dist/core/segment.js +68 -53
- package/dist/core/spinnerData.d.ts +1 -1
- package/dist/core/state.d.ts +53 -0
- package/dist/core/state.js +82 -0
- package/dist/core/strip.d.ts +11 -13
- package/dist/core/strip.js +126 -113
- package/dist/core/style.d.ts +47 -14
- package/dist/core/style.js +74 -34
- package/dist/core/text.d.ts +5 -8
- package/dist/core/text.js +133 -133
- package/dist/core/timeline.d.ts +109 -0
- package/dist/core/timeline.js +180 -0
- package/dist/core/tween.d.ts +49 -0
- package/dist/core/tween.js +71 -0
- package/dist/core/view.d.ts +92 -0
- package/dist/core/view.js +83 -0
- package/dist/host/app.d.ts +49 -21
- package/dist/host/app.js +72 -38
- package/dist/host/input.js +3 -4
- package/dist/index.d.ts +30 -15
- package/dist/index.js +20 -6
- package/dist/node/traceback.js +4 -3
- package/dist/renderables/change.d.ts +42 -0
- package/dist/renderables/change.js +112 -0
- package/dist/renderables/columns.d.ts +1 -1
- package/dist/renderables/columns.js +3 -3
- package/dist/renderables/constrain.js +2 -1
- package/dist/renderables/effect.d.ts +155 -92
- package/dist/renderables/effect.js +203 -136
- package/dist/renderables/effects.d.ts +137 -24
- package/dist/renderables/effects.js +276 -113
- package/dist/renderables/embed.d.ts +1 -1
- package/dist/renderables/embed.js +18 -9
- package/dist/renderables/flexStrip.js +2 -1
- package/dist/renderables/frames.d.ts +45 -43
- package/dist/renderables/frames.js +57 -45
- package/dist/renderables/glyph-effect.d.ts +158 -0
- package/dist/renderables/glyph-effect.js +237 -0
- package/dist/renderables/glyph-effects.d.ts +147 -0
- package/dist/renderables/glyph-effects.js +283 -0
- package/dist/renderables/group.d.ts +1 -1
- package/dist/renderables/group.js +2 -2
- package/dist/renderables/html-entities.d.ts +8 -0
- package/dist/renderables/html-entities.js +386 -0
- package/dist/renderables/layout.d.ts +10 -9
- package/dist/renderables/layout.js +42 -35
- package/dist/renderables/lifecycle.d.ts +77 -0
- package/dist/renderables/lifecycle.js +132 -0
- package/dist/renderables/list-view.d.ts +2 -2
- package/dist/renderables/list-view.js +11 -8
- package/dist/renderables/live.d.ts +36 -21
- package/dist/renderables/live.js +89 -42
- package/dist/renderables/markdown-parse.d.ts +109 -0
- package/dist/renderables/markdown-parse.js +1427 -0
- package/dist/renderables/markdown-url.d.ts +19 -0
- package/dist/renderables/markdown-url.js +314 -0
- package/dist/renderables/markdown.d.ts +5 -7
- package/dist/renderables/markdown.js +356 -683
- package/dist/renderables/padding.d.ts +7 -20
- package/dist/renderables/padding.js +29 -50
- package/dist/renderables/panel.d.ts +30 -32
- package/dist/renderables/panel.js +65 -109
- package/dist/renderables/progress.d.ts +83 -32
- package/dist/renderables/progress.js +186 -90
- package/dist/renderables/progressBar.d.ts +22 -12
- package/dist/renderables/progressBar.js +33 -10
- package/dist/renderables/prompt.js +3 -2
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +25 -0
- package/dist/renderables/scroll.d.ts +50 -0
- package/dist/renderables/scroll.js +106 -0
- package/dist/renderables/spinner.d.ts +57 -24
- package/dist/renderables/spinner.js +101 -49
- package/dist/renderables/status.js +1 -1
- package/dist/renderables/syntax.d.ts +10 -9
- package/dist/renderables/syntax.js +5 -3
- package/dist/renderables/table.d.ts +48 -46
- package/dist/renderables/table.js +170 -208
- package/dist/renderables/traceback.d.ts +3 -3
- package/dist/renderables/traceback.js +12 -10
- package/dist/renderables/tree.d.ts +28 -25
- package/dist/renderables/tree.js +108 -58
- package/dist/renderables/viewport.d.ts +77 -38
- package/dist/renderables/viewport.js +156 -72
- package/dist/template-bindings/animation-funcs.d.ts +78 -9
- package/dist/template-bindings/animation-funcs.js +132 -14
- package/dist/template-bindings/index.d.ts +5 -5
- package/dist/template-bindings/index.js +7 -6
- package/dist/themes/ramp.d.ts +62 -4
- package/dist/themes/ramp.js +82 -4
- package/dist/themes/registry.js +5 -7
- package/dist/widgets/button.js +3 -2
- package/dist/widgets/checkbox.js +4 -3
- package/dist/widgets/dropdown.d.ts +3 -1
- package/dist/widgets/dropdown.js +20 -15
- package/dist/widgets/event-router.js +4 -2
- package/dist/widgets/focus-manager.d.ts +23 -14
- package/dist/widgets/focus-manager.js +34 -33
- package/dist/widgets/hit.d.ts +6 -0
- package/dist/widgets/hit.js +8 -1
- package/dist/widgets/index.d.ts +1 -1
- package/dist/widgets/observable.d.ts +23 -0
- package/dist/widgets/observable.js +43 -0
- package/dist/widgets/slider.d.ts +1 -1
- package/dist/widgets/slider.js +12 -3
- package/dist/widgets/static-item.d.ts +1 -1
- package/dist/widgets/static-item.js +6 -7
- package/dist/widgets/text-input.d.ts +53 -47
- package/dist/widgets/text-input.js +456 -399
- package/dist/widgets/themed-widget.js +2 -1
- package/dist/widgets/toggle.js +4 -3
- package/dist/widgets/types.d.ts +10 -4
- package/dist/widgets/types.js +8 -2
- package/dist/widgets/widget-app.d.ts +32 -6
- package/dist/widgets/widget-app.js +74 -27
- package/dist/widgets/widget-base.d.ts +2 -2
- package/dist/widgets/widget-base.js +29 -21
- package/package.json +9 -5
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* the interval; the beat, the width every frame draws at and which frame `t`
|
|
9
9
|
* lands on are the frames primitive's, so a spinner is one more sequence on
|
|
10
10
|
* it and not a second way of turning frames. Custom frames are a `FrameSequence`
|
|
11
|
-
*
|
|
11
|
+
* placed directly. [LAW:one-source-of-truth]
|
|
12
12
|
*
|
|
13
13
|
* It is built once and asked for each frame, as Rich's `Spinner` is built
|
|
14
14
|
* once and has `render(time)` called on it: the name is parsed and the
|
|
@@ -19,45 +19,47 @@
|
|
|
19
19
|
* the beat, the width, each frame padded to it — is made there too, so a
|
|
20
20
|
* drawn frame costs only finding which one `t` lands on.
|
|
21
21
|
*
|
|
22
|
-
* [LAW:no-ambient-temporal-coupling]
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `
|
|
27
|
-
*
|
|
28
|
-
* time, as `App` and `Task.time` hand it, where Rich's spinner counts from
|
|
22
|
+
* [LAW:no-ambient-temporal-coupling] Placed, a spinner draws at the time of
|
|
23
|
+
* the frame it is drawn in, and nothing here reads a clock. Whoever draws
|
|
24
|
+
* frames hands each one its time (`core/clock.ts`) — a `Progress` the
|
|
25
|
+
* moment its tasks are drawn at, a `Live` the frame's time on its clock — as
|
|
26
|
+
* Rich's `Spinner.render` is handed `console.get_time()` or `task.get_time()`. `t` is the clock's
|
|
27
|
+
* time, as a host writes it on the frame, where Rich's spinner counts from
|
|
29
28
|
* its first draw: here nothing is kept between frames, so the same `t` draws
|
|
30
29
|
* the same bytes at one redraw a second or sixty, two spinners on one clock
|
|
31
30
|
* agree at every `t`, a `Progress` on a hand-moved clock turns its spinners
|
|
32
31
|
* on that clock, and a draw costs the same at `t = 0` and `t = 10⁶`.
|
|
33
32
|
*
|
|
34
|
-
* `speed`
|
|
35
|
-
*
|
|
36
|
-
* the interval
|
|
37
|
-
* the time
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
33
|
+
* `speed` is a `Timeline` (`core/timeline.ts`) playing from `t = 0` at
|
|
34
|
+
* that rate, as Rich scales the time, `t × speed` into the beat: the frames
|
|
35
|
+
* are held for the interval the data gives, and the spinner is asked for the
|
|
36
|
+
* frame at the time the timeline maps the host's to. [LAW:one-source-of-truth]
|
|
37
|
+
* When the frame next changes (`Style.hold`) is a time on the host's
|
|
38
|
+
* timeline, and the sequence answers on its own, so the timeline carries the
|
|
39
|
+
* answer back. `at` hands over the frame alone, as every animation's does;
|
|
40
|
+
* placed, a spinner draws at the frame's time and stamps its cells with that
|
|
41
|
+
* answer for the frames the output draws. Backwards, the frames count down from the last at `t = 0`,
|
|
42
|
+
* a negative time landing in the last frame of the pass before, where Rich's
|
|
43
|
+
* `int()` truncation holds frame 0 for a first interval and then counts
|
|
44
|
+
* down: a departure, made so a negative speed is the one timeline run
|
|
45
|
+
* backwards and not a second rule about time. A speed of zero would hold a
|
|
46
|
+
* frame forever, which a paused timeline is and a rate is not, and the
|
|
47
|
+
* timeline refuses it.
|
|
47
48
|
*/
|
|
49
|
+
import { Animation, frameTimeOf, holdOf, shownAt, stampFrame } from "../core/animation.js";
|
|
48
50
|
import { Segment } from "../core/segment.js";
|
|
49
51
|
import { NULL_STYLE } from "../core/style.js";
|
|
50
52
|
import { RichText } from "../core/text.js";
|
|
51
53
|
import { SPINNERS, DEFAULT_SPINNER, LINE_SPINNER } from "../core/spinnerData.js";
|
|
52
|
-
import {
|
|
54
|
+
import { Timeline } from "../core/timeline.js";
|
|
55
|
+
import { drawable, measure, render } from "../core/protocol.js";
|
|
53
56
|
import { EmbeddedText } from "./embed.js";
|
|
54
57
|
import { FrameSequence } from "./frames.js";
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return { frames: new FrameSequence(speed < 0 ? frames.reverse() : frames), glyphs: data.frames.join("") };
|
|
58
|
+
import { finiteTime } from "../core/easing.js";
|
|
59
|
+
import { State } from "../core/state.js";
|
|
60
|
+
const sequence = (data, style, timeline) => {
|
|
61
|
+
const frames = new FrameSequence(data.frames.map((frame) => ({ text: new RichText("", { end: "" }).append(frame, style), seconds: data.interval / 1000 })));
|
|
62
|
+
return { frames, played: Timeline.run(timeline, frames), glyphs: data.frames.join("") };
|
|
61
63
|
};
|
|
62
64
|
const sequenceGlyphs = ({ glyphs }) => glyphs;
|
|
63
65
|
export class Spinner {
|
|
@@ -71,12 +73,26 @@ export class Spinner {
|
|
|
71
73
|
* whatever the console says — and a `RichText` changed in place draws as
|
|
72
74
|
* changed, as the `Text` Rich keeps does. [LAW:single-enforcer]
|
|
73
75
|
*/
|
|
74
|
-
text
|
|
76
|
+
get text() {
|
|
77
|
+
return this._text.get();
|
|
78
|
+
}
|
|
79
|
+
set text(value) {
|
|
80
|
+
this._text.set(value);
|
|
81
|
+
}
|
|
82
|
+
// Rich's `Spinner.update` sets the text after it is built.
|
|
83
|
+
_text;
|
|
75
84
|
_data;
|
|
76
85
|
// The named data's frames, and the `line` spinner's that stand in for them
|
|
77
86
|
// on an ASCII-only output: prepared once, which is drawn chosen at `at`.
|
|
78
87
|
_named;
|
|
79
88
|
_ascii;
|
|
89
|
+
/**
|
|
90
|
+
* The spinner drawn at the frame's time, its cells saying the hold it is in
|
|
91
|
+
* on the host's clock (`stampFrame`) — exactly, for the frames the output
|
|
92
|
+
* draws, where `nextChange` must answer for both. Under reduced motion it
|
|
93
|
+
* is the spinner reduced, which never turns (`shownAt`).
|
|
94
|
+
*/
|
|
95
|
+
_placed;
|
|
80
96
|
constructor(name, text = "", options) {
|
|
81
97
|
const spinnerName = name ?? DEFAULT_SPINNER;
|
|
82
98
|
const data = SPINNERS[spinnerName];
|
|
@@ -84,18 +100,21 @@ export class Spinner {
|
|
|
84
100
|
throw new Error(`Unknown spinner: "${spinnerName}"`);
|
|
85
101
|
}
|
|
86
102
|
const speed = options?.speed ?? 1;
|
|
87
|
-
// [LAW:
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
throw new RangeError(`Spinner speed must be finite and not zero, got ${speed}`);
|
|
91
|
-
}
|
|
103
|
+
// [LAW:single-enforcer] A rate is finite and not zero, and the timeline
|
|
104
|
+
// refuses one that is not where it is built.
|
|
105
|
+
const timeline = Timeline.play(0, speed);
|
|
92
106
|
this.name = spinnerName;
|
|
93
|
-
this.
|
|
107
|
+
this._text = new State(text);
|
|
94
108
|
this.speed = speed;
|
|
95
109
|
this.style = options?.style ?? NULL_STYLE;
|
|
96
110
|
this._data = data;
|
|
97
|
-
this._named = sequence(data, this.style,
|
|
98
|
-
this._ascii = sequence(LINE_SPINNER, this.style,
|
|
111
|
+
this._named = sequence(data, this.style, timeline);
|
|
112
|
+
this._ascii = sequence(LINE_SPINNER, this.style, timeline);
|
|
113
|
+
this._placed = this._frame((played, options) => {
|
|
114
|
+
const t = frameTimeOf(options);
|
|
115
|
+
const shown = shownAt(played, t, options);
|
|
116
|
+
return { value: shown.value, hold: holdOf(shown, t) };
|
|
117
|
+
});
|
|
99
118
|
}
|
|
100
119
|
get frames() {
|
|
101
120
|
return this._data.frames;
|
|
@@ -109,27 +128,59 @@ export class Spinner {
|
|
|
109
128
|
* the line wrapped to the width and ended, as the reference's `Text` is —
|
|
110
129
|
* printed, stacked in a `Group`, or laid out in a `Progress` cell alike.
|
|
111
130
|
* The frames this output can draw are chosen where the output is known.
|
|
131
|
+
* The frame says nothing of when it turns — `t` may be a time a timeline
|
|
132
|
+
* mapped — so placed, the spinner itself stamps it.
|
|
112
133
|
*/
|
|
113
134
|
at(t) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
135
|
+
finiteTime("Spinner.at", t);
|
|
136
|
+
return this._frame((played) => ({ value: played.at(t) }));
|
|
137
|
+
}
|
|
138
|
+
/** The spinner with its motion taken out: its first frame and the label, which never turn, so any time answers. */
|
|
139
|
+
reduced() {
|
|
140
|
+
return Animation.constant(this._frame((played) => ({ value: played.reduced().at(0) })));
|
|
141
|
+
}
|
|
142
|
+
render(options) {
|
|
143
|
+
return render(this._placed, options);
|
|
144
|
+
}
|
|
145
|
+
measure(options) {
|
|
146
|
+
return measure(this._placed, options);
|
|
147
|
+
}
|
|
148
|
+
/** The frame `choose` picks from the frames the output can draw, and the label, saying the hold it is in if `choose` says. */
|
|
149
|
+
_frame(choose) {
|
|
150
|
+
const drawn = (options) => drawable(options, this._named, this._ascii, sequenceGlyphs);
|
|
118
151
|
return {
|
|
119
|
-
render: (options) =>
|
|
152
|
+
render: (options) => {
|
|
153
|
+
const { value, hold } = choose(drawn(options).played, options);
|
|
154
|
+
const segments = this._render(value, options);
|
|
155
|
+
return hold === undefined ? segments : stampFrame(segments, hold);
|
|
156
|
+
},
|
|
120
157
|
// Measured at the frames' one width, the widest frame's, so a column
|
|
121
158
|
// holding it never narrows as it turns, and never offered less than
|
|
122
159
|
// that frame: a frame is one picture, which the reference's `Text`
|
|
123
160
|
// measure would let wrap apart at its spaces.
|
|
124
161
|
measure: (options) => {
|
|
125
|
-
const
|
|
126
|
-
const { minimum, maximum } = this._line(
|
|
127
|
-
return { minimum: Math.max(minimum, Math.min(
|
|
162
|
+
const { frames, played } = drawn(options);
|
|
163
|
+
const { minimum, maximum } = measure(this._line(choose(played, options).value, options), options);
|
|
164
|
+
return { minimum: Math.max(minimum, Math.min(frames.width, maximum)), maximum };
|
|
128
165
|
},
|
|
129
166
|
};
|
|
130
167
|
}
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
/**
|
|
169
|
+
* When the spinner drawn at `t` next turns, on the host's clock: the
|
|
170
|
+
* sooner of its two sequences' answers, since which one is drawn is the
|
|
171
|
+
* output's to choose and the line spinner that stands in on an ASCII-only
|
|
172
|
+
* output keeps its own interval. An early answer, on the output that draws
|
|
173
|
+
* the other one, which the contract allows; the drawn cells carry the exact one.
|
|
174
|
+
*/
|
|
175
|
+
nextChange(t) {
|
|
176
|
+
return Math.min(this._named.played.nextChange(t), this._ascii.played.nextChange(t));
|
|
177
|
+
}
|
|
178
|
+
/** When the spinner drawn at `t` began to be drawn: the later of its two sequences' answers, by the same argument. */
|
|
179
|
+
lastChange(t) {
|
|
180
|
+
return Math.max(this._named.played.lastChange(t), this._ascii.played.lastChange(t));
|
|
181
|
+
}
|
|
182
|
+
*_render(frame, options) {
|
|
183
|
+
yield* render(this._line(frame, options), options);
|
|
133
184
|
yield Segment.line();
|
|
134
185
|
}
|
|
135
186
|
/**
|
|
@@ -139,8 +190,9 @@ export class Spinner {
|
|
|
139
190
|
* styles nothing — is no label, not a trailing space, as an empty `Text` is
|
|
140
191
|
* falsy there.
|
|
141
192
|
*/
|
|
142
|
-
_line(
|
|
143
|
-
|
|
193
|
+
_line(frame, options) {
|
|
194
|
+
// The frame alone, saying nothing of when it turns: placed, the spinner stamps it.
|
|
195
|
+
const line = new RichText("", { end: "" }).append(frame);
|
|
144
196
|
// Unhighlighted: Rich's label is a `Text` from the moment it is handed over, and a `Text` is never highlighted.
|
|
145
197
|
const label = new EmbeddedText(this.text).text({ ...options, highlight: false });
|
|
146
198
|
if (label.plain !== "")
|
|
@@ -20,7 +20,7 @@ export class Status {
|
|
|
20
20
|
speed: options?.speed,
|
|
21
21
|
style: options?.spinnerStyle ?? "status.spinner",
|
|
22
22
|
});
|
|
23
|
-
this._live = new Live(
|
|
23
|
+
this._live = new Live(this._spinner, {
|
|
24
24
|
console: this._console,
|
|
25
25
|
clock: options?.clock,
|
|
26
26
|
transient: true,
|
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Segment } from "../core/segment.js";
|
|
11
11
|
import { type Measurable, type Renderable, type RenderOptions } from "../core/protocol.js";
|
|
12
|
+
import { type Pattern } from "../core/pattern.js";
|
|
12
13
|
export interface SyntaxOptions {
|
|
13
14
|
lineNumbers?: boolean;
|
|
14
15
|
startLine?: number;
|
|
15
|
-
lineRange?: [number, number];
|
|
16
|
-
highlightLines?:
|
|
16
|
+
lineRange?: readonly [number, number];
|
|
17
|
+
highlightLines?: ReadonlySet<number>;
|
|
17
18
|
wordWrap?: boolean;
|
|
18
19
|
tabSize?: number;
|
|
19
20
|
}
|
|
@@ -24,11 +25,11 @@ export interface SyntaxOptions {
|
|
|
24
25
|
* render; `"text"` is the grammar with no rules.
|
|
25
26
|
*/
|
|
26
27
|
declare const GRAMMARS: {
|
|
27
|
-
javascript:
|
|
28
|
-
typescript:
|
|
29
|
-
python:
|
|
30
|
-
bash:
|
|
31
|
-
text:
|
|
28
|
+
readonly javascript: Pattern;
|
|
29
|
+
readonly typescript: Pattern;
|
|
30
|
+
readonly python: Pattern;
|
|
31
|
+
readonly bash: Pattern;
|
|
32
|
+
readonly text: Pattern;
|
|
32
33
|
};
|
|
33
34
|
export type SyntaxLanguage = keyof typeof GRAMMARS;
|
|
34
35
|
export declare class Syntax implements Renderable, Measurable {
|
|
@@ -36,8 +37,8 @@ export declare class Syntax implements Renderable, Measurable {
|
|
|
36
37
|
readonly language: SyntaxLanguage;
|
|
37
38
|
readonly lineNumbers: boolean;
|
|
38
39
|
readonly startLine: number;
|
|
39
|
-
readonly lineRange: [number, number] | undefined;
|
|
40
|
-
readonly highlightLines:
|
|
40
|
+
readonly lineRange: readonly [number, number] | undefined;
|
|
41
|
+
readonly highlightLines: ReadonlySet<number>;
|
|
41
42
|
readonly wordWrap: boolean;
|
|
42
43
|
readonly tabSize: number;
|
|
43
44
|
/** The code with its tabs expanded, as lines — what both render and measure read. */
|
|
@@ -11,6 +11,8 @@ import { cellLen, expandTabs, parseTabSize } from "../core/cells.js";
|
|
|
11
11
|
import { Segment } from "../core/segment.js";
|
|
12
12
|
import { RichText } from "../core/text.js";
|
|
13
13
|
import { drawable, groundedStyle, getStyle, withBoundedWidth, withCellWidth, } from "../core/protocol.js";
|
|
14
|
+
import { render } from "../core/protocol.js";
|
|
15
|
+
import { pattern } from "../core/pattern.js";
|
|
14
16
|
/**
|
|
15
17
|
* One regex per grammar, each token a named alternative. A single left-to-right
|
|
16
18
|
* scan is what keeps a `//` inside a string from starting a comment, and a
|
|
@@ -19,7 +21,7 @@ import { drawable, groundedStyle, getStyle, withBoundedWidth, withCellWidth, } f
|
|
|
19
21
|
* `(?!)` never matches, so a grammar with no rules is still a regex.
|
|
20
22
|
*/
|
|
21
23
|
function grammar(rules) {
|
|
22
|
-
return new RegExp([...rules.map(([token, source]) => `(?<${token}>${source})`), "(?!)"].join("|")
|
|
24
|
+
return pattern(new RegExp([...rules.map(([token, source]) => `(?<${token}>${source})`), "(?!)"].join("|")));
|
|
23
25
|
}
|
|
24
26
|
// Not after a `.`: `map.get` and `node.type` are properties, not keywords.
|
|
25
27
|
const words = (list) => `(?<!\\.)\\b(?:${list.split(" ").join("|")})\\b`;
|
|
@@ -119,7 +121,7 @@ export class Syntax {
|
|
|
119
121
|
// Tokenized whole and then sliced, so a block comment or a triple-quoted
|
|
120
122
|
// string that spans lines is one token even when the range starts inside it.
|
|
121
123
|
const text = new RichText(this._lines.join("\n"), { end: "" });
|
|
122
|
-
for (const match of text.plain.matchAll(GRAMMARS[this.language])) {
|
|
124
|
+
for (const match of text.plain.matchAll(new RegExp(GRAMMARS[this.language].source, "g"))) {
|
|
123
125
|
const [token] = Object.entries(match.groups).find(([, value]) => value !== undefined);
|
|
124
126
|
text.stylize(`syntax.${token}`, match.index, match.index + match[0].length);
|
|
125
127
|
}
|
|
@@ -140,7 +142,7 @@ export class Syntax {
|
|
|
140
142
|
const lineNo = first + i;
|
|
141
143
|
const style = this.highlightLines.has(lineNo) ? highlightStyle : numberStyle;
|
|
142
144
|
// Terminated, so an empty line is one empty row rather than none.
|
|
143
|
-
const rows = Segment.splitLines([...textLines[i]
|
|
145
|
+
const rows = Segment.splitLines([...render(textLines[i], lineOptions), Segment.line()]);
|
|
144
146
|
for (let row = 0; row < rows.length; row++) {
|
|
145
147
|
if (this.lineNumbers) {
|
|
146
148
|
// A wrapped line's continuation rows keep the gutter, unnumbered.
|
|
@@ -9,40 +9,39 @@ import type { PaddingDimensions } from "./padding.js";
|
|
|
9
9
|
import type { Renderable, Measurable, OverflowMethod, RenderOptions } from "../core/protocol.js";
|
|
10
10
|
export interface ColumnOptions {
|
|
11
11
|
/** Anything a cell can hold, as Rich's `RenderableType` header is. */
|
|
12
|
-
header?: string | RichText | Renderable;
|
|
13
|
-
footer?: string | RichText | Renderable;
|
|
14
|
-
headerStyle?: string | Style;
|
|
15
|
-
footerStyle?: string | Style;
|
|
16
|
-
style?: string | Style;
|
|
17
|
-
justify?: "left" | "center" | "right" | "full";
|
|
18
|
-
width?: number;
|
|
19
|
-
minWidth?: number;
|
|
20
|
-
maxWidth?: number;
|
|
21
|
-
ratio?: number;
|
|
22
|
-
noWrap?: boolean;
|
|
23
|
-
overflow?: OverflowMethod;
|
|
12
|
+
readonly header?: string | RichText | Renderable;
|
|
13
|
+
readonly footer?: string | RichText | Renderable;
|
|
14
|
+
readonly headerStyle?: string | Style;
|
|
15
|
+
readonly footerStyle?: string | Style;
|
|
16
|
+
readonly style?: string | Style;
|
|
17
|
+
readonly justify?: "left" | "center" | "right" | "full";
|
|
18
|
+
readonly width?: number;
|
|
19
|
+
readonly minWidth?: number;
|
|
20
|
+
readonly maxWidth?: number;
|
|
21
|
+
readonly ratio?: number;
|
|
22
|
+
readonly noWrap?: boolean;
|
|
23
|
+
readonly overflow?: OverflowMethod;
|
|
24
24
|
/**
|
|
25
25
|
* Whether the column's string cells — its header and footer among them — are
|
|
26
26
|
* drawn with the console's highlighter, as Rich's `Column(highlight=)`.
|
|
27
27
|
* `false` for a `Column` built alone; `addColumn` takes the table's.
|
|
28
28
|
*/
|
|
29
|
-
highlight?: boolean;
|
|
29
|
+
readonly highlight?: boolean;
|
|
30
30
|
}
|
|
31
31
|
export declare class Column {
|
|
32
|
-
private _header;
|
|
33
|
-
private _footer;
|
|
34
|
-
headerStyle: string | Style;
|
|
35
|
-
footerStyle: string | Style;
|
|
36
|
-
style: string | Style;
|
|
37
|
-
justify: "left" | "center" | "right" | "full";
|
|
38
|
-
width: number | undefined;
|
|
39
|
-
minWidth: number | undefined;
|
|
40
|
-
maxWidth: number | undefined;
|
|
41
|
-
ratio: number | undefined;
|
|
42
|
-
noWrap: boolean;
|
|
43
|
-
overflow: OverflowMethod;
|
|
44
|
-
highlight: boolean;
|
|
45
|
-
private _cells;
|
|
32
|
+
private readonly _header;
|
|
33
|
+
private readonly _footer;
|
|
34
|
+
readonly headerStyle: string | Style;
|
|
35
|
+
readonly footerStyle: string | Style;
|
|
36
|
+
readonly style: string | Style;
|
|
37
|
+
readonly justify: "left" | "center" | "right" | "full";
|
|
38
|
+
readonly width: number | undefined;
|
|
39
|
+
readonly minWidth: number | undefined;
|
|
40
|
+
readonly maxWidth: number | undefined;
|
|
41
|
+
readonly ratio: number | undefined;
|
|
42
|
+
readonly noWrap: boolean;
|
|
43
|
+
readonly overflow: OverflowMethod;
|
|
44
|
+
readonly highlight: boolean;
|
|
46
45
|
constructor(options?: ColumnOptions);
|
|
47
46
|
/**
|
|
48
47
|
* The two stamped cells, stamped on assignment rather than at the constructor.
|
|
@@ -66,10 +65,6 @@ export declare class Column {
|
|
|
66
65
|
get footer(): Renderable & Partial<Measurable>;
|
|
67
66
|
set footer(content: ColumnOptions["footer"]);
|
|
68
67
|
get flexible(): boolean;
|
|
69
|
-
/** @internal */
|
|
70
|
-
addCell(cell: Renderable): void;
|
|
71
|
-
/** @internal */
|
|
72
|
-
getCells(): Renderable[];
|
|
73
68
|
copy(): Column;
|
|
74
69
|
}
|
|
75
70
|
export interface TableOptions {
|
|
@@ -94,13 +89,13 @@ export interface TableOptions {
|
|
|
94
89
|
captionJustify?: "left" | "center" | "right" | "full";
|
|
95
90
|
width?: number;
|
|
96
91
|
minWidth?: number;
|
|
97
|
-
rowStyles?: string[];
|
|
92
|
+
rowStyles?: readonly string[];
|
|
98
93
|
/** The `highlight` every column `addColumn` makes takes unless told otherwise (default: `false`). */
|
|
99
94
|
highlight?: boolean;
|
|
100
95
|
}
|
|
101
96
|
export declare class Table implements Renderable, Measurable {
|
|
102
|
-
private _columns;
|
|
103
|
-
private _rows;
|
|
97
|
+
private readonly _columns;
|
|
98
|
+
private readonly _rows;
|
|
104
99
|
readonly box: Box | null;
|
|
105
100
|
private readonly _title;
|
|
106
101
|
private readonly _caption;
|
|
@@ -109,7 +104,7 @@ export declare class Table implements Renderable, Measurable {
|
|
|
109
104
|
readonly showFooter: boolean;
|
|
110
105
|
readonly showLines: boolean;
|
|
111
106
|
readonly showEdge: boolean;
|
|
112
|
-
readonly padding: [number, number, number, number];
|
|
107
|
+
readonly padding: readonly [number, number, number, number];
|
|
113
108
|
readonly collapsePadding: boolean;
|
|
114
109
|
readonly padEdge: boolean;
|
|
115
110
|
readonly style: string | Style;
|
|
@@ -122,12 +117,12 @@ export declare class Table implements Renderable, Measurable {
|
|
|
122
117
|
readonly captionJustify: "left" | "center" | "right" | "full";
|
|
123
118
|
readonly tableWidth: number | undefined;
|
|
124
119
|
readonly minWidth: number | undefined;
|
|
125
|
-
readonly rowStyles: string[];
|
|
120
|
+
readonly rowStyles: readonly string[];
|
|
126
121
|
readonly highlight: boolean;
|
|
127
122
|
get title(): (Renderable & Measurable) | undefined;
|
|
128
123
|
get caption(): (Renderable & Measurable) | undefined;
|
|
129
124
|
constructor(options?: TableOptions);
|
|
130
|
-
get columns(): Column[];
|
|
125
|
+
get columns(): readonly Column[];
|
|
131
126
|
get rowCount(): number;
|
|
132
127
|
addColumn(header?: ColumnOptions["header"], options?: ColumnOptions): this;
|
|
133
128
|
addRow(...cells: unknown[]): this;
|
|
@@ -183,8 +178,11 @@ export declare class Table implements Renderable, Measurable {
|
|
|
183
178
|
* mode gets its own path through the width division.
|
|
184
179
|
*
|
|
185
180
|
* Cells are measured against the width left once the frame is paid, as the
|
|
186
|
-
* reference's `_measure_column` measures them. Padding
|
|
187
|
-
*
|
|
181
|
+
* reference's `_measure_column` measures them. Padding is part of every
|
|
182
|
+
* width the demand names, because the reference measures a cell wrapped in
|
|
183
|
+
* its `Padding`, and each sizing adds it where the reference does — a
|
|
184
|
+
* rowless column's minimum is the one width it never reaches. The floor is
|
|
185
|
+
* laid over whichever sizing the column has.
|
|
188
186
|
*/
|
|
189
187
|
private _columnDemands;
|
|
190
188
|
private _columnSizing;
|
|
@@ -198,6 +196,7 @@ export declare class Table implements Renderable, Measurable {
|
|
|
198
196
|
*/
|
|
199
197
|
private _columnPadding;
|
|
200
198
|
private _rowPadding;
|
|
199
|
+
private _rowCells;
|
|
201
200
|
/**
|
|
202
201
|
* Every cell column `index` draws, in draw order.
|
|
203
202
|
*
|
|
@@ -211,19 +210,22 @@ export declare class Table implements Renderable, Measurable {
|
|
|
211
210
|
*/
|
|
212
211
|
private _columnCells;
|
|
213
212
|
/**
|
|
214
|
-
* The widest any cell in a column draws,
|
|
215
|
-
* without cutting one — the reference's
|
|
216
|
-
* when the column has no cell to measure.
|
|
217
|
-
*
|
|
218
|
-
* same answer.
|
|
213
|
+
* The widest any cell in a column draws, its padding included, and the
|
|
214
|
+
* narrowest the column can be without cutting one — the reference's
|
|
215
|
+
* `_measure_column` — or `undefined` when the column has no cell to measure.
|
|
216
|
+
* A column whose cells draw nothing measures its padding — a gutter asks for
|
|
217
|
+
* that and nothing else — which is not the same answer.
|
|
218
|
+
* [LAW:types-are-the-program]
|
|
219
219
|
*/
|
|
220
220
|
private _cellRange;
|
|
221
221
|
/**
|
|
222
|
-
* A natural width held to the column's own `minWidth`/`maxWidth
|
|
222
|
+
* A natural width held to the column's own `minWidth`/`maxWidth`, each
|
|
223
|
+
* widened by the column's `pad`, as the reference clamps a padded
|
|
224
|
+
* measurement to `min_width + padding_width`.
|
|
223
225
|
*
|
|
224
226
|
* A bound is a count of cells, read by the rule every width is read by: NaN
|
|
225
227
|
* and a negative are zero cells, so a NaN floor bounds nothing and a NaN
|
|
226
|
-
* ceiling
|
|
228
|
+
* ceiling holds the column to its padding alone, as `width: NaN` does. A bare `Math.max` returns
|
|
227
229
|
* NaN instead, and the column vanishes. [LAW:one-source-of-truth]
|
|
228
230
|
*/
|
|
229
231
|
private _bounded;
|