diff --git a/examples/term.ui/text_editor.v b/examples/term.ui/text_editor.v index ad9c77d287..8d19be5baa 100644 --- a/examples/term.ui/text_editor.v +++ b/examples/term.ui/text_editor.v @@ -7,8 +7,36 @@ import os import math import term.ui as tui import encoding.utf8 +import encoding.utf8.east_asian -const rune_digits = [`0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`] +const ( + rune_digits = [`0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`] + + zero_width_unicode = [ + `\u034f`, // U+034F COMBINING GRAPHEME JOINER + `\u061c`, // U+061C ARABIC LETTER MARK + `\u17b4`, // U+17B4 KHMER VOWEL INHERENT AQ + `\u17b5`, // U+17B5 KHMER VOWEL INHERENT AA + `\u200a`, // U+200A HAIR SPACE + `\u200b`, // U+200B ZERO WIDTH SPACE + `\u200c`, // U+200C ZERO WIDTH NON-JOINER + `\u200d`, // U+200D ZERO WIDTH JOINER + `\u200e`, // U+200E LEFT-TO-RIGHT MARK + `\u200f`, // U+200F RIGHT-TO-LEFT MARK + `\u2060`, // U+2060 WORD JOINER + `\u2061`, // U+2061 FUNCTION APPLICATION + `\u2062`, // U+2062 INVISIBLE TIMES + `\u2063`, // U+2063 INVISIBLE SEPARATOR + `\u2064`, // U+2064 INVISIBLE PLUS + `\u206a`, // U+206A INHIBIT SYMMETRIC SWAPPING + `\u206b`, // U+206B ACTIVATE SYMMETRIC SWAPPING + `\u206c`, // U+206C INHIBIT ARABIC FORM SHAPING + `\u206d`, // U+206D ACTIVATE ARABIC FORM SHAPING + `\u206e`, // U+206E NATIONAL DIGIT SHAPES + `\u206f`, // U+206F NOMINAL DIGIT SHAPES + `\ufeff`, // U+FEFF ZERO WIDTH NO-BREAK SPACE + ] +) enum Movement { up @@ -170,6 +198,14 @@ fn (b Buffer) cur_line() string { return b.line(b.cursor.pos_y) } +fn (b Buffer) cur_slice() string { + line := b.line(b.cursor.pos_y).runes() + if b.cursor.pos_x == 0 || b.cursor.pos_x > line.len { + return '' + } + return line[..b.cursor.pos_x].string() +} + fn (b Buffer) cursor_index() int { mut i := 0 for y, line in b.lines { @@ -501,7 +537,17 @@ fn frame(x voidptr) { view := ed.view(a.viewport, scroll_limit + a.viewport) a.tui.draw_text(0, 0, view.raw) a.footer() - a.tui.set_cursor_position(view.cursor.pos_x + 1, ed.cursor.pos_y + 1 - a.viewport) + + // Unicode: Handle correct mapping of cursor X position in terminal. + mut ch_x := view.cursor.pos_x + mut sl := ed.cur_slice().replace('\t', ' '.repeat(ed.tab_width)) + if sl.len > 0 { + // Strip out any zero-width codepoints. + sl = sl.runes().filter(it !in zero_width_unicode).string() + ch_x = east_asian.display_width(sl, 1) + } + + a.tui.set_cursor_position(ch_x + 1, ed.cursor.pos_y + 1 - a.viewport) a.tui.flush() }