@teaui/core 1.1.4
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/.dist/Buffer.d.ts +21 -0
- package/.dist/Buffer.js +140 -0
- package/.dist/Buffer.js.map +1 -0
- package/.dist/Color.d.ts +7 -0
- package/.dist/Color.js +39 -0
- package/.dist/Color.js.map +1 -0
- package/.dist/Container.d.ts +21 -0
- package/.dist/Container.js +138 -0
- package/.dist/Container.js.map +1 -0
- package/.dist/Screen.d.ts +54 -0
- package/.dist/Screen.js +278 -0
- package/.dist/Screen.js.map +1 -0
- package/.dist/Style.d.ts +46 -0
- package/.dist/Style.js +433 -0
- package/.dist/Style.js.map +1 -0
- package/.dist/System.d.ts +13 -0
- package/.dist/System.js +26 -0
- package/.dist/System.js.map +1 -0
- package/.dist/Theme.d.ts +56 -0
- package/.dist/Theme.js +157 -0
- package/.dist/Theme.js.map +1 -0
- package/.dist/UI.d.ts +2 -0
- package/.dist/UI.js +28 -0
- package/.dist/UI.js.map +1 -0
- package/.dist/View.d.ts +99 -0
- package/.dist/View.js +374 -0
- package/.dist/View.js.map +1 -0
- package/.dist/Viewport.d.ts +89 -0
- package/.dist/Viewport.js +295 -0
- package/.dist/Viewport.js.map +1 -0
- package/.dist/ansi.d.ts +31 -0
- package/.dist/ansi.js +139 -0
- package/.dist/ansi.js.map +1 -0
- package/.dist/components/Accordion.d.ts +47 -0
- package/.dist/components/Accordion.js +294 -0
- package/.dist/components/Accordion.js.map +1 -0
- package/.dist/components/Box.d.ts +67 -0
- package/.dist/components/Box.js +211 -0
- package/.dist/components/Box.js.map +1 -0
- package/.dist/components/Button.d.ts +28 -0
- package/.dist/components/Button.js +118 -0
- package/.dist/components/Button.js.map +1 -0
- package/.dist/components/Checkbox.d.ts +31 -0
- package/.dist/components/Checkbox.js +106 -0
- package/.dist/components/Checkbox.js.map +1 -0
- package/.dist/components/Collapsible.d.ts +32 -0
- package/.dist/components/Collapsible.js +109 -0
- package/.dist/components/Collapsible.js.map +1 -0
- package/.dist/components/CollapsibleText.d.ts +21 -0
- package/.dist/components/CollapsibleText.js +113 -0
- package/.dist/components/CollapsibleText.js.map +1 -0
- package/.dist/components/Digits.d.ts +20 -0
- package/.dist/components/Digits.js +843 -0
- package/.dist/components/Digits.js.map +1 -0
- package/.dist/components/Drawer.d.ts +44 -0
- package/.dist/components/Drawer.js +473 -0
- package/.dist/components/Drawer.js.map +1 -0
- package/.dist/components/Dropdown.d.ts +61 -0
- package/.dist/components/Dropdown.js +367 -0
- package/.dist/components/Dropdown.js.map +1 -0
- package/.dist/components/Header.d.ts +25 -0
- package/.dist/components/Header.js +106 -0
- package/.dist/components/Header.js.map +1 -0
- package/.dist/components/HotKey.d.ts +14 -0
- package/.dist/components/HotKey.js +31 -0
- package/.dist/components/HotKey.js.map +1 -0
- package/.dist/components/Input.d.ts +45 -0
- package/.dist/components/Input.js +1109 -0
- package/.dist/components/Input.js.map +1 -0
- package/.dist/components/Log.d.ts +16 -0
- package/.dist/components/Log.js +122 -0
- package/.dist/components/Log.js.map +1 -0
- package/.dist/components/Progress.d.ts +21 -0
- package/.dist/components/Progress.js +200 -0
- package/.dist/components/Progress.js.map +1 -0
- package/.dist/components/Scrollable.d.ts +50 -0
- package/.dist/components/Scrollable.js +226 -0
- package/.dist/components/Scrollable.js.map +1 -0
- package/.dist/components/ScrollableList.d.ts +70 -0
- package/.dist/components/ScrollableList.js +354 -0
- package/.dist/components/ScrollableList.js.map +1 -0
- package/.dist/components/Separator.d.ts +21 -0
- package/.dist/components/Separator.js +67 -0
- package/.dist/components/Separator.js.map +1 -0
- package/.dist/components/Slider.d.ts +60 -0
- package/.dist/components/Slider.js +341 -0
- package/.dist/components/Slider.js.map +1 -0
- package/.dist/components/Space.d.ts +19 -0
- package/.dist/components/Space.js +45 -0
- package/.dist/components/Space.js.map +1 -0
- package/.dist/components/Spinner.d.ts +16 -0
- package/.dist/components/Spinner.js +48 -0
- package/.dist/components/Spinner.js.map +1 -0
- package/.dist/components/Stack.d.ts +33 -0
- package/.dist/components/Stack.js +285 -0
- package/.dist/components/Stack.js.map +1 -0
- package/.dist/components/Tabs.d.ts +52 -0
- package/.dist/components/Tabs.js +312 -0
- package/.dist/components/Tabs.js.map +1 -0
- package/.dist/components/Text.d.ts +35 -0
- package/.dist/components/Text.js +242 -0
- package/.dist/components/Text.js.map +1 -0
- package/.dist/components/ToggleGroup.d.ts +26 -0
- package/.dist/components/ToggleGroup.js +279 -0
- package/.dist/components/ToggleGroup.js.map +1 -0
- package/.dist/components/Tree.d.ts +21 -0
- package/.dist/components/Tree.js +233 -0
- package/.dist/components/Tree.js.map +1 -0
- package/.dist/components/Window.d.ts +6 -0
- package/.dist/components/Window.js +17 -0
- package/.dist/components/Window.js.map +1 -0
- package/.dist/components/fonts.d.ts +2 -0
- package/.dist/components/fonts.js +39 -0
- package/.dist/components/fonts.js.map +1 -0
- package/.dist/components/index.d.ts +27 -0
- package/.dist/components/index.js +48 -0
- package/.dist/components/index.js.map +1 -0
- package/.dist/components/types.d.ts +6 -0
- package/.dist/components/types.js +20 -0
- package/.dist/components/types.js.map +1 -0
- package/.dist/components/utility/TrackMouse.d.ts +16 -0
- package/.dist/components/utility/TrackMouse.js +53 -0
- package/.dist/components/utility/TrackMouse.js.map +1 -0
- package/.dist/components/utility/index.d.ts +1 -0
- package/.dist/components/utility/index.js +6 -0
- package/.dist/components/utility/index.js.map +1 -0
- package/.dist/events/index.d.ts +12 -0
- package/.dist/events/index.js +20 -0
- package/.dist/events/index.js.map +1 -0
- package/.dist/events/key.d.ts +15 -0
- package/.dist/events/key.js +105 -0
- package/.dist/events/key.js.map +1 -0
- package/.dist/events/mouse.d.ts +65 -0
- package/.dist/events/mouse.js +68 -0
- package/.dist/events/mouse.js.map +1 -0
- package/.dist/events/window.d.ts +6 -0
- package/.dist/events/window.js +3 -0
- package/.dist/events/window.js.map +1 -0
- package/.dist/geometry.d.ts +178 -0
- package/.dist/geometry.js +315 -0
- package/.dist/geometry.js.map +1 -0
- package/.dist/iTerm2.d.ts +20 -0
- package/.dist/iTerm2.js +60 -0
- package/.dist/iTerm2.js.map +1 -0
- package/.dist/index.d.ts +18 -0
- package/.dist/index.js +37 -0
- package/.dist/index.js.map +1 -0
- package/.dist/inspect.d.ts +2 -0
- package/.dist/inspect.js +118 -0
- package/.dist/inspect.js.map +1 -0
- package/.dist/log.d.ts +12 -0
- package/.dist/log.js +56 -0
- package/.dist/log.js.map +1 -0
- package/.dist/managers/FocusManager.d.ts +28 -0
- package/.dist/managers/FocusManager.js +143 -0
- package/.dist/managers/FocusManager.js.map +1 -0
- package/.dist/managers/ModalManager.d.ts +10 -0
- package/.dist/managers/ModalManager.js +67 -0
- package/.dist/managers/ModalManager.js.map +1 -0
- package/.dist/managers/MouseManager.d.ts +21 -0
- package/.dist/managers/MouseManager.js +313 -0
- package/.dist/managers/MouseManager.js.map +1 -0
- package/.dist/managers/TickManager.d.ts +11 -0
- package/.dist/managers/TickManager.js +63 -0
- package/.dist/managers/TickManager.js.map +1 -0
- package/.dist/sys/alias.d.ts +470 -0
- package/.dist/sys/alias.js +487 -0
- package/.dist/sys/alias.js.map +1 -0
- package/.dist/sys/colors.d.ts +10 -0
- package/.dist/sys/colors.js +285 -0
- package/.dist/sys/colors.js.map +1 -0
- package/.dist/sys/gpmclient.d.ts +11 -0
- package/.dist/sys/gpmclient.js +193 -0
- package/.dist/sys/gpmclient.js.map +1 -0
- package/.dist/sys/index.d.ts +4 -0
- package/.dist/sys/index.js +41 -0
- package/.dist/sys/index.js.map +1 -0
- package/.dist/sys/keys.d.ts +4 -0
- package/.dist/sys/keys.js +531 -0
- package/.dist/sys/keys.js.map +1 -0
- package/.dist/sys/program.d.ts +356 -0
- package/.dist/sys/program.js +3739 -0
- package/.dist/sys/program.js.map +1 -0
- package/.dist/sys/tput.d.ts +297 -0
- package/.dist/sys/tput.js +2708 -0
- package/.dist/sys/tput.js.map +1 -0
- package/.dist/sys/unicode.d.ts +11 -0
- package/.dist/sys/unicode.js +619 -0
- package/.dist/sys/unicode.js.map +1 -0
- package/.dist/sys/usr/linux +0 -0
- package/.dist/sys/usr/windows-ansi +0 -0
- package/.dist/sys/usr/xterm +0 -0
- package/.dist/sys/usr/xterm-256color +0 -0
- package/.dist/sys/usr/xterm.termcap +243 -0
- package/.dist/sys/usr/xterm.terminfo +1977 -0
- package/.dist/terminal.d.ts +7 -0
- package/.dist/terminal.js +3 -0
- package/.dist/terminal.js.map +1 -0
- package/.dist/util.d.ts +22 -0
- package/.dist/util.js +79 -0
- package/.dist/util.js.map +1 -0
- package/LICENSE +24 -0
- package/package.json +49 -0
|
@@ -0,0 +1,1109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Input = void 0;
|
|
4
|
+
const sys_1 = require("../sys");
|
|
5
|
+
const events_1 = require("../events");
|
|
6
|
+
const View_1 = require("../View");
|
|
7
|
+
const Style_1 = require("../Style");
|
|
8
|
+
const geometry_1 = require("../geometry");
|
|
9
|
+
const fonts_1 = require("./fonts");
|
|
10
|
+
const NL_SIGIL = '⤦';
|
|
11
|
+
/**
|
|
12
|
+
* Text input. Supports selection, word movement via alt+←→, single and multiline
|
|
13
|
+
* input, and wrapped lines.
|
|
14
|
+
*/
|
|
15
|
+
class Input extends View_1.View {
|
|
16
|
+
/**
|
|
17
|
+
* Array of graphemes, with pre-calculated length
|
|
18
|
+
*/
|
|
19
|
+
#placeholder = [];
|
|
20
|
+
#printableLines = [];
|
|
21
|
+
/**
|
|
22
|
+
* Cached after assignment - this is converted to #chars and #lines
|
|
23
|
+
*/
|
|
24
|
+
#value = '';
|
|
25
|
+
/**
|
|
26
|
+
* For easy edit operations. Gets converted to #lines for printing.
|
|
27
|
+
*/
|
|
28
|
+
#chars = [];
|
|
29
|
+
#wrappedLines = [];
|
|
30
|
+
// formatting options
|
|
31
|
+
#wrap = false;
|
|
32
|
+
#multiline = false;
|
|
33
|
+
#font = 'default';
|
|
34
|
+
#onChange;
|
|
35
|
+
#onSubmit;
|
|
36
|
+
// Printable width
|
|
37
|
+
#maxLineWidth = 0;
|
|
38
|
+
#cursor = { start: 0, end: 0 };
|
|
39
|
+
#visibleWidth = 0;
|
|
40
|
+
constructor(props = {}) {
|
|
41
|
+
super(props);
|
|
42
|
+
this.#update(props);
|
|
43
|
+
this.#cursor = { start: this.#chars.length, end: this.#chars.length };
|
|
44
|
+
}
|
|
45
|
+
update(props) {
|
|
46
|
+
this.#update(props);
|
|
47
|
+
super.update(props);
|
|
48
|
+
}
|
|
49
|
+
#update({ value, wrap, multiline, font, placeholder, onChange, onSubmit, }) {
|
|
50
|
+
this.#onChange = onChange;
|
|
51
|
+
this.#onSubmit = onSubmit;
|
|
52
|
+
this.#wrap = wrap ?? false;
|
|
53
|
+
this.#multiline = multiline ?? false;
|
|
54
|
+
this.#updatePlaceholderLines(placeholder ?? '');
|
|
55
|
+
this.#updateLines(sys_1.unicode.printableChars(value ?? ''), font ?? 'default');
|
|
56
|
+
}
|
|
57
|
+
#updatePlaceholderLines(placeholder) {
|
|
58
|
+
const placeholderLines = placeholder === ''
|
|
59
|
+
? []
|
|
60
|
+
: placeholder.split('\n').map(line => sys_1.unicode.printableChars(line));
|
|
61
|
+
this.#placeholder = placeholderLines.map(line => [
|
|
62
|
+
line,
|
|
63
|
+
line.reduce((w, c) => w + sys_1.unicode.charWidth(c), 0),
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
#updateLines(_chars, font) {
|
|
67
|
+
let chars = _chars ?? this.#chars;
|
|
68
|
+
if (font === undefined) {
|
|
69
|
+
font = this.#font;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.#font = font;
|
|
73
|
+
}
|
|
74
|
+
const startIsAtEnd = this.#cursor.start === this.#chars.length;
|
|
75
|
+
const endIsAtEnd = this.#cursor.end === this.#chars.length;
|
|
76
|
+
if (chars.length > 0) {
|
|
77
|
+
if (!this.#multiline) {
|
|
78
|
+
chars = chars.map(char => (char === '\n' ? ' ' : char));
|
|
79
|
+
}
|
|
80
|
+
this.#value = chars.filter(char => !isAccentChar(char)).join('');
|
|
81
|
+
this.#chars = chars;
|
|
82
|
+
const [charLines] = this.#chars.reduce(([lines, line], char, index) => {
|
|
83
|
+
if (char === '\n') {
|
|
84
|
+
lines.push(line);
|
|
85
|
+
if (index === this.#chars.length - 1) {
|
|
86
|
+
lines.push([]);
|
|
87
|
+
}
|
|
88
|
+
return [lines, []];
|
|
89
|
+
}
|
|
90
|
+
line.push(char);
|
|
91
|
+
if (index === this.#chars.length - 1) {
|
|
92
|
+
lines.push(line);
|
|
93
|
+
return [lines, []];
|
|
94
|
+
}
|
|
95
|
+
return [lines, line];
|
|
96
|
+
}, [[], []]);
|
|
97
|
+
this.#printableLines = charLines.map((printableLine, index, all) => {
|
|
98
|
+
// every line needs a ' ' or NL_SIGIL at the end, for the EOL cursor
|
|
99
|
+
return [
|
|
100
|
+
printableLine.concat(index === all.length - 1 ? ' ' : NL_SIGIL),
|
|
101
|
+
printableLine.reduce((width, char) => width + sys_1.unicode.charWidth(char), 0) + 1,
|
|
102
|
+
];
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.#value = '';
|
|
107
|
+
this.#printableLines = this.#placeholder.map(([line, width]) => {
|
|
108
|
+
return [line.concat(' '), width];
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
this.#visibleWidth = 0;
|
|
112
|
+
if (endIsAtEnd) {
|
|
113
|
+
this.#cursor.end = this.#chars.length;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.#cursor.end = Math.min(this.#cursor.end, this.#chars.length);
|
|
117
|
+
}
|
|
118
|
+
if (startIsAtEnd) {
|
|
119
|
+
this.#cursor.start = this.#chars.length;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
this.#cursor.start = Math.min(this.#cursor.start, this.#chars.length);
|
|
123
|
+
}
|
|
124
|
+
this.#maxLineWidth = this.#printableLines.reduce((maxWidth, [, width]) => {
|
|
125
|
+
// the _printable_ width, not the number of characters
|
|
126
|
+
return Math.max(maxWidth, width);
|
|
127
|
+
}, 0);
|
|
128
|
+
this.invalidateSize();
|
|
129
|
+
}
|
|
130
|
+
get value() {
|
|
131
|
+
return this.#value;
|
|
132
|
+
}
|
|
133
|
+
set value(value) {
|
|
134
|
+
if (value !== this.#value) {
|
|
135
|
+
this.#updateLines(sys_1.unicode.printableChars(value), undefined);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
get placeholder() {
|
|
139
|
+
return this.#placeholder.map(([chars]) => chars.join('')).join('\n');
|
|
140
|
+
}
|
|
141
|
+
set placeholder(placeholder) {
|
|
142
|
+
this.#updatePlaceholderLines(placeholder ?? '');
|
|
143
|
+
}
|
|
144
|
+
get font() {
|
|
145
|
+
return this.#font;
|
|
146
|
+
}
|
|
147
|
+
set font(font) {
|
|
148
|
+
if (font !== this.#font) {
|
|
149
|
+
this.#updateLines(undefined, font);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
get wrap() {
|
|
153
|
+
return this.#wrap;
|
|
154
|
+
}
|
|
155
|
+
set wrap(wrap) {
|
|
156
|
+
if (wrap !== this.#wrap) {
|
|
157
|
+
this.#wrap = wrap;
|
|
158
|
+
this.#updateLines(undefined, undefined);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
get multiline() {
|
|
162
|
+
return this.#multiline;
|
|
163
|
+
}
|
|
164
|
+
set multiline(multiline) {
|
|
165
|
+
if (multiline !== this.#multiline) {
|
|
166
|
+
this.#multiline = multiline;
|
|
167
|
+
this.#updateLines(undefined, undefined);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
naturalSize(available) {
|
|
171
|
+
let lines = this.#printableLines;
|
|
172
|
+
if (!lines.length || !available.width) {
|
|
173
|
+
return geometry_1.Size.one;
|
|
174
|
+
}
|
|
175
|
+
let height = 0;
|
|
176
|
+
if (this.#wrap) {
|
|
177
|
+
for (const [, width] of lines) {
|
|
178
|
+
// width + 1 because there should always be room for the cursor to be _after_
|
|
179
|
+
// the last character.
|
|
180
|
+
height += Math.ceil((width + 1) / available.width);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
height = lines.length;
|
|
185
|
+
}
|
|
186
|
+
return new geometry_1.Size(this.#maxLineWidth, height);
|
|
187
|
+
}
|
|
188
|
+
minSelected() {
|
|
189
|
+
return Math.min(this.#cursor.start, this.#cursor.end);
|
|
190
|
+
}
|
|
191
|
+
maxSelected() {
|
|
192
|
+
return isEmptySelection(this.#cursor)
|
|
193
|
+
? this.#cursor.start + 1
|
|
194
|
+
: Math.max(this.#cursor.start, this.#cursor.end);
|
|
195
|
+
}
|
|
196
|
+
receiveKey(event) {
|
|
197
|
+
const prevChars = this.#chars;
|
|
198
|
+
const prevText = this.#value;
|
|
199
|
+
let removeAccent = true;
|
|
200
|
+
if (event.name === 'enter' || event.name === 'return') {
|
|
201
|
+
if (this.#multiline) {
|
|
202
|
+
this.#receiveChar('\n', true);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
this.#onSubmit?.(this.#value);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (event.full === 'C-a') {
|
|
210
|
+
this.#receiveGotoStart();
|
|
211
|
+
}
|
|
212
|
+
else if (event.full === 'C-e') {
|
|
213
|
+
this.#receiveGotoEnd();
|
|
214
|
+
}
|
|
215
|
+
else if (event.name === 'up') {
|
|
216
|
+
this.#receiveKeyUpArrow(event);
|
|
217
|
+
}
|
|
218
|
+
else if (event.name === 'down') {
|
|
219
|
+
this.#receiveKeyDownArrow(event);
|
|
220
|
+
}
|
|
221
|
+
else if (event.name === 'home') {
|
|
222
|
+
this.#receiveHome(event);
|
|
223
|
+
}
|
|
224
|
+
else if (event.name === 'end') {
|
|
225
|
+
this.#receiveEnd(event);
|
|
226
|
+
}
|
|
227
|
+
else if (event.name === 'left') {
|
|
228
|
+
this.#receiveKeyLeftArrow(event);
|
|
229
|
+
}
|
|
230
|
+
else if (event.name === 'right') {
|
|
231
|
+
this.#receiveKeyRightArrow(event);
|
|
232
|
+
}
|
|
233
|
+
else if (event.full === 'backspace') {
|
|
234
|
+
this.#receiveKeyBackspace();
|
|
235
|
+
}
|
|
236
|
+
else if (event.name === 'delete') {
|
|
237
|
+
this.#receiveKeyDelete();
|
|
238
|
+
}
|
|
239
|
+
else if (event.full === 'M-backspace' || event.full === 'C-w') {
|
|
240
|
+
this.#receiveKeyDeleteWord();
|
|
241
|
+
}
|
|
242
|
+
else if (isKeyAccent(event)) {
|
|
243
|
+
this.#receiveKeyAccent(event);
|
|
244
|
+
removeAccent = false;
|
|
245
|
+
}
|
|
246
|
+
else if ((0, events_1.isKeyPrintable)(event)) {
|
|
247
|
+
this.#receiveKeyPrintable(event);
|
|
248
|
+
}
|
|
249
|
+
if (removeAccent) {
|
|
250
|
+
this.#chars = this.#chars.filter(char => !isAccentChar(char));
|
|
251
|
+
}
|
|
252
|
+
if (prevChars !== this.#chars) {
|
|
253
|
+
this.#updateLines(this.#chars, undefined);
|
|
254
|
+
}
|
|
255
|
+
if (prevText !== this.#value) {
|
|
256
|
+
this.#onChange?.(this.#value);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
receiveMouse(event, system) {
|
|
260
|
+
if (event.name === 'mouse.button.down') {
|
|
261
|
+
system.requestFocus();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
render(viewport) {
|
|
265
|
+
const hasFocus = viewport.registerFocus();
|
|
266
|
+
if (viewport.isEmpty) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const visibleSize = viewport.contentSize;
|
|
270
|
+
if (hasFocus) {
|
|
271
|
+
viewport.registerTick();
|
|
272
|
+
}
|
|
273
|
+
viewport.registerMouse('mouse.button.left');
|
|
274
|
+
// cursorEnd: the location of the cursor relative to the text
|
|
275
|
+
// (ie if the text had been drawn at 0,0, cursorEnd is the screen location of
|
|
276
|
+
// the cursor)
|
|
277
|
+
// cursorPosition: the location of the cursor relative to the viewport
|
|
278
|
+
const [cursorEnd, cursorPosition] = this.#cursorPosition(visibleSize);
|
|
279
|
+
const cursorMin = this.#toPosition(this.minSelected(), visibleSize.width);
|
|
280
|
+
const cursorMax = this.#toPosition(this.maxSelected(), visibleSize.width);
|
|
281
|
+
// cursorVisible: the text location of the first line & char to draw
|
|
282
|
+
const cursorVisible = new geometry_1.Point(cursorEnd.x - cursorPosition.x, cursorEnd.y - cursorPosition.y);
|
|
283
|
+
let lines = this.#printableLines;
|
|
284
|
+
if (visibleSize.width !== this.#visibleWidth ||
|
|
285
|
+
this.#wrappedLines.length === 0) {
|
|
286
|
+
if (this.#wrap) {
|
|
287
|
+
lines = lines.flatMap(line => {
|
|
288
|
+
const wrappedLines = [];
|
|
289
|
+
let currentLine = [];
|
|
290
|
+
let currentWidth = 0;
|
|
291
|
+
for (const char of line[0]) {
|
|
292
|
+
const charWidth = sys_1.unicode.charWidth(char);
|
|
293
|
+
currentLine.push(char);
|
|
294
|
+
currentWidth += charWidth;
|
|
295
|
+
if (currentWidth >= visibleSize.width) {
|
|
296
|
+
wrappedLines.push([currentLine, currentWidth]);
|
|
297
|
+
currentLine = [];
|
|
298
|
+
currentWidth = 0;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (currentLine.length) {
|
|
302
|
+
wrappedLines.push([currentLine, currentWidth]);
|
|
303
|
+
}
|
|
304
|
+
return wrappedLines;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
this.#wrappedLines = lines;
|
|
308
|
+
this.#visibleWidth = visibleSize.width;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
lines = this.#wrappedLines;
|
|
312
|
+
}
|
|
313
|
+
let isPlaceholder = !Boolean(this.#chars.length);
|
|
314
|
+
let currentStyle = Style_1.Style.NONE;
|
|
315
|
+
const plainStyle = this.theme.text({
|
|
316
|
+
isPlaceholder,
|
|
317
|
+
hasFocus,
|
|
318
|
+
});
|
|
319
|
+
const selectedStyle = this.theme.text({
|
|
320
|
+
isSelected: true,
|
|
321
|
+
hasFocus,
|
|
322
|
+
});
|
|
323
|
+
const cursorStyle = plainStyle.merge({ underline: true });
|
|
324
|
+
const nlStyle = this.theme.text({ isPlaceholder: true });
|
|
325
|
+
const fontMap = this.#font && fonts_1.FONTS[this.#font];
|
|
326
|
+
viewport.usingPen(pen => {
|
|
327
|
+
let style = plainStyle;
|
|
328
|
+
const visibleLines = lines.slice(cursorVisible.y);
|
|
329
|
+
if (visibleLines.length === 0) {
|
|
330
|
+
visibleLines.push([[' '], 0]);
|
|
331
|
+
}
|
|
332
|
+
// is the viewport tall/wide enough to show ellipses …
|
|
333
|
+
const isTallEnough = viewport.contentSize.height > 4;
|
|
334
|
+
const isWideEnough = viewport.contentSize.width > 9;
|
|
335
|
+
// do we need to show vertical ellipses
|
|
336
|
+
const isTooTall = visibleLines.length > visibleSize.height;
|
|
337
|
+
// firstPoint is top-left corner of the viewport
|
|
338
|
+
const firstPoint = new geometry_1.Point(0, cursorVisible.y);
|
|
339
|
+
// lastPoint is bottom-right corner of the viewport
|
|
340
|
+
const lastPoint = new geometry_1.Point(visibleSize.width + cursorVisible.x - 1, cursorVisible.y + visibleSize.height - 1);
|
|
341
|
+
let scanTextPosition = firstPoint.mutableCopy();
|
|
342
|
+
for (const [line, width] of visibleLines) {
|
|
343
|
+
// used to determine whether to draw a final …
|
|
344
|
+
const isTooWide = this.#wrap
|
|
345
|
+
? false
|
|
346
|
+
: width - cursorVisible.x > viewport.contentSize.width;
|
|
347
|
+
// set to true if any character is skipped
|
|
348
|
+
let drawInitialEllipses = false;
|
|
349
|
+
scanTextPosition.x = 0;
|
|
350
|
+
for (let char of line) {
|
|
351
|
+
char = fontMap?.get(char) ?? char;
|
|
352
|
+
const charWidth = sys_1.unicode.charWidth(char);
|
|
353
|
+
if (scanTextPosition.x >= cursorVisible.x) {
|
|
354
|
+
const inSelection = isInSelection(cursorMin, cursorMax, scanTextPosition);
|
|
355
|
+
const inCursor = scanTextPosition.x === cursorEnd.x &&
|
|
356
|
+
scanTextPosition.y === cursorEnd.y;
|
|
357
|
+
const inNewline = char === NL_SIGIL && scanTextPosition.x + charWidth === width;
|
|
358
|
+
if (isEmptySelection(this.#cursor)) {
|
|
359
|
+
if (isAccentChar(char)) {
|
|
360
|
+
style = plainStyle.merge({ underline: true, inverse: true });
|
|
361
|
+
}
|
|
362
|
+
else if (hasFocus && inCursor) {
|
|
363
|
+
style = inNewline
|
|
364
|
+
? nlStyle.merge({ underline: true })
|
|
365
|
+
: cursorStyle;
|
|
366
|
+
}
|
|
367
|
+
else if (inNewline) {
|
|
368
|
+
style = nlStyle;
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
style = plainStyle;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
if (inSelection) {
|
|
376
|
+
style = inNewline
|
|
377
|
+
? nlStyle.merge({ background: selectedStyle.foreground })
|
|
378
|
+
: selectedStyle.merge({ underline: inCursor });
|
|
379
|
+
}
|
|
380
|
+
else if (inNewline) {
|
|
381
|
+
style = nlStyle;
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
style = plainStyle;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (!currentStyle.isEqual(style)) {
|
|
388
|
+
pen.replacePen(style);
|
|
389
|
+
currentStyle = style;
|
|
390
|
+
}
|
|
391
|
+
let drawEllipses = false;
|
|
392
|
+
if (cursorVisible.y > 0 && scanTextPosition.isEqual(firstPoint)) {
|
|
393
|
+
drawEllipses = isTallEnough;
|
|
394
|
+
}
|
|
395
|
+
else if (isTooTall && scanTextPosition.isEqual(lastPoint)) {
|
|
396
|
+
drawEllipses = isTallEnough;
|
|
397
|
+
}
|
|
398
|
+
else if (isWideEnough) {
|
|
399
|
+
if (drawInitialEllipses) {
|
|
400
|
+
drawEllipses = true;
|
|
401
|
+
}
|
|
402
|
+
else if (isTooWide &&
|
|
403
|
+
scanTextPosition.x - cursorVisible.x + charWidth >=
|
|
404
|
+
viewport.contentSize.width) {
|
|
405
|
+
drawEllipses = true;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
viewport.write(drawEllipses ? '…' : char, scanTextPosition.offset(-cursorVisible.x, -cursorVisible.y));
|
|
409
|
+
drawInitialEllipses = false;
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
drawInitialEllipses = true;
|
|
413
|
+
}
|
|
414
|
+
scanTextPosition.x += charWidth;
|
|
415
|
+
if (scanTextPosition.x - cursorVisible.x >=
|
|
416
|
+
viewport.contentSize.width) {
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
scanTextPosition.y += 1;
|
|
421
|
+
if (scanTextPosition.y - cursorVisible.y >=
|
|
422
|
+
viewport.contentSize.height) {
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* The position of the character that is at the desired cursor offset, taking
|
|
430
|
+
* character widths into account, relative to the text (as if the text were drawn
|
|
431
|
+
* at 0,0), and 'wrap' setting.
|
|
432
|
+
*/
|
|
433
|
+
#toPosition(offset, visibleWidth) {
|
|
434
|
+
if (this.#wrap) {
|
|
435
|
+
let y = 0, index = 0;
|
|
436
|
+
let x = 0;
|
|
437
|
+
// immediately after a line wrap, we don't want to also increase y by 1
|
|
438
|
+
let isFirst = true;
|
|
439
|
+
for (const [chars] of this.#printableLines) {
|
|
440
|
+
if (!isFirst) {
|
|
441
|
+
y += 1;
|
|
442
|
+
}
|
|
443
|
+
isFirst = false;
|
|
444
|
+
x = 0;
|
|
445
|
+
for (const char of chars) {
|
|
446
|
+
if (index === offset) {
|
|
447
|
+
if (x === visibleWidth) {
|
|
448
|
+
x = 0;
|
|
449
|
+
y += 1;
|
|
450
|
+
}
|
|
451
|
+
return new geometry_1.Point(x, y);
|
|
452
|
+
}
|
|
453
|
+
const charWidth = sys_1.unicode.charWidth(char);
|
|
454
|
+
if (x + charWidth > visibleWidth) {
|
|
455
|
+
x = charWidth;
|
|
456
|
+
y += 1;
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
x += charWidth;
|
|
460
|
+
}
|
|
461
|
+
index += 1;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return new geometry_1.Point(x, y);
|
|
465
|
+
}
|
|
466
|
+
let y = 0, index = 0;
|
|
467
|
+
for (const [chars] of this.#printableLines) {
|
|
468
|
+
if (index + chars.length > offset) {
|
|
469
|
+
let x = 0;
|
|
470
|
+
for (const char of chars.slice(0, offset - index)) {
|
|
471
|
+
x += sys_1.unicode.charWidth(char);
|
|
472
|
+
}
|
|
473
|
+
return new geometry_1.Point({ x, y });
|
|
474
|
+
}
|
|
475
|
+
index += chars.length;
|
|
476
|
+
y += 1;
|
|
477
|
+
}
|
|
478
|
+
return new geometry_1.Point(0, y);
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Returns the cursor offset that points to the character at the desired screen
|
|
482
|
+
* position, taking into account character widths.
|
|
483
|
+
*/
|
|
484
|
+
#toOffset(position, visibleWidth) {
|
|
485
|
+
if (this.#wrap) {
|
|
486
|
+
let y = 0, index = 0;
|
|
487
|
+
let x = 0;
|
|
488
|
+
for (const [chars] of this.#printableLines) {
|
|
489
|
+
if (y) {
|
|
490
|
+
y += 1;
|
|
491
|
+
}
|
|
492
|
+
x = 0;
|
|
493
|
+
for (const char of chars) {
|
|
494
|
+
if (position.isEqual(x, y)) {
|
|
495
|
+
return index;
|
|
496
|
+
}
|
|
497
|
+
const charWidth = sys_1.unicode.charWidth(char);
|
|
498
|
+
if (x + charWidth >= visibleWidth) {
|
|
499
|
+
x = 0;
|
|
500
|
+
y += 1;
|
|
501
|
+
index += 1;
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
x += charWidth;
|
|
505
|
+
index += 1;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return index;
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
if (position.y >= this.#printableLines.length) {
|
|
513
|
+
return this.#chars.length;
|
|
514
|
+
}
|
|
515
|
+
let y = 0, index = 0;
|
|
516
|
+
for (const [chars, width] of this.#printableLines) {
|
|
517
|
+
if (y === position.y) {
|
|
518
|
+
let x = 0;
|
|
519
|
+
for (const char of chars) {
|
|
520
|
+
x += sys_1.unicode.charWidth(char);
|
|
521
|
+
if (x > position.x) {
|
|
522
|
+
return index;
|
|
523
|
+
}
|
|
524
|
+
index += 1;
|
|
525
|
+
}
|
|
526
|
+
return index;
|
|
527
|
+
}
|
|
528
|
+
y += 1;
|
|
529
|
+
index += chars.length + 1;
|
|
530
|
+
}
|
|
531
|
+
return this.#chars.length;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Determine the position of the cursor, relative to the viewport, based on the
|
|
536
|
+
* text and viewport sizes.
|
|
537
|
+
*
|
|
538
|
+
* The cursor is placed so that it will appear at the start or end of the viewport
|
|
539
|
+
* when it is near the start or end of the line, otherwise it tries to be centered.
|
|
540
|
+
*/
|
|
541
|
+
#cursorPosition(visibleSize) {
|
|
542
|
+
const halfWidth = Math.floor(visibleSize.width / 2);
|
|
543
|
+
const halfHeight = Math.floor(visibleSize.height / 2);
|
|
544
|
+
// the cursor, relative to the start of text (as if all text was visible),
|
|
545
|
+
// ie in the "coordinate system" of the text.
|
|
546
|
+
let cursorEnd = this.#toPosition(this.#cursor.end, visibleSize.width);
|
|
547
|
+
let currentLineWidth, totalHeight;
|
|
548
|
+
if (!this.#printableLines.length) {
|
|
549
|
+
return [cursorEnd, new geometry_1.Point(0, 0)];
|
|
550
|
+
}
|
|
551
|
+
if (this.#wrap) {
|
|
552
|
+
// run through the lines until we get to our desired cursorEnd.y
|
|
553
|
+
// but also add all the heights to calculate currentHeight
|
|
554
|
+
let h = 0;
|
|
555
|
+
currentLineWidth = -1;
|
|
556
|
+
totalHeight = 0;
|
|
557
|
+
for (const [, width] of this.#printableLines) {
|
|
558
|
+
const dh = Math.ceil(width / visibleSize.width);
|
|
559
|
+
totalHeight += dh;
|
|
560
|
+
if (currentLineWidth === -1 && dh >= cursorEnd.y) {
|
|
561
|
+
if (cursorEnd.y - h === dh) {
|
|
562
|
+
// the cursor is on the last wrapped line, use modulo divide to calculate the
|
|
563
|
+
// last line width, add 1 for the EOL cursor
|
|
564
|
+
currentLineWidth = (visibleSize.width % width) + 1;
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
currentLineWidth = visibleSize.width;
|
|
568
|
+
}
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
currentLineWidth = Math.max(0, currentLineWidth);
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
currentLineWidth = this.#printableLines[cursorEnd.y]?.[1] ?? 0;
|
|
576
|
+
totalHeight = this.#printableLines.length;
|
|
577
|
+
}
|
|
578
|
+
// Calculate the viewport location where the cursor will be drawn
|
|
579
|
+
// x location:
|
|
580
|
+
let cursorX;
|
|
581
|
+
if (currentLineWidth <= visibleSize.width) {
|
|
582
|
+
// If the viewport can accommodate the entire line
|
|
583
|
+
// draw the cursor at its natural location.
|
|
584
|
+
cursorX = cursorEnd.x;
|
|
585
|
+
}
|
|
586
|
+
else if (cursorEnd.x < halfWidth) {
|
|
587
|
+
// If the cursor is at the start of the line
|
|
588
|
+
// place the cursor at the start of the viewport
|
|
589
|
+
cursorX = cursorEnd.x;
|
|
590
|
+
}
|
|
591
|
+
else if (cursorEnd.x > currentLineWidth - halfWidth) {
|
|
592
|
+
// or if the cursor is at the end of the line
|
|
593
|
+
// draw it at the end of the viewport
|
|
594
|
+
cursorX = visibleSize.width - currentLineWidth + cursorEnd.x;
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
// otherwise place it in the middle.
|
|
598
|
+
cursorX = halfWidth;
|
|
599
|
+
}
|
|
600
|
+
// y location:
|
|
601
|
+
let cursorY;
|
|
602
|
+
if (totalHeight <= visibleSize.height) {
|
|
603
|
+
// If the viewport can accommodate the entire height
|
|
604
|
+
// draw the cursor at its natural location.
|
|
605
|
+
cursorY = cursorEnd.y;
|
|
606
|
+
}
|
|
607
|
+
else if (cursorEnd.y < halfHeight) {
|
|
608
|
+
// If the cursor is at the start of the text
|
|
609
|
+
// place the cursor at the start of the viewport
|
|
610
|
+
cursorY = cursorEnd.y;
|
|
611
|
+
}
|
|
612
|
+
else if (cursorEnd.y >= totalHeight - halfHeight) {
|
|
613
|
+
// or if the cursor is at the end of the text
|
|
614
|
+
// draw it at the end of the viewport
|
|
615
|
+
cursorY = visibleSize.height - totalHeight + cursorEnd.y;
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
// otherwise place it in the middle.
|
|
619
|
+
cursorY = halfHeight;
|
|
620
|
+
}
|
|
621
|
+
// The viewport location where the cursor will be drawn
|
|
622
|
+
return [cursorEnd, new geometry_1.Point(cursorX, cursorY)];
|
|
623
|
+
}
|
|
624
|
+
#receiveKeyAccent(event) {
|
|
625
|
+
this.#chars = this.#chars.filter(char => !isAccentChar(char));
|
|
626
|
+
let char = ACCENT_KEYS[event.full];
|
|
627
|
+
if (!char) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
this.#receiveChar(char, false);
|
|
631
|
+
}
|
|
632
|
+
#receiveKeyPrintable({ char }) {
|
|
633
|
+
if (this.#cursor.start === this.#cursor.end &&
|
|
634
|
+
isAccentChar(this.#chars[this.#cursor.start])) {
|
|
635
|
+
// if character under cursor is an accent, replace it.
|
|
636
|
+
const accented = accentChar(this.#chars[this.#cursor.start], char);
|
|
637
|
+
this.#receiveChar(accented, true);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
this.#receiveChar(char, true);
|
|
641
|
+
}
|
|
642
|
+
#receiveChar(char, advance) {
|
|
643
|
+
if (isEmptySelection(this.#cursor)) {
|
|
644
|
+
this.#chars = this.#chars
|
|
645
|
+
.slice(0, this.#cursor.start)
|
|
646
|
+
.concat(char, this.#chars.slice(this.#cursor.start));
|
|
647
|
+
this.#cursor.start = this.#cursor.end =
|
|
648
|
+
this.#cursor.start + (advance ? 1 : 0);
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
this.#chars = this.#chars
|
|
652
|
+
.slice(0, this.minSelected())
|
|
653
|
+
.concat(char, this.#chars.slice(this.maxSelected()));
|
|
654
|
+
this.#cursor.start = this.#cursor.end =
|
|
655
|
+
this.minSelected() + (advance ? 1 : 0);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
#receiveGotoStart() {
|
|
659
|
+
this.#cursor = { start: 0, end: 0 };
|
|
660
|
+
}
|
|
661
|
+
#receiveGotoEnd() {
|
|
662
|
+
this.#cursor = { start: this.#chars.length, end: this.#chars.length };
|
|
663
|
+
}
|
|
664
|
+
#receiveHome({ shift }) {
|
|
665
|
+
let dest = 0;
|
|
666
|
+
// move the cursor to the previous line, moving the cursor until it is at the
|
|
667
|
+
// same X position.
|
|
668
|
+
let cursorPosition = this.#toPosition(this.#cursor.end, this.#visibleWidth).mutableCopy();
|
|
669
|
+
if (cursorPosition.y === 0) {
|
|
670
|
+
dest = 0;
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
const [targetChars, targetWidth] = this.#wrappedLines[cursorPosition.y];
|
|
674
|
+
dest = this.#wrappedLines
|
|
675
|
+
.slice(0, cursorPosition.y)
|
|
676
|
+
.reduce((dest, [, width]) => {
|
|
677
|
+
return dest + width;
|
|
678
|
+
}, 0);
|
|
679
|
+
}
|
|
680
|
+
if (shift) {
|
|
681
|
+
this.#cursor.end = dest;
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
this.#cursor = { start: dest, end: dest };
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
#receiveEnd({ shift }) {
|
|
688
|
+
let dest = 0;
|
|
689
|
+
// move the cursor to the next line, moving the cursor until it is at the
|
|
690
|
+
// same X position.
|
|
691
|
+
let cursorPosition = this.#toPosition(this.#cursor.end, this.#visibleWidth).mutableCopy();
|
|
692
|
+
if (cursorPosition.y === this.#wrappedLines.length - 1) {
|
|
693
|
+
dest = this.#chars.length;
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
const [targetChars, targetWidth] = this.#wrappedLines[cursorPosition.y + 1];
|
|
697
|
+
dest =
|
|
698
|
+
this.#wrappedLines
|
|
699
|
+
.slice(0, cursorPosition.y + 1)
|
|
700
|
+
.reduce((dest, [, width]) => {
|
|
701
|
+
return dest + width;
|
|
702
|
+
}, 0) - 1;
|
|
703
|
+
}
|
|
704
|
+
if (shift) {
|
|
705
|
+
this.#cursor.end = dest;
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
this.#cursor = { start: dest, end: dest };
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
#receiveKeyUpArrow({ shift }) {
|
|
712
|
+
let dest = 0;
|
|
713
|
+
// move the cursor to the previous line, moving the cursor until it is at the
|
|
714
|
+
// same X position.
|
|
715
|
+
let cursorPosition = this.#toPosition(this.#cursor.end, this.#visibleWidth).mutableCopy();
|
|
716
|
+
if (cursorPosition.y === 0) {
|
|
717
|
+
dest = 0;
|
|
718
|
+
}
|
|
719
|
+
else if (cursorPosition.y <= this.#wrappedLines.length) {
|
|
720
|
+
const [targetChars, targetWidth] = this.#wrappedLines[cursorPosition.y - 1];
|
|
721
|
+
dest = this.#wrappedLines
|
|
722
|
+
.slice(0, cursorPosition.y - 1)
|
|
723
|
+
.reduce((dest, [, width]) => {
|
|
724
|
+
return dest + width;
|
|
725
|
+
}, 0);
|
|
726
|
+
if (targetWidth <= cursorPosition.x) {
|
|
727
|
+
dest += targetWidth - 1;
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
let destOffset = 0;
|
|
731
|
+
for (const char of targetChars) {
|
|
732
|
+
const charWidth = sys_1.unicode.charWidth(char);
|
|
733
|
+
if (destOffset + charWidth > cursorPosition.x) {
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
736
|
+
destOffset += 1;
|
|
737
|
+
}
|
|
738
|
+
dest += destOffset;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (shift) {
|
|
742
|
+
this.#cursor.end = dest;
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
this.#cursor = { start: dest, end: dest };
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
#receiveKeyDownArrow({ shift }) {
|
|
749
|
+
let dest = 0;
|
|
750
|
+
// move the cursor to the next line, moving the cursor until it is at the
|
|
751
|
+
// same X position.
|
|
752
|
+
let cursorPosition = this.#toPosition(this.#cursor.end, this.#visibleWidth).mutableCopy();
|
|
753
|
+
if (cursorPosition.y === this.#wrappedLines.length - 1 ||
|
|
754
|
+
this.#wrappedLines.length === 0) {
|
|
755
|
+
dest = this.#chars.length;
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
const [targetChars, targetWidth] = this.#wrappedLines[cursorPosition.y + 1];
|
|
759
|
+
dest = this.#wrappedLines
|
|
760
|
+
.slice(0, cursorPosition.y + 1)
|
|
761
|
+
.reduce((dest, [, width]) => {
|
|
762
|
+
return dest + width;
|
|
763
|
+
}, 0);
|
|
764
|
+
if (targetWidth <= cursorPosition.x) {
|
|
765
|
+
dest += targetWidth - 1;
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
let destOffset = 0;
|
|
769
|
+
for (const char of targetChars) {
|
|
770
|
+
const charWidth = sys_1.unicode.charWidth(char);
|
|
771
|
+
if (destOffset + charWidth > cursorPosition.x) {
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
destOffset += 1;
|
|
775
|
+
}
|
|
776
|
+
dest += destOffset;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (shift) {
|
|
780
|
+
this.#cursor.end = dest;
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
this.#cursor = { start: dest, end: dest };
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
#prevWordOffset(shift) {
|
|
787
|
+
let cursor;
|
|
788
|
+
if (shift) {
|
|
789
|
+
cursor = this.#cursor.end;
|
|
790
|
+
}
|
|
791
|
+
else if (isEmptySelection(this.#cursor)) {
|
|
792
|
+
cursor = this.#cursor.start;
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
cursor = this.minSelected();
|
|
796
|
+
}
|
|
797
|
+
let prevWordOffset = 0;
|
|
798
|
+
for (const [chars, offset] of sys_1.unicode.words(this.#chars)) {
|
|
799
|
+
prevWordOffset = offset;
|
|
800
|
+
if (cursor <= offset + chars.length) {
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return prevWordOffset;
|
|
805
|
+
}
|
|
806
|
+
#nextWordOffset(shift) {
|
|
807
|
+
let cursor;
|
|
808
|
+
if (shift) {
|
|
809
|
+
cursor = this.#cursor.end;
|
|
810
|
+
}
|
|
811
|
+
else if (isEmptySelection(this.#cursor)) {
|
|
812
|
+
cursor = this.#cursor.start;
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
cursor = this.maxSelected();
|
|
816
|
+
}
|
|
817
|
+
let nextWordOffset = 0;
|
|
818
|
+
for (const [chars, offset] of sys_1.unicode.words(this.#chars)) {
|
|
819
|
+
nextWordOffset = offset + chars.length;
|
|
820
|
+
if (cursor < offset + chars.length) {
|
|
821
|
+
break;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return nextWordOffset;
|
|
825
|
+
}
|
|
826
|
+
#receiveKeyLeftArrow({ shift, meta }) {
|
|
827
|
+
if (meta) {
|
|
828
|
+
const prevWordOffset = this.#prevWordOffset(shift);
|
|
829
|
+
if (shift) {
|
|
830
|
+
this.#cursor.end = prevWordOffset;
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
this.#cursor.start = this.#cursor.end = prevWordOffset;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
else if (shift) {
|
|
837
|
+
this.#cursor.end = Math.max(0, this.#cursor.end - 1);
|
|
838
|
+
}
|
|
839
|
+
else if (isEmptySelection(this.#cursor)) {
|
|
840
|
+
this.#cursor.start = this.#cursor.end = Math.max(0, this.#cursor.start - 1);
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
this.#cursor.start = this.#cursor.end = this.minSelected();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
#receiveKeyRightArrow({ shift, meta }) {
|
|
847
|
+
if (meta) {
|
|
848
|
+
const nextWordOffset = this.#nextWordOffset(shift);
|
|
849
|
+
if (shift) {
|
|
850
|
+
this.#cursor.end = nextWordOffset;
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
this.#cursor.start = this.#cursor.end = nextWordOffset;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
else if (shift) {
|
|
857
|
+
this.#cursor.end = Math.min(this.#chars.length, this.#cursor.end + 1);
|
|
858
|
+
}
|
|
859
|
+
else if (isEmptySelection(this.#cursor)) {
|
|
860
|
+
this.#cursor.start = this.#cursor.end = Math.min(this.#chars.length, this.#cursor.start + 1);
|
|
861
|
+
}
|
|
862
|
+
else {
|
|
863
|
+
this.#cursor.start = this.#cursor.end = this.maxSelected();
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
#updateWidth() {
|
|
867
|
+
this.#maxLineWidth = this.#chars
|
|
868
|
+
.map(sys_1.unicode.charWidth)
|
|
869
|
+
.reduce((a, b) => a + b, 0);
|
|
870
|
+
}
|
|
871
|
+
#deleteSelection() {
|
|
872
|
+
this.#chars = this.#chars
|
|
873
|
+
.slice(0, this.minSelected())
|
|
874
|
+
.concat(this.#chars.slice(this.maxSelected()));
|
|
875
|
+
this.#cursor.start = this.#cursor.end = this.minSelected();
|
|
876
|
+
this.#updateWidth();
|
|
877
|
+
}
|
|
878
|
+
#receiveKeyBackspace() {
|
|
879
|
+
if (isEmptySelection(this.#cursor)) {
|
|
880
|
+
if (this.#cursor.start === 0) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
this.#chars = this.#chars
|
|
884
|
+
.slice(0, this.#cursor.start - 1)
|
|
885
|
+
.concat(this.#chars.slice(this.#cursor.start));
|
|
886
|
+
this.#cursor.start = this.#cursor.end = this.#cursor.start - 1;
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
this.#deleteSelection();
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
#receiveKeyDelete() {
|
|
893
|
+
if (isEmptySelection(this.#cursor)) {
|
|
894
|
+
if (this.#cursor.start > this.#chars.length - 1) {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
this.#maxLineWidth -= sys_1.unicode.charWidth(this.#chars[this.#cursor.start]);
|
|
898
|
+
this.#chars = this.#chars
|
|
899
|
+
.slice(0, this.#cursor.start)
|
|
900
|
+
.concat(this.#chars.slice(this.#cursor.start + 1));
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
this.#deleteSelection();
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
#receiveKeyDeleteWord() {
|
|
907
|
+
if (!isEmptySelection(this.#cursor)) {
|
|
908
|
+
return this.#deleteSelection();
|
|
909
|
+
}
|
|
910
|
+
if (this.#cursor.start === 0) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
const offset = this.#prevWordOffset(false);
|
|
914
|
+
this.#chars = this.#chars
|
|
915
|
+
.slice(0, offset)
|
|
916
|
+
.concat(this.#chars.slice(this.#cursor.start));
|
|
917
|
+
this.#cursor.start = this.#cursor.end = offset;
|
|
918
|
+
this.#updateWidth();
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
exports.Input = Input;
|
|
922
|
+
function isEmptySelection(cursor) {
|
|
923
|
+
return cursor.start === cursor.end;
|
|
924
|
+
}
|
|
925
|
+
function isInSelection(cursorMin, cursorMax, scanTextPosition) {
|
|
926
|
+
if (scanTextPosition.y < cursorMin.y || scanTextPosition.y > cursorMax.y) {
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
if (scanTextPosition.y === cursorMin.y) {
|
|
930
|
+
if (scanTextPosition.x < cursorMin.x) {
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
if (scanTextPosition.y === cursorMax.y) {
|
|
935
|
+
if (scanTextPosition.x >= cursorMax.x) {
|
|
936
|
+
return false;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
return true;
|
|
940
|
+
}
|
|
941
|
+
function isAccentChar(char) {
|
|
942
|
+
return ACCENTS[char] !== undefined;
|
|
943
|
+
}
|
|
944
|
+
const ACCENTS = {
|
|
945
|
+
'`': {
|
|
946
|
+
A: 'À',
|
|
947
|
+
E: 'È',
|
|
948
|
+
I: 'Ì',
|
|
949
|
+
O: 'Ò',
|
|
950
|
+
U: 'Ù',
|
|
951
|
+
N: 'Ǹ',
|
|
952
|
+
a: 'à',
|
|
953
|
+
e: 'è',
|
|
954
|
+
i: 'ì',
|
|
955
|
+
o: 'ò',
|
|
956
|
+
u: 'ù',
|
|
957
|
+
n: 'ǹ',
|
|
958
|
+
},
|
|
959
|
+
'¸': {
|
|
960
|
+
C: 'Ç',
|
|
961
|
+
D: 'Ḑ',
|
|
962
|
+
E: 'Ȩ',
|
|
963
|
+
G: 'Ģ',
|
|
964
|
+
H: 'Ḩ',
|
|
965
|
+
K: 'Ķ',
|
|
966
|
+
L: 'Ļ',
|
|
967
|
+
N: 'Ņ',
|
|
968
|
+
R: 'Ŗ',
|
|
969
|
+
S: 'Ş',
|
|
970
|
+
T: 'Ţ',
|
|
971
|
+
c: 'ç',
|
|
972
|
+
d: 'ḑ',
|
|
973
|
+
e: 'ȩ',
|
|
974
|
+
g: 'ģ',
|
|
975
|
+
h: 'ḩ',
|
|
976
|
+
k: 'ķ',
|
|
977
|
+
l: 'ļ',
|
|
978
|
+
n: 'ņ',
|
|
979
|
+
r: 'ŗ',
|
|
980
|
+
s: 'ş',
|
|
981
|
+
t: 'ţ',
|
|
982
|
+
},
|
|
983
|
+
'´': {
|
|
984
|
+
A: 'Á',
|
|
985
|
+
C: 'Ć',
|
|
986
|
+
E: 'É',
|
|
987
|
+
G: 'Ǵ',
|
|
988
|
+
I: 'Í',
|
|
989
|
+
K: 'Ḱ',
|
|
990
|
+
L: 'Ĺ',
|
|
991
|
+
M: 'Ḿ',
|
|
992
|
+
N: 'Ń',
|
|
993
|
+
O: 'Ó',
|
|
994
|
+
P: 'Ṕ',
|
|
995
|
+
R: 'Ŕ',
|
|
996
|
+
S: 'Ś',
|
|
997
|
+
U: 'Ú',
|
|
998
|
+
W: 'Ẃ',
|
|
999
|
+
Y: 'Ý',
|
|
1000
|
+
a: 'á',
|
|
1001
|
+
c: 'ć',
|
|
1002
|
+
e: 'é',
|
|
1003
|
+
g: 'ǵ',
|
|
1004
|
+
i: 'í',
|
|
1005
|
+
k: 'ḱ',
|
|
1006
|
+
l: 'ĺ',
|
|
1007
|
+
m: 'ḿ',
|
|
1008
|
+
n: 'ń',
|
|
1009
|
+
o: 'ó',
|
|
1010
|
+
p: 'ṕ',
|
|
1011
|
+
r: 'ŕ',
|
|
1012
|
+
s: 'ś',
|
|
1013
|
+
u: 'ú',
|
|
1014
|
+
w: 'ẃ',
|
|
1015
|
+
y: 'ý',
|
|
1016
|
+
},
|
|
1017
|
+
ˆ: {
|
|
1018
|
+
A: 'Â',
|
|
1019
|
+
C: 'Ĉ',
|
|
1020
|
+
E: 'Ê',
|
|
1021
|
+
G: 'Ĝ',
|
|
1022
|
+
H: 'Ĥ',
|
|
1023
|
+
I: 'Î',
|
|
1024
|
+
J: 'Ĵ',
|
|
1025
|
+
O: 'Ô',
|
|
1026
|
+
S: 'Ŝ',
|
|
1027
|
+
U: 'Û',
|
|
1028
|
+
W: 'Ŵ',
|
|
1029
|
+
Y: 'Ŷ',
|
|
1030
|
+
a: 'â',
|
|
1031
|
+
c: 'ĉ',
|
|
1032
|
+
e: 'ê',
|
|
1033
|
+
g: 'ĝ',
|
|
1034
|
+
h: 'ĥ',
|
|
1035
|
+
i: 'î',
|
|
1036
|
+
j: 'ĵ',
|
|
1037
|
+
o: 'ô',
|
|
1038
|
+
s: 'ŝ',
|
|
1039
|
+
u: 'û',
|
|
1040
|
+
w: 'ŵ',
|
|
1041
|
+
y: 'ŷ',
|
|
1042
|
+
},
|
|
1043
|
+
'˜': {
|
|
1044
|
+
A: 'Ã',
|
|
1045
|
+
I: 'Ĩ',
|
|
1046
|
+
N: 'Ñ',
|
|
1047
|
+
O: 'Õ',
|
|
1048
|
+
U: 'Ũ',
|
|
1049
|
+
Y: 'Ỹ',
|
|
1050
|
+
a: 'ã',
|
|
1051
|
+
i: 'ĩ',
|
|
1052
|
+
n: 'ñ',
|
|
1053
|
+
o: 'õ',
|
|
1054
|
+
u: 'ũ',
|
|
1055
|
+
y: 'ỹ',
|
|
1056
|
+
},
|
|
1057
|
+
'¯': {
|
|
1058
|
+
A: 'Ā',
|
|
1059
|
+
E: 'Ē',
|
|
1060
|
+
I: 'Ī',
|
|
1061
|
+
O: 'Ō',
|
|
1062
|
+
U: 'Ū',
|
|
1063
|
+
Y: 'Ȳ',
|
|
1064
|
+
a: 'ā',
|
|
1065
|
+
e: 'ē',
|
|
1066
|
+
i: 'ī',
|
|
1067
|
+
o: 'ō',
|
|
1068
|
+
u: 'ū',
|
|
1069
|
+
y: 'ȳ',
|
|
1070
|
+
},
|
|
1071
|
+
'¨': {
|
|
1072
|
+
A: 'Ä',
|
|
1073
|
+
E: 'Ë',
|
|
1074
|
+
I: 'Ï',
|
|
1075
|
+
O: 'Ö',
|
|
1076
|
+
U: 'Ü',
|
|
1077
|
+
W: 'Ẅ',
|
|
1078
|
+
X: 'Ẍ',
|
|
1079
|
+
Y: 'Ÿ',
|
|
1080
|
+
a: 'ä',
|
|
1081
|
+
e: 'ë',
|
|
1082
|
+
i: 'ï',
|
|
1083
|
+
o: 'ö',
|
|
1084
|
+
u: 'ü',
|
|
1085
|
+
w: 'ẅ',
|
|
1086
|
+
x: 'ẍ',
|
|
1087
|
+
y: 'ÿ',
|
|
1088
|
+
},
|
|
1089
|
+
};
|
|
1090
|
+
const ACCENT_KEYS = {
|
|
1091
|
+
'M-a': '`',
|
|
1092
|
+
'M-c': '¸',
|
|
1093
|
+
'M-e': '´',
|
|
1094
|
+
'M-i': 'ˆ',
|
|
1095
|
+
'M-n': '˜',
|
|
1096
|
+
'M-o': '¯',
|
|
1097
|
+
'M-s': '¸',
|
|
1098
|
+
'M-u': '¨',
|
|
1099
|
+
};
|
|
1100
|
+
function accentChar(accent, char) {
|
|
1101
|
+
return ACCENTS[accent]?.[char] ?? char;
|
|
1102
|
+
}
|
|
1103
|
+
function isKeyAccent(event) {
|
|
1104
|
+
if (!event.meta || event.ctrl) {
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
return ACCENT_KEYS[event.full] !== undefined;
|
|
1108
|
+
}
|
|
1109
|
+
//# sourceMappingURL=Input.js.map
|