@akiojin/gwt 9.7.0 → 9.8.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.ja.md +37 -6
- package/README.md +38 -8
- package/assets/icons/icon.icns +0 -0
- package/assets/icons/icon.ico +0 -0
- package/assets/icons/icon.png +0 -0
- package/assets/release-assets.json +18 -0
- package/docker-compose.yml +2 -2
- package/package.json +6 -3
- package/scripts/check-release-flow.sh +17 -0
- package/scripts/release-assets.cjs +11 -0
- package/scripts/test-all.sh +15 -1
- package/scripts/test_release_assets.cjs +67 -0
- package/vendor/vt100/LICENSE +21 -0
- package/vendor/vt100/src/attrs.rs +144 -0
- package/vendor/vt100/src/callbacks.rs +69 -0
- package/vendor/vt100/src/cell.rs +179 -0
- package/vendor/vt100/src/grid.rs +742 -0
- package/vendor/vt100/src/lib.rs +64 -0
- package/vendor/vt100/src/parser.rs +96 -0
- package/vendor/vt100/src/perform.rs +277 -0
- package/vendor/vt100/src/row.rs +488 -0
- package/vendor/vt100/src/screen.rs +1354 -0
- package/vendor/vt100/src/term.rs +551 -0
- package/wix/main.wxs +40 -5
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
use crate::term::BufWrite as _;
|
|
2
|
+
|
|
3
|
+
#[derive(Clone, Debug)]
|
|
4
|
+
pub struct Grid {
|
|
5
|
+
size: Size,
|
|
6
|
+
pos: Pos,
|
|
7
|
+
saved_pos: Pos,
|
|
8
|
+
rows: Vec<crate::row::Row>,
|
|
9
|
+
scroll_top: u16,
|
|
10
|
+
scroll_bottom: u16,
|
|
11
|
+
origin_mode: bool,
|
|
12
|
+
saved_origin_mode: bool,
|
|
13
|
+
scrollback: std::collections::VecDeque<crate::row::Row>,
|
|
14
|
+
scrollback_len: usize,
|
|
15
|
+
scrollback_offset: usize,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl Grid {
|
|
19
|
+
pub fn new(size: Size, scrollback_len: usize) -> Self {
|
|
20
|
+
Self {
|
|
21
|
+
size,
|
|
22
|
+
pos: Pos::default(),
|
|
23
|
+
saved_pos: Pos::default(),
|
|
24
|
+
rows: vec![],
|
|
25
|
+
scroll_top: 0,
|
|
26
|
+
scroll_bottom: size.rows - 1,
|
|
27
|
+
origin_mode: false,
|
|
28
|
+
saved_origin_mode: false,
|
|
29
|
+
scrollback: std::collections::VecDeque::new(),
|
|
30
|
+
scrollback_len,
|
|
31
|
+
scrollback_offset: 0,
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
pub fn allocate_rows(&mut self) {
|
|
36
|
+
if self.rows.is_empty() {
|
|
37
|
+
self.rows.extend(
|
|
38
|
+
std::iter::repeat_with(|| {
|
|
39
|
+
crate::row::Row::new(self.size.cols)
|
|
40
|
+
})
|
|
41
|
+
.take(usize::from(self.size.rows)),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fn new_row(&self) -> crate::row::Row {
|
|
47
|
+
crate::row::Row::new(self.size.cols)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
pub fn clear(&mut self) {
|
|
51
|
+
self.pos = Pos::default();
|
|
52
|
+
self.saved_pos = Pos::default();
|
|
53
|
+
for row in self.drawing_rows_mut() {
|
|
54
|
+
row.clear(crate::attrs::Attrs::default());
|
|
55
|
+
}
|
|
56
|
+
self.scroll_top = 0;
|
|
57
|
+
self.scroll_bottom = self.size.rows - 1;
|
|
58
|
+
self.origin_mode = false;
|
|
59
|
+
self.saved_origin_mode = false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub fn size(&self) -> Size {
|
|
63
|
+
self.size
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pub fn set_size(&mut self, size: Size) {
|
|
67
|
+
if size.cols != self.size.cols {
|
|
68
|
+
for row in &mut self.rows {
|
|
69
|
+
row.wrap(false);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if self.scroll_bottom == self.size.rows - 1 {
|
|
74
|
+
self.scroll_bottom = size.rows - 1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
self.size = size;
|
|
78
|
+
for row in &mut self.rows {
|
|
79
|
+
row.resize(size.cols, crate::Cell::new());
|
|
80
|
+
}
|
|
81
|
+
self.rows.resize(usize::from(size.rows), self.new_row());
|
|
82
|
+
|
|
83
|
+
if self.scroll_bottom >= size.rows {
|
|
84
|
+
self.scroll_bottom = size.rows - 1;
|
|
85
|
+
}
|
|
86
|
+
if self.scroll_bottom < self.scroll_top {
|
|
87
|
+
self.scroll_top = 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
self.row_clamp_top(false);
|
|
91
|
+
self.row_clamp_bottom(false);
|
|
92
|
+
self.col_clamp();
|
|
93
|
+
|
|
94
|
+
if self.saved_pos.row > self.size.rows - 1 {
|
|
95
|
+
self.saved_pos.row = self.size.rows - 1;
|
|
96
|
+
}
|
|
97
|
+
if self.saved_pos.col > self.size.cols - 1 {
|
|
98
|
+
self.saved_pos.col = self.size.cols - 1;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pub fn pos(&self) -> Pos {
|
|
103
|
+
self.pos
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
pub fn set_pos(&mut self, mut pos: Pos) {
|
|
107
|
+
if self.origin_mode {
|
|
108
|
+
pos.row = pos.row.saturating_add(self.scroll_top);
|
|
109
|
+
}
|
|
110
|
+
self.pos = pos;
|
|
111
|
+
self.row_clamp_top(self.origin_mode);
|
|
112
|
+
self.row_clamp_bottom(self.origin_mode);
|
|
113
|
+
self.col_clamp();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
pub fn save_cursor(&mut self) {
|
|
117
|
+
self.saved_pos = self.pos;
|
|
118
|
+
self.saved_origin_mode = self.origin_mode;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
pub fn restore_cursor(&mut self) {
|
|
122
|
+
self.pos = self.saved_pos;
|
|
123
|
+
self.origin_mode = self.saved_origin_mode;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
pub fn visible_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
|
|
127
|
+
let scrollback_len = self.scrollback.len();
|
|
128
|
+
let rows_len = self.rows.len();
|
|
129
|
+
self.scrollback
|
|
130
|
+
.iter()
|
|
131
|
+
.skip(scrollback_len - self.scrollback_offset)
|
|
132
|
+
// when scrollback_offset > rows_len (e.g. rows = 3,
|
|
133
|
+
// scrollback_len = 10, offset = 9) the skip(10 - 9)
|
|
134
|
+
// will take 9 rows instead of 3. we need to set
|
|
135
|
+
// the upper bound to rows_len (e.g. 3)
|
|
136
|
+
.take(rows_len)
|
|
137
|
+
// same for rows_len - scrollback_offset (e.g. 3 - 9).
|
|
138
|
+
// it'll panic with overflow. we have to saturate the subtraction.
|
|
139
|
+
.chain(
|
|
140
|
+
self.rows
|
|
141
|
+
.iter()
|
|
142
|
+
.take(rows_len.saturating_sub(self.scrollback_offset)),
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
pub fn drawing_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
|
|
147
|
+
self.rows.iter()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
pub fn drawing_rows_mut(
|
|
151
|
+
&mut self,
|
|
152
|
+
) -> impl Iterator<Item = &mut crate::row::Row> {
|
|
153
|
+
self.rows.iter_mut()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
pub fn visible_row(&self, row: u16) -> Option<&crate::row::Row> {
|
|
157
|
+
self.visible_rows().nth(usize::from(row))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
pub fn drawing_row(&self, row: u16) -> Option<&crate::row::Row> {
|
|
161
|
+
self.drawing_rows().nth(usize::from(row))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
pub fn drawing_row_mut(
|
|
165
|
+
&mut self,
|
|
166
|
+
row: u16,
|
|
167
|
+
) -> Option<&mut crate::row::Row> {
|
|
168
|
+
self.drawing_rows_mut().nth(usize::from(row))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
pub fn current_row_mut(&mut self) -> &mut crate::row::Row {
|
|
172
|
+
self.drawing_row_mut(self.pos.row)
|
|
173
|
+
// we assume self.pos.row is always valid
|
|
174
|
+
.unwrap()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
pub fn visible_cell(&self, pos: Pos) -> Option<&crate::Cell> {
|
|
178
|
+
self.visible_row(pos.row).and_then(|r| r.get(pos.col))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
pub fn drawing_cell(&self, pos: Pos) -> Option<&crate::Cell> {
|
|
182
|
+
self.drawing_row(pos.row).and_then(|r| r.get(pos.col))
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
pub fn drawing_cell_mut(&mut self, pos: Pos) -> Option<&mut crate::Cell> {
|
|
186
|
+
self.drawing_row_mut(pos.row)
|
|
187
|
+
.and_then(|r| r.get_mut(pos.col))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
pub fn scrollback_len(&self) -> usize {
|
|
191
|
+
self.scrollback_len
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
pub fn scrollback(&self) -> usize {
|
|
195
|
+
self.scrollback_offset
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
pub fn set_scrollback(&mut self, rows: usize) {
|
|
199
|
+
self.scrollback_offset = rows.min(self.scrollback.len());
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
pub fn write_contents(&self, contents: &mut String) {
|
|
203
|
+
let mut wrapping = false;
|
|
204
|
+
for row in self.visible_rows() {
|
|
205
|
+
row.write_contents(contents, 0, self.size.cols, wrapping);
|
|
206
|
+
if !row.wrapped() {
|
|
207
|
+
contents.push('\n');
|
|
208
|
+
}
|
|
209
|
+
wrapping = row.wrapped();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
while contents.ends_with('\n') {
|
|
213
|
+
contents.truncate(contents.len() - 1);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
pub fn write_contents_formatted(
|
|
218
|
+
&self,
|
|
219
|
+
contents: &mut Vec<u8>,
|
|
220
|
+
) -> crate::attrs::Attrs {
|
|
221
|
+
crate::term::ClearAttrs.write_buf(contents);
|
|
222
|
+
crate::term::ClearScreen.write_buf(contents);
|
|
223
|
+
|
|
224
|
+
let mut prev_attrs = crate::attrs::Attrs::default();
|
|
225
|
+
let mut prev_pos = Pos::default();
|
|
226
|
+
let mut wrapping = false;
|
|
227
|
+
for (i, row) in self.visible_rows().enumerate() {
|
|
228
|
+
// we limit the number of cols to a u16 (see Size), so
|
|
229
|
+
// visible_rows() can never return more rows than will fit
|
|
230
|
+
let i = i.try_into().unwrap();
|
|
231
|
+
let (new_pos, new_attrs) = row.write_contents_formatted(
|
|
232
|
+
contents,
|
|
233
|
+
0,
|
|
234
|
+
self.size.cols,
|
|
235
|
+
i,
|
|
236
|
+
wrapping,
|
|
237
|
+
Some(prev_pos),
|
|
238
|
+
Some(prev_attrs),
|
|
239
|
+
);
|
|
240
|
+
prev_pos = new_pos;
|
|
241
|
+
prev_attrs = new_attrs;
|
|
242
|
+
wrapping = row.wrapped();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
self.write_cursor_position_formatted(
|
|
246
|
+
contents,
|
|
247
|
+
Some(prev_pos),
|
|
248
|
+
Some(prev_attrs),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
prev_attrs
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
pub fn write_contents_diff(
|
|
255
|
+
&self,
|
|
256
|
+
contents: &mut Vec<u8>,
|
|
257
|
+
prev: &Self,
|
|
258
|
+
mut prev_attrs: crate::attrs::Attrs,
|
|
259
|
+
) -> crate::attrs::Attrs {
|
|
260
|
+
let mut prev_pos = prev.pos;
|
|
261
|
+
let mut wrapping = false;
|
|
262
|
+
let mut prev_wrapping = false;
|
|
263
|
+
for (i, (row, prev_row)) in
|
|
264
|
+
self.visible_rows().zip(prev.visible_rows()).enumerate()
|
|
265
|
+
{
|
|
266
|
+
// we limit the number of cols to a u16 (see Size), so
|
|
267
|
+
// visible_rows() can never return more rows than will fit
|
|
268
|
+
let i = i.try_into().unwrap();
|
|
269
|
+
let (new_pos, new_attrs) = row.write_contents_diff(
|
|
270
|
+
contents,
|
|
271
|
+
prev_row,
|
|
272
|
+
0,
|
|
273
|
+
self.size.cols,
|
|
274
|
+
i,
|
|
275
|
+
wrapping,
|
|
276
|
+
prev_wrapping,
|
|
277
|
+
prev_pos,
|
|
278
|
+
prev_attrs,
|
|
279
|
+
);
|
|
280
|
+
prev_pos = new_pos;
|
|
281
|
+
prev_attrs = new_attrs;
|
|
282
|
+
wrapping = row.wrapped();
|
|
283
|
+
prev_wrapping = prev_row.wrapped();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
self.write_cursor_position_formatted(
|
|
287
|
+
contents,
|
|
288
|
+
Some(prev_pos),
|
|
289
|
+
Some(prev_attrs),
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
prev_attrs
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
pub fn write_cursor_position_formatted(
|
|
296
|
+
&self,
|
|
297
|
+
contents: &mut Vec<u8>,
|
|
298
|
+
prev_pos: Option<Pos>,
|
|
299
|
+
prev_attrs: Option<crate::attrs::Attrs>,
|
|
300
|
+
) {
|
|
301
|
+
let prev_attrs = prev_attrs.unwrap_or_default();
|
|
302
|
+
// writing a character to the last column of a row doesn't wrap the
|
|
303
|
+
// cursor immediately - it waits until the next character is actually
|
|
304
|
+
// drawn. it is only possible for the cursor to have this kind of
|
|
305
|
+
// position after drawing a character though, so if we end in this
|
|
306
|
+
// position, we need to redraw the character at the end of the row.
|
|
307
|
+
if prev_pos != Some(self.pos) && self.pos.col >= self.size.cols {
|
|
308
|
+
let mut pos = Pos {
|
|
309
|
+
row: self.pos.row,
|
|
310
|
+
col: self.size.cols - 1,
|
|
311
|
+
};
|
|
312
|
+
if self
|
|
313
|
+
.drawing_cell(pos)
|
|
314
|
+
// we assume self.pos.row is always valid, and self.size.cols
|
|
315
|
+
// - 1 is always a valid column
|
|
316
|
+
.unwrap()
|
|
317
|
+
.is_wide_continuation()
|
|
318
|
+
{
|
|
319
|
+
pos.col = self.size.cols - 2;
|
|
320
|
+
}
|
|
321
|
+
let cell =
|
|
322
|
+
// we assume self.pos.row is always valid, and self.size.cols
|
|
323
|
+
// - 2 must be a valid column because self.size.cols - 1 is
|
|
324
|
+
// always valid and we just checked that the cell at
|
|
325
|
+
// self.size.cols - 1 is a wide continuation character, which
|
|
326
|
+
// means that the first half of the wide character must be
|
|
327
|
+
// before it
|
|
328
|
+
self.drawing_cell(pos).unwrap();
|
|
329
|
+
if cell.has_contents() {
|
|
330
|
+
if let Some(prev_pos) = prev_pos {
|
|
331
|
+
crate::term::MoveFromTo::new(prev_pos, pos)
|
|
332
|
+
.write_buf(contents);
|
|
333
|
+
} else {
|
|
334
|
+
crate::term::MoveTo::new(pos).write_buf(contents);
|
|
335
|
+
}
|
|
336
|
+
cell.attrs().write_escape_code_diff(contents, &prev_attrs);
|
|
337
|
+
contents.extend(cell.contents().as_bytes());
|
|
338
|
+
prev_attrs.write_escape_code_diff(contents, cell.attrs());
|
|
339
|
+
} else {
|
|
340
|
+
// if the cell doesn't have contents, we can't have gotten
|
|
341
|
+
// here by drawing a character in the last column. this means
|
|
342
|
+
// that as far as i'm aware, we have to have reached here from
|
|
343
|
+
// a newline when we were already after the end of an earlier
|
|
344
|
+
// row. in the case where we are already after the end of an
|
|
345
|
+
// earlier row, we can just write a few newlines, otherwise we
|
|
346
|
+
// also need to do the same as above to get ourselves to after
|
|
347
|
+
// the end of a row.
|
|
348
|
+
let mut found = false;
|
|
349
|
+
for i in (0..self.pos.row).rev() {
|
|
350
|
+
pos.row = i;
|
|
351
|
+
pos.col = self.size.cols - 1;
|
|
352
|
+
if self
|
|
353
|
+
.drawing_cell(pos)
|
|
354
|
+
// i is always less than self.pos.row, which we assume
|
|
355
|
+
// to be always valid, so it must also be valid.
|
|
356
|
+
// self.size.cols - 1 is always a valid col.
|
|
357
|
+
.unwrap()
|
|
358
|
+
.is_wide_continuation()
|
|
359
|
+
{
|
|
360
|
+
pos.col = self.size.cols - 2;
|
|
361
|
+
}
|
|
362
|
+
let cell = self
|
|
363
|
+
.drawing_cell(pos)
|
|
364
|
+
// i is always less than self.pos.row, which we assume
|
|
365
|
+
// to be always valid, so it must also be valid.
|
|
366
|
+
// self.size.cols - 2 is valid because self.size.cols
|
|
367
|
+
// - 1 is always valid, and col gets set to
|
|
368
|
+
// self.size.cols - 2 when the cell at self.size.cols
|
|
369
|
+
// - 1 is a wide continuation character, meaning that
|
|
370
|
+
// the first half of the wide character must be before
|
|
371
|
+
// it
|
|
372
|
+
.unwrap();
|
|
373
|
+
if cell.has_contents() {
|
|
374
|
+
if let Some(prev_pos) = prev_pos {
|
|
375
|
+
if prev_pos.row != i
|
|
376
|
+
|| prev_pos.col < self.size.cols
|
|
377
|
+
{
|
|
378
|
+
crate::term::MoveFromTo::new(prev_pos, pos)
|
|
379
|
+
.write_buf(contents);
|
|
380
|
+
cell.attrs().write_escape_code_diff(
|
|
381
|
+
contents,
|
|
382
|
+
&prev_attrs,
|
|
383
|
+
);
|
|
384
|
+
contents.extend(cell.contents().as_bytes());
|
|
385
|
+
prev_attrs.write_escape_code_diff(
|
|
386
|
+
contents,
|
|
387
|
+
cell.attrs(),
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
crate::term::MoveTo::new(pos).write_buf(contents);
|
|
392
|
+
cell.attrs().write_escape_code_diff(
|
|
393
|
+
contents,
|
|
394
|
+
&prev_attrs,
|
|
395
|
+
);
|
|
396
|
+
contents.extend(cell.contents().as_bytes());
|
|
397
|
+
prev_attrs.write_escape_code_diff(
|
|
398
|
+
contents,
|
|
399
|
+
cell.attrs(),
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
contents.extend(
|
|
403
|
+
"\n".repeat(usize::from(self.pos.row - i))
|
|
404
|
+
.as_bytes(),
|
|
405
|
+
);
|
|
406
|
+
found = true;
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// this can happen if you get the cursor off the end of a row,
|
|
412
|
+
// and then do something to clear the end of the current row
|
|
413
|
+
// without moving the cursor (IL, DL, ED, EL, etc). we know
|
|
414
|
+
// there can't be something in the last column because we
|
|
415
|
+
// would have caught that above, so it should be safe to
|
|
416
|
+
// overwrite it.
|
|
417
|
+
if !found {
|
|
418
|
+
pos = Pos {
|
|
419
|
+
row: self.pos.row,
|
|
420
|
+
col: self.size.cols - 1,
|
|
421
|
+
};
|
|
422
|
+
if let Some(prev_pos) = prev_pos {
|
|
423
|
+
crate::term::MoveFromTo::new(prev_pos, pos)
|
|
424
|
+
.write_buf(contents);
|
|
425
|
+
} else {
|
|
426
|
+
crate::term::MoveTo::new(pos).write_buf(contents);
|
|
427
|
+
}
|
|
428
|
+
contents.push(b' ');
|
|
429
|
+
// we know that the cell has no contents, but it still may
|
|
430
|
+
// have drawing attributes (background color, etc)
|
|
431
|
+
let end_cell = self
|
|
432
|
+
.drawing_cell(pos)
|
|
433
|
+
// we assume self.pos.row is always valid, and
|
|
434
|
+
// self.size.cols - 1 is always a valid column
|
|
435
|
+
.unwrap();
|
|
436
|
+
end_cell
|
|
437
|
+
.attrs()
|
|
438
|
+
.write_escape_code_diff(contents, &prev_attrs);
|
|
439
|
+
crate::term::SaveCursor.write_buf(contents);
|
|
440
|
+
crate::term::Backspace.write_buf(contents);
|
|
441
|
+
crate::term::EraseChar::new(1).write_buf(contents);
|
|
442
|
+
crate::term::RestoreCursor.write_buf(contents);
|
|
443
|
+
prev_attrs
|
|
444
|
+
.write_escape_code_diff(contents, end_cell.attrs());
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
} else if let Some(prev_pos) = prev_pos {
|
|
448
|
+
crate::term::MoveFromTo::new(prev_pos, self.pos)
|
|
449
|
+
.write_buf(contents);
|
|
450
|
+
} else {
|
|
451
|
+
crate::term::MoveTo::new(self.pos).write_buf(contents);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
pub fn erase_all(&mut self, attrs: crate::attrs::Attrs) {
|
|
456
|
+
for row in self.drawing_rows_mut() {
|
|
457
|
+
row.clear(attrs);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
pub fn erase_all_forward(&mut self, attrs: crate::attrs::Attrs) {
|
|
462
|
+
let pos = self.pos;
|
|
463
|
+
for row in self.drawing_rows_mut().skip(usize::from(pos.row) + 1) {
|
|
464
|
+
row.clear(attrs);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
self.erase_row_forward(attrs);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
pub fn erase_all_backward(&mut self, attrs: crate::attrs::Attrs) {
|
|
471
|
+
let pos = self.pos;
|
|
472
|
+
for row in self.drawing_rows_mut().take(usize::from(pos.row)) {
|
|
473
|
+
row.clear(attrs);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
self.erase_row_backward(attrs);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
pub fn erase_row(&mut self, attrs: crate::attrs::Attrs) {
|
|
480
|
+
self.current_row_mut().clear(attrs);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
pub fn erase_row_forward(&mut self, attrs: crate::attrs::Attrs) {
|
|
484
|
+
let size = self.size;
|
|
485
|
+
let pos = self.pos;
|
|
486
|
+
let row = self.current_row_mut();
|
|
487
|
+
for col in pos.col..size.cols {
|
|
488
|
+
row.erase(col, attrs);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
pub fn erase_row_backward(&mut self, attrs: crate::attrs::Attrs) {
|
|
493
|
+
let size = self.size;
|
|
494
|
+
let pos = self.pos;
|
|
495
|
+
let row = self.current_row_mut();
|
|
496
|
+
for col in 0..=pos.col.min(size.cols - 1) {
|
|
497
|
+
row.erase(col, attrs);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
pub fn insert_cells(&mut self, count: u16) {
|
|
502
|
+
let size = self.size;
|
|
503
|
+
let pos = self.pos;
|
|
504
|
+
let wide = pos.col < size.cols
|
|
505
|
+
&& self
|
|
506
|
+
.drawing_cell(pos)
|
|
507
|
+
// we assume self.pos.row is always valid, and we know we are
|
|
508
|
+
// not off the end of a row because we just checked pos.col <
|
|
509
|
+
// size.cols
|
|
510
|
+
.unwrap()
|
|
511
|
+
.is_wide_continuation();
|
|
512
|
+
let row = self.current_row_mut();
|
|
513
|
+
for _ in 0..count {
|
|
514
|
+
if wide {
|
|
515
|
+
row.get_mut(pos.col).unwrap().set_wide_continuation(false);
|
|
516
|
+
}
|
|
517
|
+
row.insert(pos.col, crate::Cell::new());
|
|
518
|
+
if wide {
|
|
519
|
+
row.get_mut(pos.col).unwrap().set_wide_continuation(true);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
row.truncate(size.cols);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
pub fn delete_cells(&mut self, count: u16) {
|
|
526
|
+
let size = self.size;
|
|
527
|
+
let pos = self.pos;
|
|
528
|
+
let row = self.current_row_mut();
|
|
529
|
+
for _ in 0..(count.min(size.cols - pos.col)) {
|
|
530
|
+
row.remove(pos.col);
|
|
531
|
+
}
|
|
532
|
+
row.resize(size.cols, crate::Cell::new());
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
pub fn erase_cells(&mut self, count: u16, attrs: crate::attrs::Attrs) {
|
|
536
|
+
let size = self.size;
|
|
537
|
+
let pos = self.pos;
|
|
538
|
+
let row = self.current_row_mut();
|
|
539
|
+
for col in pos.col..((pos.col.saturating_add(count)).min(size.cols)) {
|
|
540
|
+
row.erase(col, attrs);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
pub fn insert_lines(&mut self, count: u16) {
|
|
545
|
+
for _ in 0..count {
|
|
546
|
+
self.rows.remove(usize::from(self.scroll_bottom));
|
|
547
|
+
self.rows.insert(usize::from(self.pos.row), self.new_row());
|
|
548
|
+
// self.scroll_bottom is maintained to always be a valid row
|
|
549
|
+
self.rows[usize::from(self.scroll_bottom)].wrap(false);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
pub fn delete_lines(&mut self, count: u16) {
|
|
554
|
+
for _ in 0..(count.min(self.size.rows - self.pos.row)) {
|
|
555
|
+
self.rows
|
|
556
|
+
.insert(usize::from(self.scroll_bottom) + 1, self.new_row());
|
|
557
|
+
self.rows.remove(usize::from(self.pos.row));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
pub fn scroll_up(&mut self, count: u16) {
|
|
562
|
+
for _ in 0..(count.min(self.size.rows - self.scroll_top)) {
|
|
563
|
+
self.rows
|
|
564
|
+
.insert(usize::from(self.scroll_bottom) + 1, self.new_row());
|
|
565
|
+
let removed = self.rows.remove(usize::from(self.scroll_top));
|
|
566
|
+
if self.scrollback_len > 0 && !self.scroll_region_active() {
|
|
567
|
+
self.scrollback.push_back(removed);
|
|
568
|
+
while self.scrollback.len() > self.scrollback_len {
|
|
569
|
+
self.scrollback.pop_front();
|
|
570
|
+
}
|
|
571
|
+
if self.scrollback_offset > 0 {
|
|
572
|
+
self.scrollback_offset =
|
|
573
|
+
self.scrollback.len().min(self.scrollback_offset + 1);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
pub fn scroll_down(&mut self, count: u16) {
|
|
580
|
+
for _ in 0..count {
|
|
581
|
+
self.rows.remove(usize::from(self.scroll_bottom));
|
|
582
|
+
self.rows
|
|
583
|
+
.insert(usize::from(self.scroll_top), self.new_row());
|
|
584
|
+
// self.scroll_bottom is maintained to always be a valid row
|
|
585
|
+
self.rows[usize::from(self.scroll_bottom)].wrap(false);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
pub fn set_scroll_region(&mut self, top: u16, bottom: u16) {
|
|
590
|
+
let bottom = bottom.min(self.size().rows - 1);
|
|
591
|
+
if top < bottom {
|
|
592
|
+
self.scroll_top = top;
|
|
593
|
+
self.scroll_bottom = bottom;
|
|
594
|
+
} else {
|
|
595
|
+
self.scroll_top = 0;
|
|
596
|
+
self.scroll_bottom = self.size().rows - 1;
|
|
597
|
+
}
|
|
598
|
+
self.pos.row = self.scroll_top;
|
|
599
|
+
self.pos.col = 0;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
fn in_scroll_region(&self) -> bool {
|
|
603
|
+
self.pos.row >= self.scroll_top && self.pos.row <= self.scroll_bottom
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
fn scroll_region_active(&self) -> bool {
|
|
607
|
+
self.scroll_top != 0 || self.scroll_bottom != self.size.rows - 1
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
pub fn set_origin_mode(&mut self, mode: bool) {
|
|
611
|
+
self.origin_mode = mode;
|
|
612
|
+
self.set_pos(Pos { row: 0, col: 0 });
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
pub fn row_inc_clamp(&mut self, count: u16) {
|
|
616
|
+
let in_scroll_region = self.in_scroll_region();
|
|
617
|
+
self.pos.row = self.pos.row.saturating_add(count);
|
|
618
|
+
self.row_clamp_bottom(in_scroll_region);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
pub fn row_inc_scroll(&mut self, count: u16) -> u16 {
|
|
622
|
+
let in_scroll_region = self.in_scroll_region();
|
|
623
|
+
self.pos.row = self.pos.row.saturating_add(count);
|
|
624
|
+
let lines = self.row_clamp_bottom(in_scroll_region);
|
|
625
|
+
if in_scroll_region {
|
|
626
|
+
self.scroll_up(lines);
|
|
627
|
+
lines
|
|
628
|
+
} else {
|
|
629
|
+
0
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
pub fn row_dec_clamp(&mut self, count: u16) {
|
|
634
|
+
let in_scroll_region = self.in_scroll_region();
|
|
635
|
+
self.pos.row = self.pos.row.saturating_sub(count);
|
|
636
|
+
self.row_clamp_top(in_scroll_region);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
pub fn row_dec_scroll(&mut self, count: u16) {
|
|
640
|
+
let in_scroll_region = self.in_scroll_region();
|
|
641
|
+
// need to account for clamping by both row_clamp_top and by
|
|
642
|
+
// saturating_sub
|
|
643
|
+
let extra_lines = count.saturating_sub(self.pos.row);
|
|
644
|
+
self.pos.row = self.pos.row.saturating_sub(count);
|
|
645
|
+
let lines = self.row_clamp_top(in_scroll_region);
|
|
646
|
+
self.scroll_down(lines + extra_lines);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
pub fn row_set(&mut self, i: u16) {
|
|
650
|
+
self.pos.row = i;
|
|
651
|
+
self.row_clamp();
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
pub fn col_inc(&mut self, count: u16) {
|
|
655
|
+
self.pos.col = self.pos.col.saturating_add(count);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
pub fn col_inc_clamp(&mut self, count: u16) {
|
|
659
|
+
self.pos.col = self.pos.col.saturating_add(count);
|
|
660
|
+
self.col_clamp();
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
pub fn col_dec(&mut self, count: u16) {
|
|
664
|
+
self.pos.col = self.pos.col.saturating_sub(count);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
pub fn col_tab(&mut self) {
|
|
668
|
+
self.pos.col -= self.pos.col % 8;
|
|
669
|
+
self.pos.col += 8;
|
|
670
|
+
self.col_clamp();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
pub fn col_set(&mut self, i: u16) {
|
|
674
|
+
self.pos.col = i;
|
|
675
|
+
self.col_clamp();
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
pub fn col_wrap(&mut self, width: u16, wrap: bool) {
|
|
679
|
+
if self.pos.col > self.size.cols - width {
|
|
680
|
+
let mut prev_pos = self.pos;
|
|
681
|
+
self.pos.col = 0;
|
|
682
|
+
let scrolled = self.row_inc_scroll(1);
|
|
683
|
+
prev_pos.row -= scrolled;
|
|
684
|
+
let new_pos = self.pos;
|
|
685
|
+
self.drawing_row_mut(prev_pos.row)
|
|
686
|
+
// we assume self.pos.row is always valid, and so prev_pos.row
|
|
687
|
+
// must be valid because it is always less than or equal to
|
|
688
|
+
// self.pos.row
|
|
689
|
+
.unwrap()
|
|
690
|
+
.wrap(wrap && prev_pos.row + 1 == new_pos.row);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
fn row_clamp_top(&mut self, limit_to_scroll_region: bool) -> u16 {
|
|
695
|
+
if limit_to_scroll_region && self.pos.row < self.scroll_top {
|
|
696
|
+
let rows = self.scroll_top - self.pos.row;
|
|
697
|
+
self.pos.row = self.scroll_top;
|
|
698
|
+
rows
|
|
699
|
+
} else {
|
|
700
|
+
0
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
fn row_clamp_bottom(&mut self, limit_to_scroll_region: bool) -> u16 {
|
|
705
|
+
let bottom = if limit_to_scroll_region {
|
|
706
|
+
self.scroll_bottom
|
|
707
|
+
} else {
|
|
708
|
+
self.size.rows - 1
|
|
709
|
+
};
|
|
710
|
+
if self.pos.row > bottom {
|
|
711
|
+
let rows = self.pos.row - bottom;
|
|
712
|
+
self.pos.row = bottom;
|
|
713
|
+
rows
|
|
714
|
+
} else {
|
|
715
|
+
0
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
fn row_clamp(&mut self) {
|
|
720
|
+
if self.pos.row > self.size.rows - 1 {
|
|
721
|
+
self.pos.row = self.size.rows - 1;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
fn col_clamp(&mut self) {
|
|
726
|
+
if self.pos.col > self.size.cols - 1 {
|
|
727
|
+
self.pos.col = self.size.cols - 1;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
|
733
|
+
pub struct Size {
|
|
734
|
+
pub rows: u16,
|
|
735
|
+
pub cols: u16,
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
|
739
|
+
pub struct Pos {
|
|
740
|
+
pub row: u16,
|
|
741
|
+
pub col: u16,
|
|
742
|
+
}
|