Skip to content

Commit 95d8632

Browse files
authored
examples: support unicode in the text_editor.v buffer (#13269)
1 parent 28ddd84 commit 95d8632

File tree

1 file changed

+55
-31
lines changed

1 file changed

+55
-31
lines changed

examples/term.ui/text_editor.v

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import strings
66
import os
77
import math
88
import term.ui as tui
9+
import encoding.utf8
10+
11+
const rune_digits = [`0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`]
912

1013
enum Movement {
1114
up
@@ -131,7 +134,7 @@ fn (b Buffer) raw() string {
131134
}
132135

133136
fn (b Buffer) view(from int, to int) View {
134-
l := b.cur_line()
137+
l := b.cur_line().runes()
135138
mut x := 0
136139
for i := 0; i < b.cursor.pos_x && i < l.len; i++ {
137140
if l[i] == `\t` {
@@ -174,7 +177,7 @@ fn (b Buffer) cursor_index() int {
174177
i += b.cursor.pos_x
175178
break
176179
}
177-
i += line.len + 1
180+
i += line.runes().len + 1
178181
}
179182
return i
180183
}
@@ -185,22 +188,22 @@ fn (mut b Buffer) put(s string) {
185188
if b.lines.len == 0 {
186189
b.lines.prepend('')
187190
}
188-
line := b.lines[y]
189-
l, r := line[..x], line[x..]
191+
line := b.lines[y].runes()
192+
l, r := line[..x].string(), line[x..].string()
190193
if has_line_ending {
191194
mut lines := s.split('\n')
192195
lines[0] = l + lines[0]
193196
lines[lines.len - 1] += r
194197
b.lines.delete(y)
195198
b.lines.insert(y, lines)
196-
last := lines[lines.len - 1]
199+
last := lines[lines.len - 1].runes()
197200
b.cursor.set(last.len, y + lines.len - 1)
198201
if s == '\n' {
199202
b.cursor.set(0, b.cursor.pos_y)
200203
}
201204
} else {
202205
b.lines[y] = l + s + r
203-
b.cursor.set(x + s.len, y)
206+
b.cursor.set(x + s.runes().len, y)
204207
}
205208
$if debug {
206209
flat := s.replace('\n', r'\n')
@@ -217,24 +220,35 @@ fn (mut b Buffer) del(amount int) string {
217220
if x == 0 && y == 0 {
218221
return ''
219222
}
220-
} else if x >= b.cur_line().len && y >= b.lines.len - 1 {
223+
} else if x >= b.cur_line().runes().len && y >= b.lines.len - 1 {
221224
return ''
222225
}
223226
mut removed := ''
224227
if amount < 0 { // backspace (backward)
225228
i := b.cursor_index()
226-
removed = b.raw()[i + amount..i]
229+
raw_runes := b.raw().runes()
230+
removed = raw_runes[i + amount..i].string()
227231
mut left := amount * -1
228232
for li := y; li >= 0 && left > 0; li-- {
229-
ln := b.lines[li]
230-
if left > ln.len {
233+
ln := b.lines[li].runes()
234+
if left == ln.len + 1 { // All of the line + 1 - since we're going backwards the "+1" is the line break delimiter.
235+
b.lines.delete(li)
236+
left = 0
237+
if y == 0 {
238+
return ''
239+
}
240+
line_above := b.lines[li - 1].runes()
241+
b.cursor.pos_x = line_above.len
242+
b.cursor.pos_y--
243+
break
244+
} else if left > ln.len {
231245
b.lines.delete(li)
232246
if ln.len == 0 { // line break delimiter
233247
left--
234248
if y == 0 {
235249
return ''
236250
}
237-
line_above := b.lines[li - 1]
251+
line_above := b.lines[li - 1].runes()
238252
b.cursor.pos_x = line_above.len
239253
} else {
240254
left -= ln.len
@@ -245,22 +259,23 @@ fn (mut b Buffer) del(amount int) string {
245259
if y == 0 {
246260
return ''
247261
}
248-
line_above := b.lines[li - 1]
262+
line_above := b.lines[li - 1].runes()
249263
if ln.len == 0 { // at line break
250264
b.lines.delete(li)
251265
b.cursor.pos_y--
252266
b.cursor.pos_x = line_above.len
253267
} else {
254-
b.lines[li - 1] = line_above + ln
268+
b.lines[li - 1] = line_above.string() + ln.string()
255269
b.lines.delete(li)
256270
b.cursor.pos_y--
257271
b.cursor.pos_x = line_above.len
258272
}
259273
} else if x == 1 {
260-
b.lines[li] = b.lines[li][left..]
274+
runes := b.lines[li].runes()
275+
b.lines[li] = runes[left..].string()
261276
b.cursor.pos_x = 0
262277
} else {
263-
b.lines[li] = ln[..x - left] + ln[x..]
278+
b.lines[li] = ln[..x - left].string() + ln[x..].string()
264279
b.cursor.pos_x -= left
265280
}
266281
left = 0
@@ -269,13 +284,20 @@ fn (mut b Buffer) del(amount int) string {
269284
}
270285
} else { // delete (forward)
271286
i := b.cursor_index() + 1
272-
removed = b.raw()[i - amount..i]
287+
raw_buffer := b.raw().runes()
288+
from_i := i
289+
mut to_i := i + amount
290+
291+
if to_i > raw_buffer.len {
292+
to_i = raw_buffer.len
293+
}
294+
removed = raw_buffer[from_i..to_i].string()
273295
mut left := amount
274296
for li := y; li >= 0 && left > 0; li++ {
275-
ln := b.lines[li]
297+
ln := b.lines[li].runes()
276298
if x == ln.len { // at line end
277299
if y + 1 <= b.lines.len {
278-
b.lines[li] = ln + b.lines[y + 1]
300+
b.lines[li] = ln.string() + b.lines[y + 1]
279301
b.lines.delete(y + 1)
280302
left--
281303
b.del(left)
@@ -284,7 +306,7 @@ fn (mut b Buffer) del(amount int) string {
284306
b.lines.delete(li)
285307
left -= ln.len
286308
} else {
287-
b.lines[li] = ln[..x] + ln[x + left..]
309+
b.lines[li] = ln[..x].string() + ln[x + left..].string()
288310
left = 0
289311
}
290312
}
@@ -309,15 +331,15 @@ fn (mut b Buffer) free() {
309331
fn (mut b Buffer) move_updown(amount int) {
310332
b.cursor.move(0, amount)
311333
// Check the move
312-
line := b.cur_line()
334+
line := b.cur_line().runes()
313335
if b.cursor.pos_x > line.len {
314336
b.cursor.set(line.len, b.cursor.pos_y)
315337
}
316338
}
317339

318340
// move_cursor will navigate the cursor within the buffer bounds
319341
fn (mut b Buffer) move_cursor(amount int, movement Movement) {
320-
cur_line := b.cur_line()
342+
cur_line := b.cur_line().runes()
321343
match movement {
322344
.up {
323345
if b.cursor.pos_y - amount >= 0 {
@@ -341,7 +363,7 @@ fn (mut b Buffer) move_cursor(amount int, movement Movement) {
341363
if b.cursor.pos_x - amount >= 0 {
342364
b.cursor.move(-amount, 0)
343365
} else if b.cursor.pos_y > 0 {
344-
b.cursor.set(b.line(b.cursor.pos_y - 1).len, b.cursor.pos_y - 1)
366+
b.cursor.set(b.line(b.cursor.pos_y - 1).runes().len, b.cursor.pos_y - 1)
345367
}
346368
}
347369
.right {
@@ -362,25 +384,26 @@ fn (mut b Buffer) move_cursor(amount int, movement Movement) {
362384

363385
fn (mut b Buffer) move_to_word(movement Movement) {
364386
a := if movement == .left { -1 } else { 1 }
365-
mut line := b.cur_line()
387+
388+
mut line := b.cur_line().runes()
366389
mut x, mut y := b.cursor.pos_x, b.cursor.pos_y
367390
if x + a < 0 && y > 0 {
368391
y--
369-
line = b.line(b.cursor.pos_y - 1)
392+
line = b.line(b.cursor.pos_y - 1).runes()
370393
x = line.len
371394
} else if x + a >= line.len && y + 1 < b.lines.len {
372395
y++
373-
line = b.line(b.cursor.pos_y + 1)
396+
line = b.line(b.cursor.pos_y + 1).runes()
374397
x = 0
375398
}
376399
// first, move past all non-`a-zA-Z0-9_` characters
377-
for x + a >= 0 && x + a < line.len && !(line[x + a].is_letter()
378-
|| line[x + a].is_digit() || line[x + a] == `_`) {
400+
for x + a >= 0 && x + a < line.len && !(utf8.is_letter(line[x + a])
401+
|| line[x + a] in rune_digits || line[x + a] == `_`) {
379402
x += a
380403
}
381404
// then, move past all the letters and numbers
382-
for x + a >= 0 && x + a < line.len && (line[x + a].is_letter()
383-
|| line[x + a].is_digit() || line[x + a] == `_`) {
405+
for x + a >= 0 && x + a < line.len && (utf8.is_letter(line[x + a])
406+
|| line[x + a] in rune_digits || line[x + a] == `_`) {
384407
x += a
385408
}
386409
// if the cursor is out of bounds, move it to the next/previous line
@@ -457,7 +480,7 @@ fn (a &App) view_height() int {
457480
fn (mut a App) magnet_cursor_x() {
458481
mut buffer := a.ed
459482
if buffer.cursor.pos_x < a.magnet_x {
460-
if a.magnet_x < buffer.cur_line().len {
483+
if a.magnet_x < buffer.cur_line().runes().len {
461484
move_x := a.magnet_x - buffer.cursor.pos_x
462485
buffer.move_cursor(move_x, .right)
463486
}
@@ -555,7 +578,8 @@ fn event(e &tui.Event, x voidptr) {
555578
return
556579
}
557580
}
558-
buffer.put(e.utf8.bytes().bytestr())
581+
582+
buffer.put(e.utf8)
559583
}
560584
}
561585
} else if e.typ == .mouse_scroll {

0 commit comments

Comments
 (0)