/** * 计算字符串的显示宽度 * @param str UTF-8编码字符串 * @param size 字符串的长度 * @return 字符串在编辑器终端上的显示宽度 */ static size_t display_width(const char *str, size_t size) { size_t width = 0; for (wchar_t wc = next_wchar(&str, &size); wc && wc != WEOF; ) { width += display_width_wchar(wc); wc = next_wchar(&str, &size); } return width; }
static const char *seek_to_display_width(const char *str, size_t size, size_t width) { const char *last = str; for (wchar_t wc = next_wchar(&str, &size); wc && wc != WEOF; ) { size_t w = display_width_wchar(wc); if (w > width) { return last; } else { last = str; width -= w; } wc = next_wchar(&str, &size); } return last; }
static const char *update_quote_width(const char *begin, const char *end, bool utf8, size_t *width) { const char *ptr = begin; while (ptr < end && *width) { if (utf8) { size_t left = end - ptr; const char *next = ptr; wchar_t wc = next_wchar(&next, &left); if (!wc || wc == WEOF) return ptr; int w = fb_wcwidth(wc); if (w <= 0) w = next - ptr > 0 ? next - ptr : 1; if (*width < w) return ptr; *width -= w; ptr = next; } else { if (*ptr & 0x80) { if (*width < 2) return ptr; *width -= 2; ptr += 2; } else { *width -= 1; ++ptr; } } } return ptr > end ? end : ptr; }
size_t screen_display_width(const char *ptr, bool utf8) { size_t width = 0; bool ansi = false; while (*ptr) { if (ansi) { if (isalpha(*ptr)) ansi = false; } else { if (*ptr == '\033') { ansi = true; } else { if (utf8) { wchar_t wc = next_wchar(&ptr, NULL); if (wc == WEOF) break; width += fb_wcwidth(wc); --ptr; } else { ++width; } } } ++ptr; } return width; }
/** * Find newline in [begin, end), truncate at TRUNCATE_WIDTH. * @param begin The head pointer. * @param end The off-the-end pointer. * @param utf8 是否以UTF-8编码 * @return Off-the-end pointer to the first (truncated) line. */ static const char *get_truncated_line(const char *begin, const char *end, bool utf8) { const char *code = "[0123456789;"; bool ansi = false; int width = TRUNCATE_WIDTH; if (end - begin >= 2 && *begin == ':' && *(begin + 1) == ' ') width += 2; for (const char *s = begin; s < end; ++s) { if (*s == '\n') return s + 1; if (*s == '\033') { ansi = true; continue; } if (ansi) { if (!memchr(code, *s, sizeof(code) - 1)) ansi = false; continue; } if (*s & 0x80) { if (utf8) { const char *ptr = s; size_t size = end - s; wchar_t wc = next_wchar(&ptr, &size); if (wc == WEOF || wc == 0) { ++s; } else { width -= fb_wcwidth(wc); if (width < 0) return s; s = ptr - 1; } } else { width -= 2; if (width < 0) return s; ++s; } if (width == 0) return (s + 1 > end ? end : s + 1); } else { if (--width == 0) return s + 1; } } return end; }
static wchar_t get_next_wchar(editor_t *editor) { const char *buffer = NULL; size_t bytes = 0; char utf8_buffer[8] = { '\0' }; int ch = terminal_getchar(); editor->input_buffer[(int) editor->pending_bytes++] = ch; if (terminal_is_utf8()) { buffer = editor->input_buffer; bytes = editor->pending_bytes; } else { if (editor->pending_bytes > 1) { convert(CONVERT_G2U, editor->input_buffer, editor->pending_bytes, utf8_buffer, sizeof(utf8_buffer), NULL, NULL); editor->pending_bytes = 0; if (utf8_buffer[0] != '\0') { buffer = utf8_buffer; bytes = strlen(utf8_buffer); } } else if (!(ch & 0x80)) { editor->pending_bytes = 0; return ch; } } if (buffer) { wchar_t wc = next_wchar(&buffer, &bytes); if ((wc && wc != WEOF) || editor->pending_bytes == sizeof(editor->input_buffer)) { editor->pending_bytes = 0; return wc; } } return 0; }