diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2016-02-13 12:22:18 +0100 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2016-02-13 13:32:43 +0100 |
| commit | cc7ce30fa6a2ebdb2e14d589b11520757e5a20e3 (patch) | |
| tree | 8852d4330cf14f4cff292dd1b7e7c14da1aabb9d /text-motions.c | |
| parent | e0b157f56694a5b5e535083c8fc0bc0e1194c9dc (diff) | |
| download | vis-cc7ce30fa6a2ebdb2e14d589b11520757e5a20e3.tar.gz vis-cc7ce30fa6a2ebdb2e14d589b11520757e5a20e3.tar.xz | |
text-motion: add functions to get/set position based on display width
This is inherently a tricky thing to do because we cannot rely on
the current display state. The reason being that the position/cursor
which is modified might not currently be in the visible area.
Tabs are a particular problem because they have a variable display
width. However this new code is certainly not worse than the current
behaviour which relies on text_line_char_{get,set} and thus simply
counts graphemes.
Not yet completely convinced that this is the right approach.
Diffstat (limited to 'text-motions.c')
| -rw-r--r-- | text-motions.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/text-motions.c b/text-motions.c index 6ca2f8f..04ccab1 100644 --- a/text-motions.c +++ b/text-motions.c @@ -15,6 +15,9 @@ */ #include <ctype.h> #include <string.h> +#include <stdlib.h> +#include <wchar.h> +#include <errno.h> #include "text-motions.h" #include "text-util.h" #include "util.h" @@ -218,6 +221,80 @@ int text_line_char_get(Text *txt, size_t pos) { return count; } +int text_line_width_get(Text *txt, size_t pos) { + int width = 0; + mbstate_t ps = { 0 }; + size_t bol = text_line_begin(txt, pos); + Iterator it = text_iterator_get(txt, bol); + + while (it.pos < pos) { + char buf[MB_CUR_MAX]; + size_t len = text_bytes_get(txt, it.pos, sizeof buf, buf); + if (len == 0 || buf[0] == '\r' || buf[0] == '\n') + break; + wchar_t wc; + size_t wclen = mbrtowc(&wc, buf, len, &ps); + if (wclen == (size_t)-1 && errno == EILSEQ) { + /* assume a replacement symbol will be displayed */ + width++; + } else if (wclen == (size_t)-2) { + /* do nothing, advance to next character */ + } else if (wclen == 0) { + /* assume NUL byte will be displayed as ^@ */ + width += 2; + } else if (buf[0] == '\t') { + width++; + } else { + int w = wcwidth(wc); + if (w == -1) + w = 2; /* assume non-printable will be displayed as ^{char} */ + width += w; + } + + if (!text_iterator_codepoint_next(&it, NULL)) + break; + } + + return width; +} + +size_t text_line_width_set(Text *txt, size_t pos, int width) { + int cur_width = 0; + mbstate_t ps = { 0 }; + size_t bol = text_line_begin(txt, pos); + Iterator it = text_iterator_get(txt, bol); + + for (;;) { + char buf[MB_CUR_MAX]; + size_t len = text_bytes_get(txt, it.pos, sizeof buf, buf); + if (len == 0 || buf[0] == '\r' || buf[0] == '\n') + break; + wchar_t wc; + size_t wclen = mbrtowc(&wc, buf, len, &ps); + if (wclen == (size_t)-1 && errno == EILSEQ) { + /* assume a replacement symbol will be displayed */ + cur_width++; + } else if (wclen == (size_t)-2) { + /* do nothing, advance to next character */ + } else if (wclen == 0) { + /* assume NUL byte will be displayed as ^@ */ + cur_width += 2; + } else if (buf[0] == '\t') { + cur_width++; + } else { + int w = wcwidth(wc); + if (w == -1) + w = 2; /* assume non-printable will be displayed as ^{char} */ + cur_width += w; + } + + if (cur_width >= width || !text_iterator_codepoint_next(&it, NULL)) + break; + } + + return it.pos; +} + size_t text_line_char_next(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, pos); |
