diff options
Diffstat (limited to 'src')
391 files changed, 29091 insertions, 21869 deletions
diff --git a/src/clint.py b/src/clint.py index 1f588322f3..062901b43a 100755 --- a/src/clint.py +++ b/src/clint.py @@ -152,8 +152,10 @@ _ERROR_CATEGORIES = [ 'build/endif_comment', 'build/header_guard', 'build/include_defs', + 'build/defs_header', 'build/printf_format', 'build/storage_class', + 'build/init_macro', 'readability/bool', 'readability/multiline_comment', 'readability/multiline_string', @@ -749,53 +751,6 @@ BRACES = { } -CLOSING_BRACES = {v: k for k, v in BRACES.items()} - - -def GetExprBracesPosition(clean_lines, linenum, pos): - """List positions of all kinds of braces - - If input points to ( or { or [ then function proceeds until finding the - position which closes it. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Current line number. - pos: A position on the line. - - Yields: - A tuple (linenum, pos, brace, depth) that points to each brace. - Additionally each new line (linenum, pos, 's', depth) is yielded, for each - line end (linenum, pos, 'e', depth) is yielded and at the very end it - yields (linenum, pos, None, None). - """ - depth = 0 - yielded_line_start = True - startpos = pos - while linenum < clean_lines.NumLines() - 1: - line = clean_lines.elided_with_space_strings[linenum] - if not line.startswith('#') or yielded_line_start: - # Ignore #ifdefs, but not if it is macros that are checked - for i, brace in enumerate(line[startpos:]): - pos = i + startpos - if brace != ' ' and not yielded_line_start: - yield (linenum, pos, 's', depth) - yielded_line_start = True - if brace in BRACES: - depth += 1 - yield (linenum, pos, brace, depth) - elif brace in CLOSING_BRACES: - yield (linenum, pos, brace, depth) - depth -= 1 - if depth == 0: - yield (linenum, pos, None, None) - return - yield (linenum, len(line) - 1, 'e', depth) - yielded_line_start = False - startpos = 0 - linenum += 1 - - def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): """Find the position just after the matching endchar. @@ -880,112 +835,51 @@ def CheckForHeaderGuard(filename, lines, error): error(filename, 0, 'build/header_guard', 5, 'No "#pragma once" found in header') + def CheckIncludes(filename, lines, error): - """Checks that headers only include _defs headers + """Checks that headers only include _defs headers. Args: filename: The name of the C++ header file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ - if filename.endswith('.c.h') or filename.endswith('.in.h') or FileInfo(filename).RelativePath() in { + if (filename.endswith('.c.h') + or filename.endswith('.in.h') + or FileInfo(filename).RelativePath() in { 'func_attr.h', 'os/pty_process.h', - }: + }): return - # These should be synced with the ignored headers in the `iwyu` target in - # the Makefile. check_includes_ignore = [ - "src/nvim/api/extmark.h", - "src/nvim/api/private/dispatch.h", - "src/nvim/api/private/helpers.h", "src/nvim/api/private/validate.h", - "src/nvim/api/ui.h", - "src/nvim/ascii_defs.h", "src/nvim/assert_defs.h", - "src/nvim/autocmd.h", - "src/nvim/autocmd_defs.h", - "src/nvim/buffer.h", - "src/nvim/buffer_defs.h", "src/nvim/channel.h", "src/nvim/charset.h", - "src/nvim/cmdexpand.h", - "src/nvim/cmdhist.h", - "src/nvim/decoration.h", - "src/nvim/diff.h", - "src/nvim/drawline.h", - "src/nvim/drawscreen.h", - "src/nvim/eval.h", - "src/nvim/eval/encode.h", "src/nvim/eval/typval.h", - "src/nvim/eval/typval_defs.h", - "src/nvim/eval/userfunc.h", - "src/nvim/eval/window.h", - "src/nvim/event/libuv_process.h", - "src/nvim/event/loop.h", "src/nvim/event/multiqueue.h", - "src/nvim/event/process.h", - "src/nvim/event/rstream.h", - "src/nvim/event/signal.h", - "src/nvim/event/socket.h", - "src/nvim/event/stream.h", - "src/nvim/event/time.h", - "src/nvim/event/wstream.h", - "src/nvim/ex_cmds.h", - "src/nvim/ex_cmds_defs.h", - "src/nvim/ex_docmd.h", - "src/nvim/extmark.h", - "src/nvim/file_search.h", - "src/nvim/fileio.h", - "src/nvim/fold.h", "src/nvim/garray.h", - "src/nvim/getchar.h", "src/nvim/globals.h", - "src/nvim/grid.h", "src/nvim/highlight.h", - "src/nvim/highlight_group.h", - "src/nvim/input.h", - "src/nvim/insexpand.h", - "src/nvim/keycodes.h", - "src/nvim/log.h", "src/nvim/lua/executor.h", "src/nvim/main.h", "src/nvim/mark.h", - "src/nvim/mouse.h", - "src/nvim/move.h", - "src/nvim/msgpack_rpc/channel.h", "src/nvim/msgpack_rpc/channel_defs.h", - "src/nvim/msgpack_rpc/helpers.h", "src/nvim/msgpack_rpc/unpacker.h", "src/nvim/option.h", - "src/nvim/os/fileio.h", - "src/nvim/os/input.h", "src/nvim/os/pty_conpty_win.h", - "src/nvim/os/pty_process_unix.h", "src/nvim/os/pty_process_win.h", - "src/nvim/path.h", - "src/nvim/plines.h", - "src/nvim/popupmenu.h", - "src/nvim/search.h", - "src/nvim/spell.h", - "src/nvim/syntax.h", - "src/nvim/textobject.h", - "src/nvim/tui/input.h", - "src/nvim/tui/tui.h", - "src/nvim/ui.h", - "src/nvim/ui_client.h", - "src/nvim/ui_compositor.h", - "src/nvim/viml/parser/expressions.h", - "src/nvim/viml/parser/parser.h", - "src/nvim/window.h", ] skip_headers = [ - "klib/kvec.h", - "klib/klist.h", "auto/config.h", - "nvim/func_attr.h" + "klib/klist.h", + "klib/kvec.h", + "mpack/mpack_core.h", + "mpack/object.h", + "nvim/func_attr.h", + "termkey/termkey.h", ] for i in check_includes_ignore: @@ -999,12 +893,29 @@ def CheckIncludes(filename, lines, error): if name in skip_headers: continue if (not name.endswith('.h.generated.h') and + not name.endswith('/defs.h') and not name.endswith('_defs.h') and - not name.endswith('/defs.h')): + not name.endswith('_defs.generated.h') and + not name.endswith('_enum.generated.h')): error(filename, i, 'build/include_defs', 5, 'Headers should not include non-"_defs" headers') +def CheckNonSymbols(filename, lines, error): + """Checks that a _defs.h header only contains non-symbols. + + Args: + filename: The name of the C++ header file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for i, line in enumerate(lines): + # Only a check against extern variables for now. + if line.startswith('EXTERN ') or line.startswith('extern '): + error(filename, i, 'build/defs_header', 5, + '"_defs" headers should not contain extern variables') + + def CheckForBadCharacters(filename, lines, error): """Logs an error for each line containing bad characters. @@ -1703,8 +1614,7 @@ def CheckSpacing(filename, clean_lines, linenum, error): line[commentpos - 1] not in string.whitespace) or (commentpos >= 2 and line[commentpos - 2] not in string.whitespace))): - error(filename, linenum, 'whitespace/comments', 2, - 'At least two spaces is best between code and comments') + return # There should always be a space between the // and the comment commentend = commentpos + 2 if commentend < len(line) and not line[commentend] == ' ': @@ -1815,8 +1725,7 @@ def CheckSpacing(filename, clean_lines, linenum, error): # There shouldn't be space around unary operators match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) if match: - error(filename, linenum, 'whitespace/operators', 4, - 'Extra space for operator %s' % match.group(1)) + return # For if/for/while/switch, the left and right parens should be # consistent about how many spaces are inside the parens, and @@ -2098,7 +2007,7 @@ def CheckLanguage(filename, clean_lines, linenum, error): if match: error(filename, linenum, 'runtime/deprecated', 4, 'Accessing list_T internals directly is prohibited; ' - 'see https://github.com/neovim/neovim/wiki/List-management-in-Neovim') + 'see https://neovim.io/doc/user/dev_vimpatch.html#dev-vimpatch-list-management') # Check for suspicious usage of "if" like # } if (a == b) { @@ -2179,6 +2088,11 @@ def CheckLanguage(filename, clean_lines, linenum, error): " named ('k' followed by CamelCase) compile-time constant for" " the size.") + # INIT() macro should only be used in header files. + if not filename.endswith('.h') and Search(r' INIT\(', line): + error(filename, linenum, 'build/init_macro', 4, + 'INIT() macro should only be used in header files.') + # Detect TRUE and FALSE. match = Search(r'\b(TRUE|FALSE)\b', line) if match: @@ -2292,6 +2206,8 @@ def ProcessFileData(filename, file_extension, lines, error, if file_extension == 'h': CheckForHeaderGuard(filename, lines, error) CheckIncludes(filename, lines, error) + if filename.endswith('/defs.h') or filename.endswith('_defs.h'): + CheckNonSymbols(filename, lines, error) RemoveMultiLineComments(filename, lines, error) clean_lines = CleansedLines(lines, init_lines) diff --git a/src/klib/kbtree.h b/src/klib/kbtree.h deleted file mode 100644 index 99f79952d7..0000000000 --- a/src/klib/kbtree.h +++ /dev/null @@ -1,477 +0,0 @@ -/*- - * Copyright 1997-1999, 2001, John-Mark Gurney. - * 2008-2009, Attractive Chaos <attractor@live.co.uk> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -// Gotchas -// ------- -// -// if you delete from a kbtree while iterating over it you must use -// kb_del_itr and not kb_del otherwise the iterator might point to freed memory. - -#ifndef NVIM_LIB_KBTREE_H -#define NVIM_LIB_KBTREE_H - -#include <assert.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#include "nvim/memory.h" - -#define KB_MAX_DEPTH 64 - -#define __KB_KEY(type, x) (x->key) -#define __KB_PTR(btr, x) (x->ptr) - -#define __KB_TREE_T(name, key_t, T) \ - typedef struct kbnode_##name##_s kbnode_##name##_t; \ - struct kbnode_##name##_s { \ - int32_t n; \ - bool is_internal; \ - key_t key[2*T - 1]; \ - kbnode_##name##_t *ptr[]; \ - }; \ - typedef struct { \ - kbnode_##name##_t *root; \ - int n_keys, n_nodes; \ - } kbtree_##name##_t; \ - typedef struct { \ - kbnode_##name##_t *x; \ - int i; \ - } kbpos_##name##_t; \ - typedef struct { \ - kbpos_##name##_t stack[KB_MAX_DEPTH], *p; \ - } kbitr_##name##_t; \ - - -#define __kb_destroy(kbnode_t, b) do { \ - int i; \ - unsigned int max = 8; \ - kbnode_t *x, **top, **stack = 0; \ - if (b->root) { \ - top = stack = (kbnode_t **)xcalloc(max, sizeof(kbnode_t *)); \ - *top++ = (b)->root; \ - while (top != stack) { \ - x = *--top; \ - if (x->is_internal == 0) { XFREE_CLEAR(x); continue; } \ - for (i = 0; i <= x->n; ++i) \ - if (__KB_PTR(b, x)[i]) { \ - if (top - stack == (int)max) { \ - max <<= 1; \ - stack = (kbnode_t **)xrealloc(stack, max * sizeof(kbnode_t *)); \ - top = stack + (max>>1); \ - } \ - *top++ = __KB_PTR(b, x)[i]; \ - } \ - XFREE_CLEAR(x); \ - } \ - } \ - XFREE_CLEAR(stack); \ -} while (0) - -#define __KB_GET_AUX1(name, key_t, kbnode_t, __cmp) \ - static inline int __kb_getp_aux_##name(const kbnode_t * __restrict x, key_t * __restrict k, \ - int *r) \ - { \ - int tr, *rr, begin = 0, end = x->n; \ - if (x->n == 0) return -1; \ - rr = r? r : &tr; \ - while (begin < end) { \ - int mid = (begin + end) >> 1; \ - if (__cmp(__KB_KEY(key_t, x)[mid], *k) < 0) begin = mid + 1; \ - else end = mid; \ - } \ - if (begin == x->n) { *rr = 1; return x->n - 1; } \ - if ((*rr = __cmp(*k, __KB_KEY(key_t, x)[begin])) < 0) --begin; \ - return begin; \ - } - -#define __KB_GET(name, key_t, kbnode_t) \ - static key_t *kb_getp_##name(kbtree_##name##_t *b, key_t * __restrict k) \ - { \ - if (!b->root) { \ - return 0; \ - } \ - int i, r = 0; \ - kbnode_t *x = b->root; \ - while (x) { \ - i = __kb_getp_aux_##name(x, k, &r); \ - if (i >= 0 && r == 0) return &__KB_KEY(key_t, x)[i]; \ - if (x->is_internal == 0) return 0; \ - x = __KB_PTR(b, x)[i + 1]; \ - } \ - return 0; \ - } \ - static inline key_t *kb_get_##name(kbtree_##name##_t *b, key_t k) \ - { \ - return kb_getp_##name(b, &k); \ - } - -#define __KB_INTERVAL(name, key_t, kbnode_t) \ - static inline void kb_intervalp_##name(kbtree_##name##_t *b, key_t * __restrict k, key_t **lower, \ - key_t **upper) \ - { \ - if (!b->root) { \ - return; \ - } \ - int i, r = 0; \ - kbnode_t *x = b->root; \ - *lower = *upper = 0; \ - while (x) { \ - i = __kb_getp_aux_##name(x, k, &r); \ - if (i >= 0 && r == 0) { \ - *lower = *upper = &__KB_KEY(key_t, x)[i]; \ - return; \ - } \ - if (i >= 0) *lower = &__KB_KEY(key_t, x)[i]; \ - if (i < x->n - 1) *upper = &__KB_KEY(key_t, x)[i + 1]; \ - if (x->is_internal == 0) return; \ - x = __KB_PTR(b, x)[i + 1]; \ - } \ - } \ - static inline void kb_interval_##name(kbtree_##name##_t *b, key_t k, key_t **lower, key_t **upper) \ - { \ - kb_intervalp_##name(b, &k, lower, upper); \ - } - -#define __KB_PUT(name, key_t, kbnode_t, __cmp, T, ILEN) \ - /* x must be an internal node */ \ - static inline void __kb_split_##name(kbtree_##name##_t *b, kbnode_t *x, int i, kbnode_t *y) \ - { \ - kbnode_t *z; \ - z = (kbnode_t *)xcalloc(1, y->is_internal? ILEN : sizeof(kbnode_##name##_t)); \ - ++b->n_nodes; \ - z->is_internal = y->is_internal; \ - z->n = T - 1; \ - memcpy(__KB_KEY(key_t, z), &__KB_KEY(key_t, y)[T], sizeof(key_t) * (T - 1)); \ - if (y->is_internal) memcpy(__KB_PTR(b, z), &__KB_PTR(b, y)[T], sizeof(void *) * T); \ - y->n = T - 1; \ - memmove(&__KB_PTR(b, x)[i + 2], &__KB_PTR(b, \ - x)[i + 1], sizeof(void *) * (unsigned int)(x->n - i)); \ - __KB_PTR(b, x)[i + 1] = z; \ - memmove(&__KB_KEY(key_t, x)[i + 1], &__KB_KEY(key_t, x)[i], \ - sizeof(key_t) * (unsigned int)(x->n - i)); \ - __KB_KEY(key_t, x)[i] = __KB_KEY(key_t, y)[T - 1]; \ - ++x->n; \ - } \ - static inline key_t *__kb_putp_aux_##name(kbtree_##name##_t *b, kbnode_t *x, key_t * __restrict k) \ - { \ - int i = x->n - 1; \ - key_t *ret; \ - if (x->is_internal == 0) { \ - i = __kb_getp_aux_##name(x, k, 0); \ - if (i != x->n - 1) \ - memmove(&__KB_KEY(key_t, x)[i + 2], &__KB_KEY(key_t, \ - x)[i + 1], \ - (unsigned int)(x->n - i - 1) * sizeof(key_t)); \ - ret = &__KB_KEY(key_t, x)[i + 1]; \ - *ret = *k; \ - ++x->n; \ - } else { \ - i = __kb_getp_aux_##name(x, k, 0) + 1; \ - if (__KB_PTR(b, x)[i]->n == 2 * T - 1) { \ - __kb_split_##name(b, x, i, __KB_PTR(b, x)[i]); \ - if (__cmp(*k, __KB_KEY(key_t, x)[i]) > 0) ++i; \ - } \ - ret = __kb_putp_aux_##name(b, __KB_PTR(b, x)[i], k); \ - } \ - return ret; \ - } \ - static inline key_t *kb_putp_##name(kbtree_##name##_t *b, key_t * __restrict k) \ - { \ - if (!b->root) { \ - b->root = (kbnode_t *)xcalloc(1, ILEN); \ - ++b->n_nodes; \ - } \ - kbnode_t *r, *s; \ - ++b->n_keys; \ - r = b->root; \ - if (r->n == 2 * T - 1) { \ - ++b->n_nodes; \ - s = (kbnode_t *)xcalloc(1, ILEN); \ - b->root = s; s->is_internal = 1; s->n = 0; \ - __KB_PTR(b, s)[0] = r; \ - __kb_split_##name(b, s, 0, r); \ - r = s; \ - } \ - return __kb_putp_aux_##name(b, r, k); \ - } \ - static inline void kb_put_##name(kbtree_##name##_t *b, key_t k) \ - { \ - kb_putp_##name(b, &k); \ - } - -#define __KB_DEL(name, key_t, kbnode_t, T) \ - static inline key_t __kb_delp_aux_##name(kbtree_##name##_t *b, kbnode_t *x, key_t * __restrict k, \ - int s) \ - { \ - int yn, zn, i, r = 0; \ - kbnode_t *xp, *y, *z; \ - key_t kp; \ - if (x == 0) return *k; \ - if (s) { /* s can only be 0, 1 or 2 */ \ - r = x->is_internal == 0? 0 : s == 1? 1 : -1; \ - i = s == 1? x->n - 1 : -1; \ - } else i = __kb_getp_aux_##name(x, k, &r); \ - if (x->is_internal == 0) { \ - if (s == 2) ++i; \ - kp = __KB_KEY(key_t, x)[i]; \ - memmove(&__KB_KEY(key_t, x)[i], &__KB_KEY(key_t, \ - x)[i + 1], \ - (unsigned int)(x->n - i - 1) * sizeof(key_t)); \ - --x->n; \ - return kp; \ - } \ - if (r == 0) { \ - if ((yn = __KB_PTR(b, x)[i]->n) >= T) { \ - xp = __KB_PTR(b, x)[i]; \ - kp = __KB_KEY(key_t, x)[i]; \ - __KB_KEY(key_t, x)[i] = __kb_delp_aux_##name(b, xp, 0, 1); \ - return kp; \ - } else if ((zn = __KB_PTR(b, x)[i + 1]->n) >= T) { \ - xp = __KB_PTR(b, x)[i + 1]; \ - kp = __KB_KEY(key_t, x)[i]; \ - __KB_KEY(key_t, x)[i] = __kb_delp_aux_##name(b, xp, 0, 2); \ - return kp; \ - } else if (yn == T - 1 && zn == T - 1) { \ - y = __KB_PTR(b, x)[i]; z = __KB_PTR(b, x)[i + 1]; \ - __KB_KEY(key_t, y)[y->n++] = *k; \ - memmove(&__KB_KEY(key_t, y)[y->n], __KB_KEY(key_t, z), (unsigned int)z->n * sizeof(key_t)); \ - if (y->is_internal) memmove(&__KB_PTR(b, y)[y->n], __KB_PTR(b, \ - z), \ - (unsigned int)(z->n + 1) * sizeof(void *)); \ - y->n += z->n; \ - memmove(&__KB_KEY(key_t, x)[i], &__KB_KEY(key_t, \ - x)[i + 1], \ - (unsigned int)(x->n - i - 1) * sizeof(key_t)); \ - memmove(&__KB_PTR(b, x)[i + 1], &__KB_PTR(b, \ - x)[i + 2], \ - (unsigned int)(x->n - i - 1) * sizeof(void *)); \ - --x->n; \ - XFREE_CLEAR(z); \ - return __kb_delp_aux_##name(b, y, k, s); \ - } \ - } \ - ++i; \ - if ((xp = __KB_PTR(b, x)[i])->n == T - 1) { \ - if (i > 0 && (y = __KB_PTR(b, x)[i - 1])->n >= T) { \ - memmove(&__KB_KEY(key_t, xp)[1], __KB_KEY(key_t, xp), (unsigned int)xp->n * sizeof(key_t)); \ - if (xp->is_internal) memmove(&__KB_PTR(b, xp)[1], __KB_PTR(b, \ - xp), \ - (unsigned int)(xp->n + 1) * sizeof(void *)); \ - __KB_KEY(key_t, xp)[0] = __KB_KEY(key_t, x)[i - 1]; \ - __KB_KEY(key_t, x)[i - 1] = __KB_KEY(key_t, y)[y->n - 1]; \ - if (xp->is_internal) __KB_PTR(b, xp)[0] = __KB_PTR(b, y)[y->n]; \ - --y->n; ++xp->n; \ - } else if (i < x->n && (y = __KB_PTR(b, x)[i + 1])->n >= T) { \ - __KB_KEY(key_t, xp)[xp->n++] = __KB_KEY(key_t, x)[i]; \ - __KB_KEY(key_t, x)[i] = __KB_KEY(key_t, y)[0]; \ - if (xp->is_internal) __KB_PTR(b, xp)[xp->n] = __KB_PTR(b, y)[0]; \ - --y->n; \ - memmove(__KB_KEY(key_t, y), &__KB_KEY(key_t, y)[1], (unsigned int)y->n * sizeof(key_t)); \ - if (y->is_internal) memmove(__KB_PTR(b, y), &__KB_PTR(b, \ - y)[1], \ - (unsigned int)(y->n + 1) * sizeof(void *)); \ - } else if (i > 0 && (y = __KB_PTR(b, x)[i - 1])->n == T - 1) { \ - __KB_KEY(key_t, y)[y->n++] = __KB_KEY(key_t, x)[i - 1]; \ - memmove(&__KB_KEY(key_t, y)[y->n], __KB_KEY(key_t, xp), \ - (unsigned int)xp->n * sizeof(key_t)); \ - if (y->is_internal) memmove(&__KB_PTR(b, y)[y->n], __KB_PTR(b, \ - xp), \ - (unsigned int)(xp->n + 1) * sizeof(void *)); \ - y->n += xp->n; \ - memmove(&__KB_KEY(key_t, x)[i - 1], &__KB_KEY(key_t, \ - x)[i], \ - (unsigned int)(x->n - i) * sizeof(key_t)); \ - memmove(&__KB_PTR(b, x)[i], &__KB_PTR(b, \ - x)[i + 1], (unsigned int)(x->n - i) * sizeof(void *)); \ - --x->n; \ - XFREE_CLEAR(xp); \ - xp = y; \ - } else if (i < x->n && (y = __KB_PTR(b, x)[i + 1])->n == T - 1) { \ - __KB_KEY(key_t, xp)[xp->n++] = __KB_KEY(key_t, x)[i]; \ - memmove(&__KB_KEY(key_t, xp)[xp->n], __KB_KEY(key_t, y), \ - (unsigned int)y->n * sizeof(key_t)); \ - if (xp->is_internal) memmove(&__KB_PTR(b, xp)[xp->n], __KB_PTR(b, y), \ - (unsigned int)(y->n + 1) * sizeof(void *)); \ - xp->n += y->n; \ - memmove(&__KB_KEY(key_t, x)[i], &__KB_KEY(key_t, \ - x)[i + 1], \ - (unsigned int)(x->n - i - 1) * sizeof(key_t)); \ - memmove(&__KB_PTR(b, x)[i + 1], &__KB_PTR(b, \ - x)[i + 2], \ - (unsigned int)(x->n - i - 1) * sizeof(void *)); \ - --x->n; \ - XFREE_CLEAR(y); \ - } \ - } \ - return __kb_delp_aux_##name(b, xp, k, s); \ - } \ - static inline key_t kb_delp_##name(kbtree_##name##_t *b, key_t * __restrict k) \ - { \ - kbnode_t *x; \ - key_t ret; \ - ret = __kb_delp_aux_##name(b, b->root, k, 0); \ - --b->n_keys; \ - if (b->root->n == 0 && b->root->is_internal) { \ - --b->n_nodes; \ - x = b->root; \ - b->root = __KB_PTR(b, x)[0]; \ - XFREE_CLEAR(x); \ - } \ - return ret; \ - } \ - static inline key_t kb_del_##name(kbtree_##name##_t *b, key_t k) \ - { \ - return kb_delp_##name(b, &k); \ - } - -#define __KB_ITR(name, key_t, kbnode_t) \ - static inline void kb_itr_first_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \ - { \ - itr->p = NULL; \ - if (b->n_keys == 0) return; \ - itr->p = itr->stack; \ - itr->p->x = b->root; itr->p->i = 0; \ - while (itr->p->x->is_internal && __KB_PTR(b, itr->p->x)[0] != 0) { \ - kbnode_t *x = itr->p->x; \ - ++itr->p; \ - itr->p->x = __KB_PTR(b, x)[0]; itr->p->i = 0; \ - } \ - } \ - static inline int kb_itr_next_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \ - { \ - if (itr->p == NULL) return 0; \ - for (;;) { \ - ++itr->p->i; \ - assert(itr->p->i <= 21); \ - while (itr->p->x && itr->p->i <= itr->p->x->n) { \ - itr->p[1].i = 0; \ - itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[itr->p->i] : 0; \ - ++itr->p; \ - } \ - if (itr->p == itr->stack) { \ - itr->p = NULL; \ - return 0; \ - } \ - --itr->p; \ - if (itr->p->x && itr->p->i < itr->p->x->n) return 1; \ - } \ - } \ - static inline int kb_itr_prev_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \ - { \ - if (itr->p == NULL) return 0; \ - for (;;) { \ - while (itr->p->x && itr->p->i >= 0) { \ - itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[itr->p->i] : 0; \ - itr->p[1].i = itr->p[1].x ? itr->p[1].x->n : -1; \ - ++itr->p; \ - } \ - if (itr->p == itr->stack) { \ - itr->p = NULL; \ - return 0; \ - } \ - --itr->p; \ - --itr->p->i; \ - if (itr->p->x && itr->p->i >= 0) return 1; \ - } \ - } \ - static inline int kb_itr_getp_##name(kbtree_##name##_t *b, key_t * __restrict k, \ - kbitr_##name##_t *itr) \ - { \ - if (b->n_keys == 0) { \ - itr->p = NULL; \ - return 0; \ - } \ - int i, r = 0; \ - itr->p = itr->stack; \ - itr->p->x = b->root; \ - while (itr->p->x) { \ - i = __kb_getp_aux_##name(itr->p->x, k, &r); \ - itr->p->i = i; \ - if (i >= 0 && r == 0) return 1; \ - ++itr->p->i; \ - assert(itr->p->i <= 21); \ - itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[i + 1] : 0; \ - ++itr->p; \ - } \ - itr->p->i = 0; \ - return 0; \ - } \ - static inline int kb_itr_get_##name(kbtree_##name##_t *b, key_t k, kbitr_##name##_t *itr) \ - { \ - return kb_itr_getp_##name(b, &k, itr); \ - } \ - static inline void kb_del_itr_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \ - { \ - key_t k = kb_itr_key(itr); \ - kb_delp_##name(b, &k); \ - kb_itr_getp_##name(b, &k, itr); \ - } - -#define KBTREE_INIT(name, key_t, __cmp, T) \ - KBTREE_INIT_IMPL(name, key_t, kbnode_##name##_t, __cmp, T, \ - (sizeof(kbnode_##name##_t) + (2*T)*sizeof(void *))) - -#define KBTREE_INIT_IMPL(name, key_t, kbnode_t, __cmp, T, ILEN) \ - __KB_TREE_T(name, key_t, T) \ - __KB_GET_AUX1(name, key_t, kbnode_t, __cmp) \ - __KB_GET(name, key_t, kbnode_t) \ - __KB_INTERVAL(name, key_t, kbnode_t) \ - __KB_PUT(name, key_t, kbnode_t, __cmp, T, ILEN) \ - __KB_DEL(name, key_t, kbnode_t, T) \ - __KB_ITR(name, key_t, kbnode_t) - -#define KB_DEFAULT_SIZE 512 - -#define kbtree_t(name) kbtree_##name##_t -#define kbitr_t(name) kbitr_##name##_t -#define kb_init(b) ((b)->n_keys = (b)->n_nodes = 0, (b)->root = 0) -#define kb_destroy(name, b) __kb_destroy(kbnode_##name##_t, b) -#define kb_get(name, b, k) kb_get_##name(b, k) -#define kb_put(name, b, k) kb_put_##name(b, k) -#define kb_del(name, b, k) kb_del_##name(b, k) -#define kb_interval(name, b, k, l, u) kb_interval_##name(b, k, l, u) -#define kb_getp(name, b, k) kb_getp_##name(b, k) -#define kb_putp(name, b, k) kb_putp_##name(b, k) -#define kb_delp(name, b, k) kb_delp_##name(b, k) -#define kb_intervalp(name, b, k, l, u) kb_intervalp_##name(b, k, l, u) - -#define kb_itr_first(name, b, i) kb_itr_first_##name(b, i) -#define kb_itr_get(name, b, k, i) kb_itr_get_##name(b, k, i) -#define kb_itr_getp(name, b, k, i) kb_itr_getp_##name(b, k, i) -#define kb_itr_next(name, b, i) kb_itr_next_##name(b, i) -#define kb_itr_prev(name, b, i) kb_itr_prev_##name(b, i) -#define kb_del_itr(name, b, i) kb_del_itr_##name(b, i) -#define kb_itr_key(itr) __KB_KEY(dummy, (itr)->p->x)[(itr)->p->i] -#define kb_itr_valid(itr) ((itr)->p >= (itr)->stack) - -#define kb_size(b) ((b)->n_keys) - -#define kb_generic_cmp(a, b) (((b) < (a)) - ((a) < (b))) -#define kb_str_cmp(a, b) strcmp(a, b) - -#endif // NVIM_LIB_KBTREE_H diff --git a/src/klib/kvec.h b/src/klib/kvec.h index 5677a93b1b..a32b35a14c 100644 --- a/src/klib/kvec.h +++ b/src/klib/kvec.h @@ -105,11 +105,12 @@ } while (0) #define kv_concat_len(v, data, len) \ - do { \ + if (len > 0) { \ kv_ensure_space(v, len); \ + assert((v).items); \ memcpy((v).items + (v).size, data, sizeof((v).items[0]) * len); \ (v).size = (v).size + len; \ - } while (0) + } #define kv_concat(v, str) kv_concat_len(v, str, strlen(str)) #define kv_splice(v1, v0) kv_concat_len(v1, (v0).items, (v0).size) @@ -160,10 +161,12 @@ (v).size = 0, \ (v).items = (v).init_array) +static inline void *_memcpy_free(void *restrict dest, void *restrict src, size_t size) + REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_ALWAYS_INLINE; + /// Move data to a new destination and free source static inline void *_memcpy_free(void *const restrict dest, void *const restrict src, const size_t size) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_ALWAYS_INLINE { memcpy(dest, src, size); XFREE_CLEAR(src); diff --git a/src/mpack/lmpack.c b/src/mpack/lmpack.c index ff21e29789..4ce4b5f3e5 100644 --- a/src/mpack/lmpack.c +++ b/src/mpack/lmpack.c @@ -882,7 +882,9 @@ static int lmpack_session_receive(lua_State *L) luaL_argcheck(L, (size_t)startpos <= len, 3, "start position must be less than or equal to the input string length"); - str += (size_t)startpos - 1; + size_t offset = (size_t)startpos - 1 ; + str += offset; + len -= offset; if (session->unpacker != LUA_REFNIL) { lmpack_geti(L, session->reg, session->unpacker); diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 3505f8be4f..047b22edcc 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -1,65 +1,57 @@ add_library(main_lib INTERFACE) -add_executable(nvim main.c) -set_target_properties(nvim +# Internally we need to make a distinction between "nvim without runtime files" +# (nvim_bin) and "nvim with runtime files" (nvim). +add_executable(nvim_bin EXCLUDE_FROM_ALL) + +set_target_properties(nvim_bin PROPERTIES EXPORT_COMPILE_COMMANDS ON - ENABLE_EXPORTS TRUE) + ENABLE_EXPORTS TRUE + OUTPUT_NAME nvim) #------------------------------------------------------------------------------- # Dependencies #------------------------------------------------------------------------------- -add_library(libuv INTERFACE) -find_package(libuv CONFIG QUIET) -if(TARGET libuv::uv_a) - target_link_libraries(libuv INTERFACE libuv::uv_a) - mark_as_advanced(libuv_DIR) -else() - # Fall back to find module for libuv versions older than v1.45.0 which don't - # provide a config file - find_package(Libuv 1.28.0 REQUIRED MODULE) - target_include_directories(libuv SYSTEM BEFORE INTERFACE ${LIBUV_INCLUDE_DIR}) - target_link_libraries(libuv INTERFACE ${LIBUV_LIBRARIES}) -endif() - add_library(nlua0 MODULE) if(WIN32) target_compile_definitions(nlua0 PUBLIC LUA_BUILD_AS_DLL LUA_LIB) set_target_properties(nlua0 PROPERTIES ENABLE_EXPORTS TRUE) elseif(APPLE) - set_target_properties(nlua0 PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") + target_link_options(nlua0 PRIVATE -undefined dynamic_lookup) endif() +# TODO(dundargoc): unittest stops working if I create an pseudo-imported +# library "luv" as with the other dependencies. Figure out why and fix. find_package(Luv 1.43.0 REQUIRED) target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LUV_INCLUDE_DIR}) target_link_libraries(main_lib INTERFACE ${LUV_LIBRARY}) find_package(Iconv REQUIRED) -find_package(Lpeg REQUIRED) -find_package(Libtermkey 0.22 REQUIRED) +find_package(Libuv 1.28.0 REQUIRED) find_package(Libvterm 0.3.3 REQUIRED) +find_package(Lpeg REQUIRED) find_package(Msgpack 1.0.0 REQUIRED) -find_package(Treesitter 0.20.8 REQUIRED) +find_package(Treesitter 0.20.9 REQUIRED) find_package(Unibilium 2.0 REQUIRED) target_link_libraries(main_lib INTERFACE iconv - libtermkey libvterm + lpeg msgpack treesitter - unibilium - lpeg) + unibilium) target_link_libraries(nlua0 PUBLIC lpeg) -# Libintl (not Intl) selects our FindLibintl.cmake script. #8464 -find_package(Libintl REQUIRED) -target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBINTL_INCLUDE_DIR}) -if (LIBINTL_LIBRARY) - target_link_libraries(main_lib INTERFACE ${LIBINTL_LIBRARY}) +if(ENABLE_LIBINTL) + find_package(Libintl REQUIRED) # Libintl (not Intl) selects our FindLibintl.cmake script. #8464 + target_link_libraries(main_lib INTERFACE libintl) endif() +target_compile_definitions(main_lib INTERFACE HAVE_UNIBILIUM) + # The unit test lib requires LuaJIT; it will be skipped if LuaJIT is missing. option(PREFER_LUA "Prefer Lua over LuaJIT in the nvim executable." OFF) if(PREFER_LUA) @@ -127,9 +119,11 @@ elseif(MINGW) target_compile_definitions(main_lib INTERFACE __USE_MINGW_ANSI_STDIO) # Enable wmain - target_link_libraries(nvim PRIVATE -municode) + target_link_libraries(nvim_bin PRIVATE -municode) elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") - target_compile_options(main_lib INTERFACE -fno-common + target_compile_options(main_lib INTERFACE + -Wno-conversion + -fno-common $<$<CONFIG:Release>:-Wno-unused-result> $<$<CONFIG:RelWithDebInfo>:-Wno-unused-result> $<$<CONFIG:MinSizeRel>:-Wno-unused-result>) @@ -142,7 +136,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") # workaround for clang-11 on macOS, supported on later versions if(NOT APPLE) - target_link_libraries(nvim PRIVATE -Wl,--no-undefined) + target_link_libraries(nvim_bin PRIVATE -Wl,--no-undefined) endif() endif() @@ -158,16 +152,16 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows") target_compile_definitions(main_lib INTERFACE _WIN32_WINNT=0x0602 MSWIN) target_link_libraries(main_lib INTERFACE netapi32) elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") - target_link_libraries(nvim PRIVATE "-framework CoreServices") + target_link_libraries(nvim_bin PRIVATE "-framework CoreServices") # Actually export symbols - symbols may not be visible even though # ENABLE_EXPORTS is set to true. See # https://github.com/neovim/neovim/issues/25295 - set_target_properties(nvim PROPERTIES LINK_FLAGS "-Wl,-export_dynamic") + target_link_options(nvim_bin PRIVATE "-Wl,-export_dynamic") elseif(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") target_link_libraries(main_lib INTERFACE pthread c++abi) elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS") - target_link_libraries(nvim PRIVATE -lsocket) + target_link_libraries(nvim_bin PRIVATE -lsocket) endif() check_c_compiler_flag(-Wimplicit-fallthrough HAVE_WIMPLICIT_FALLTHROUGH_FLAG) @@ -213,31 +207,35 @@ if(ENABLE_ASAN_UBSAN) if(NOT MSVC) if(CI_BUILD) # Try to recover from all sanitize issues so we get reports about all failures - target_compile_options(nvim PRIVATE -fsanitize-recover=all) + target_compile_options(nvim_bin PRIVATE -fsanitize-recover=all) else() - target_compile_options(nvim PRIVATE -fno-sanitize-recover=all) + target_compile_options(nvim_bin PRIVATE -fno-sanitize-recover=all) endif() - target_compile_options(nvim PRIVATE + target_compile_options(nvim_bin PRIVATE -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=undefined) endif() - target_compile_options(nvim PRIVATE -fsanitize=address) - target_link_libraries(nvim PRIVATE -fsanitize=address -fsanitize=undefined) - target_compile_definitions(nvim PRIVATE ENABLE_ASAN_UBSAN) -elseif(ENABLE_MSAN) + target_compile_options(nvim_bin PRIVATE -fsanitize=address) + target_link_libraries(nvim_bin PRIVATE -fsanitize=address -fsanitize=undefined) + target_compile_definitions(nvim_bin PRIVATE ENABLE_ASAN_UBSAN) +endif() + +if(ENABLE_MSAN) message(STATUS "Enabling memory sanitizer for nvim.") - target_compile_options(nvim PRIVATE + target_compile_options(nvim_bin PRIVATE -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -fno-optimize-sibling-calls) - target_link_libraries(nvim PRIVATE -fsanitize=memory -fsanitize-memory-track-origins) -elseif(ENABLE_TSAN) + target_link_libraries(nvim_bin PRIVATE -fsanitize=memory -fsanitize-memory-track-origins) +endif() + +if(ENABLE_TSAN) message(STATUS "Enabling thread sanitizer for nvim.") - target_compile_options(nvim PRIVATE -fsanitize=thread -fPIE) - target_link_libraries(nvim PRIVATE -fsanitize=thread) + target_compile_options(nvim_bin PRIVATE -fsanitize=thread -fPIE) + target_link_libraries(nvim_bin PRIVATE -fsanitize=thread) endif() option(CI_BUILD "CI, extra flags will be set" OFF) @@ -252,42 +250,23 @@ endif() option(ENABLE_IWYU "Run include-what-you-use with the compiler." OFF) if(ENABLE_IWYU) - find_program(IWYU_PRG NAMES include-what-you-use iwyu) - if(NOT IWYU_PRG) - message(FATAL_ERROR "ENABLE_IWYU is ON but include-what-you-use is not found!") - endif() - + find_program(IWYU_PRG NAMES include-what-you-use iwyu REQUIRED) set(iwyu_flags "${IWYU_PRG};") string(APPEND iwyu_flags "-Xiwyu;--no_default_mappings;") string(APPEND iwyu_flags "-Xiwyu;--no_fwd_decls;") string(APPEND iwyu_flags "-Xiwyu;--mapping_file=${PROJECT_SOURCE_DIR}/cmake.config/iwyu/mapping.imp") - set_target_properties(nvim PROPERTIES C_INCLUDE_WHAT_YOU_USE "${iwyu_flags}") + set_target_properties(nvim_bin PROPERTIES C_INCLUDE_WHAT_YOU_USE "${iwyu_flags}") target_compile_definitions(main_lib INTERFACE EXITFREE) endif() option(ENABLE_COMPILER_SUGGESTIONS "Enable -Wsuggest compiler warnings" OFF) if(ENABLE_COMPILER_SUGGESTIONS) - # Clang doesn't have -Wsuggest-attribute so check for each one. - check_c_compiler_flag(-Wsuggest-attribute=pure HAVE_WSUGGEST_ATTRIBUTE_PURE) - if(HAVE_WSUGGEST_ATTRIBUTE_PURE) - target_compile_options(main_lib INTERFACE -Wsuggest-attribute=pure) - endif() - - check_c_compiler_flag(-Wsuggest-attribute=const HAVE_WSUGGEST_ATTRIBUTE_CONST) - if(HAVE_WSUGGEST_ATTRIBUTE_CONST) - target_compile_options(main_lib INTERFACE -Wsuggest-attribute=const) - endif() - - check_c_compiler_flag(-Wsuggest-attribute=malloc HAVE_WSUGGEST_ATTRIBUTE_MALLOC) - if(HAVE_WSUGGEST_ATTRIBUTE_MALLOC) - target_compile_options(main_lib INTERFACE -Wsuggest-attribute=malloc) - endif() - - check_c_compiler_flag(-Wsuggest-attribute=cold HAVE_WSUGGEST_ATTRIBUTE_COLD) - if(HAVE_WSUGGEST_ATTRIBUTE_COLD) - target_compile_options(main_lib INTERFACE -Wsuggest-attribute=cold) - endif() + target_compile_options(main_lib INTERFACE + -Wsuggest-attribute=cold + -Wsuggest-attribute=const + -Wsuggest-attribute=malloc + -Wsuggest-attribute=pure) endif() option(ENABLE_GCOV "Enable gcov support" OFF) @@ -306,80 +285,88 @@ endif() # Variables #------------------------------------------------------------------------------- -set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators) +set(FUNCS_METADATA ${PROJECT_BINARY_DIR}/funcs_metadata.mpack) +set(UI_METADATA ${PROJECT_BINARY_DIR}/ui_metadata.mpack) +set(BINARY_LIB_DIR ${PROJECT_BINARY_DIR}/lib/nvim) set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) -set(BINARY_LIB_DIR ${PROJECT_BINARY_DIR}/lib/nvim/) +set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) +set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators) +set(GEN_EVAL_TOUCH ${TOUCHES_DIR}/gen_doc_eval) +set(LUAJIT_RUNTIME_DIR ${DEPS_PREFIX}/share/luajit-2.1/jit) +set(NVIM_RUNTIME_DIR ${PROJECT_SOURCE_DIR}/runtime) +set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/src/unicode) + +# GENERATOR_DIR set(API_DISPATCH_GENERATOR ${GENERATOR_DIR}/gen_api_dispatch.lua) set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua) -set(GENERATOR_PRELOAD ${GENERATOR_DIR}/preload.lua) +set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua) +set(EVENTS_GENERATOR ${GENERATOR_DIR}/gen_events.lua) +set(EX_CMDS_GENERATOR ${GENERATOR_DIR}/gen_ex_cmds.lua) +set(FUNCS_GENERATOR ${GENERATOR_DIR}/gen_eval.lua) set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua) set(GENERATOR_HASHY ${GENERATOR_DIR}/hashy.lua) -set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack) -set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) -set(LUA_API_C_BINDINGS ${GENERATED_DIR}/lua_api_c_bindings.generated.c) +set(GENERATOR_PRELOAD ${GENERATOR_DIR}/preload.lua) set(HEADER_GENERATOR ${GENERATOR_DIR}/gen_declarations.lua) -set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) +set(OPTIONS_ENUM_GENERATOR ${GENERATOR_DIR}/gen_options_enum.lua) +set(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua) +set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua) + +# GENERATED_DIR and GENERATED_INCLUDES_DIR set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h) -set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generated.h) -set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h) -set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) -set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h) -set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h) -set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) +set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) +set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) +set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h) +set(GENERATED_API_METADATA ${GENERATED_DIR}/api/private/api_metadata.generated.h) set(GENERATED_KEYSETS_DEFS ${GENERATED_DIR}/keysets_defs.generated.h) -set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) -set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h) -set(EX_CMDS_GENERATOR ${GENERATOR_DIR}/gen_ex_cmds.lua) -set(FUNCS_GENERATOR ${GENERATOR_DIR}/gen_eval.lua) -set(EVENTS_GENERATOR ${GENERATOR_DIR}/gen_events.lua) -set(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua) -set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua) -set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/src/unicode) +set(GENERATED_OPTIONS_ENUM ${GENERATED_DIR}/options_enum.generated.h) +set(GENERATED_OPTIONS_MAP ${GENERATED_DIR}/options_map.generated.h) +set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h) +set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h) +set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) +set(LUA_API_C_BINDINGS ${GENERATED_DIR}/lua_api_c_bindings.generated.c) set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h) -set(NVIM_RUNTIME_DIR ${PROJECT_SOURCE_DIR}/runtime) + +# NVIM_RUNTIME_DIR +set(LUA_DEFAULTS_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_defaults.lua) set(LUA_EDITOR_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_editor.lua) -set(LUA_SHARED_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/shared.lua) -set(LUA_LOADER_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/loader.lua) -set(LUA_INSPECT_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/inspect.lua) +set(LUA_FILETYPE_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/filetype.lua) set(LUA_FS_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/fs.lua) set(LUA_F_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/F.lua) -set(LUA_DEFAULTS_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_defaults.lua) -set(LUA_OPTIONS_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_options.lua) -set(LUA_FILETYPE_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/filetype.lua) set(LUA_INIT_PACKAGES_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_init_packages.lua) +set(LUA_INSPECT_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/inspect.lua) set(LUA_KEYMAP_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/keymap.lua) -set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua) -set(LUAJIT_RUNTIME_DIR ${DEPS_PREFIX}/share/luajit-2.1/jit) +set(LUA_LOADER_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/loader.lua) +set(LUA_OPTIONS_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_options.lua) +set(LUA_SHARED_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/shared.lua) -glob_wrapper(UNICODE_FILES ${UNICODE_DIR}/*.txt) -glob_wrapper(API_HEADERS api/*.h) +file(GLOB UNICODE_FILES CONFIGURE_DEPENDS ${UNICODE_DIR}/*.txt) +file(GLOB API_HEADERS CONFIGURE_DEPENDS api/*.h) list(REMOVE_ITEM API_HEADERS ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h) -glob_wrapper(MSGPACK_RPC_HEADERS msgpack_rpc/*.h) +file(GLOB MSGPACK_RPC_HEADERS CONFIGURE_DEPENDS msgpack_rpc/*.h) -target_include_directories(main_lib INTERFACE ${GENERATED_DIR}) -target_include_directories(main_lib INTERFACE ${CACHED_GENERATED_DIR}) -target_include_directories(main_lib INTERFACE ${GENERATED_INCLUDES_DIR}) -target_include_directories(main_lib INTERFACE "${PROJECT_BINARY_DIR}/cmake.config") -target_include_directories(main_lib INTERFACE "${PROJECT_SOURCE_DIR}/src") +target_include_directories(main_lib INTERFACE + ${GENERATED_DIR} + ${GENERATED_INCLUDES_DIR} + "${PROJECT_BINARY_DIR}/cmake.config" + "${PROJECT_SOURCE_DIR}/src") -target_include_directories(nlua0 PUBLIC "${PROJECT_SOURCE_DIR}/src") -target_include_directories(nlua0 PUBLIC "${PROJECT_BINARY_DIR}/cmake.config") -target_include_directories(nlua0 PUBLIC ${GENERATED_INCLUDES_DIR}) +target_include_directories(nlua0 PUBLIC + "${PROJECT_SOURCE_DIR}/src" + "${PROJECT_BINARY_DIR}/cmake.config" + ${GENERATED_INCLUDES_DIR}) -file(MAKE_DIRECTORY ${TOUCHES_DIR}) -file(MAKE_DIRECTORY ${GENERATED_DIR}) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) +file(MAKE_DIRECTORY ${TOUCHES_DIR} ${GENERATED_DIR} ${GENERATED_INCLUDES_DIR}) -glob_wrapper(NVIM_SOURCES *.c) -glob_wrapper(NVIM_HEADERS *.h) -glob_wrapper(EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c) -glob_wrapper(EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h) +file(GLOB NVIM_SOURCES CONFIGURE_DEPENDS *.c) +file(GLOB NVIM_HEADERS CONFIGURE_DEPENDS *.h) +file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c ../termkey/*.c) +file(GLOB EXTERNAL_HEADERS CONFIGURE_DEPENDS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h ../termkey/*.h) -glob_wrapper(NLUA0_SOURCES ../mpack/*.c) +file(GLOB NLUA0_SOURCES CONFIGURE_DEPENDS ../mpack/*.c) if(PREFER_LUA) # luajit not used, use a vendored copy of the bit module @@ -405,8 +392,8 @@ foreach(subdir file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) - glob_wrapper(sources ${subdir}/*.c) - glob_wrapper(headers ${subdir}/*.h) + file(GLOB sources CONFIGURE_DEPENDS ${subdir}/*.c) + file(GLOB headers CONFIGURE_DEPENDS ${subdir}/*.h) list(APPEND NVIM_SOURCES ${sources}) list(APPEND NVIM_HEADERS ${headers}) endforeach() @@ -436,13 +423,13 @@ endforeach() list(REMOVE_ITEM NVIM_SOURCES ${to_remove}) -# xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306 +# xdiff, mpack, lua-cjson, termkey: inlined external project, we don't maintain it. #9306 if(MSVC) set_source_files_properties( - ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -wd4090 -wd4244 -wd4267") + ${EXTERNAL_SOURCES} PROPERTIES COMPILE_OPTIONS "-wd4090;-wd4244;-wd4267") else() set_source_files_properties( - ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion -Wno-strict-prototypes -Wno-misleading-indentation") + ${EXTERNAL_SOURCES} PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-missing-noreturn;-Wno-missing-format-attribute;-Wno-double-promotion;-Wno-strict-prototypes;-Wno-misleading-indentation;-Wno-sign-compare;-Wno-implicit-fallthrough;-Wno-missing-prototypes;-Wno-missing-field-initializers") endif() # Log level (NVIM_LOG_DEBUG in log.h) @@ -451,7 +438,7 @@ if(CI_BUILD) # TODO(bfredl): debug log level also exposes some errors with EXITFREE in ASAN build. else() # Minimize logging for release-type builds. - target_compile_definitions(nvim PRIVATE $<$<CONFIG:Debug>:NVIM_LOG_DEBUG>) + target_compile_definitions(nvim_bin PRIVATE $<$<CONFIG:Debug>:NVIM_LOG_DEBUG>) endif() if(ENABLE_ASAN_UBSAN OR ENABLE_MSAN OR ENABLE_TSAN) @@ -463,7 +450,7 @@ endif() #------------------------------------------------------------------------------- get_target_property(prop main_lib INTERFACE_COMPILE_DEFINITIONS) -foreach(gen_cdef DO_NOT_DEFINE_EMPTY_ATTRIBUTES ${prop}) +foreach(gen_cdef ${prop}) if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS") list(APPEND gen_cflags "-D${gen_cdef}") endif() @@ -506,7 +493,7 @@ add_custom_command( "${NVIM_VERSION_DEF_H}" DEPENDS "${PROJECT_BINARY_DIR}/cmake.config/auto/versiondef-$<CONFIG>.h") -set(LUA_GEN ${LUA_GEN_PRG} ${GENERATOR_PRELOAD} ${PROJECT_SOURCE_DIR} $<TARGET_FILE:nlua0>) +set(LUA_GEN ${LUA_GEN_PRG} ${GENERATOR_PRELOAD} ${PROJECT_SOURCE_DIR} $<TARGET_FILE:nlua0> ${PROJECT_BINARY_DIR}) set(LUA_GEN_DEPS ${GENERATOR_PRELOAD} $<TARGET_FILE:nlua0>) # NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers @@ -566,14 +553,19 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES} ${UNICODE_FILES} ) +set(NVIM_VERSION_LUA ${PROJECT_BINARY_DIR}/nvim_version.lua) +configure_file(${GENERATOR_DIR}/nvim_version.lua.in ${NVIM_VERSION_LUA}) + add_custom_command( - OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} - ${API_METADATA} ${LUA_API_C_BINDINGS} ${GENERATED_KEYSETS_DEFS} + OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_API_METADATA} + ${FUNCS_METADATA} ${LUA_API_C_BINDINGS} ${GENERATED_KEYSETS_DEFS} COMMAND ${LUA_GEN} ${API_DISPATCH_GENERATOR} ${GENERATED_API_DISPATCH} - ${GENERATED_FUNCS_METADATA} ${API_METADATA} + ${GENERATED_API_METADATA} ${FUNCS_METADATA} ${LUA_API_C_BINDINGS} ${GENERATED_KEYSETS_DEFS} + ${UI_METADATA} + ${NVIM_VERSION_GIT_H} ${API_HEADERS} DEPENDS ${LUA_GEN_DEPS} @@ -581,6 +573,9 @@ add_custom_command( ${MSGPACK_RPC_HEADERS} ${API_DISPATCH_GENERATOR} ${GENERATOR_C_GRAMMAR} + ${UI_METADATA} + ${NVIM_VERSION_LUA} + ${NVIM_VERSION_GIT_H} ${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua ) @@ -625,13 +620,13 @@ list(APPEND NVIM_GENERATED_SOURCES add_custom_command( OUTPUT ${GENERATED_UI_EVENTS_CALL} ${GENERATED_UI_EVENTS_REMOTE} - ${GENERATED_UI_EVENTS_METADATA} + ${UI_METADATA} ${GENERATED_UI_EVENTS_CLIENT} COMMAND ${LUA_GEN} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h ${GENERATED_UI_EVENTS_CALL} ${GENERATED_UI_EVENTS_REMOTE} - ${GENERATED_UI_EVENTS_METADATA} + ${UI_METADATA} ${GENERATED_UI_EVENTS_CLIENT} DEPENDS ${LUA_GEN_DEPS} @@ -644,6 +639,7 @@ list(APPEND NVIM_GENERATED_FOR_HEADERS "${GENERATED_EX_CMDS_ENUM}" "${GENERATED_EVENTS_ENUM}" "${GENERATED_KEYSETS_DEFS}" + "${GENERATED_OPTIONS_ENUM}" ) list(APPEND NVIM_GENERATED_FOR_SOURCES @@ -651,6 +647,7 @@ list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_NAMES_MAP}" "${GENERATED_OPTIONS}" + "${GENERATED_OPTIONS_MAP}" "${GENERATED_UNICODE_TABLES}" "${VIM_MODULE_FILE}" ) @@ -665,8 +662,8 @@ add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} ) add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} - COMMAND ${LUA_GEN} ${FUNCS_GENERATOR} ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA} - DEPENDS ${LUA_GEN_DEPS} ${FUNCS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${API_METADATA} + COMMAND ${LUA_GEN} ${FUNCS_GENERATOR} ${GENERATED_DIR} ${FUNCS_METADATA} ${FUNCS_DATA} + DEPENDS ${LUA_GEN_DEPS} ${FUNCS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${FUNCS_METADATA} ) list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_FUNCS}") @@ -681,6 +678,11 @@ add_custom_command(OUTPUT ${GENERATED_OPTIONS} DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua ) +add_custom_command(OUTPUT ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} + COMMAND ${LUA_GEN} ${OPTIONS_ENUM_GENERATOR} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} + DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_ENUM_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua +) + # NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive. foreach(hfile ${NVIM_GENERATED_FOR_HEADERS}) list(FIND NVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx) @@ -692,9 +694,9 @@ endforeach() if(PREFER_LUA) message(STATUS "luajit not used, skipping unit tests") else() - glob_wrapper(UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) - target_sources(nvim PRIVATE ${UNIT_TEST_FIXTURES}) - target_compile_definitions(nvim PRIVATE UNIT_TESTING) + file(GLOB UNIT_TEST_FIXTURES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) + target_sources(nvim_bin PRIVATE ${UNIT_TEST_FIXTURES}) + target_compile_definitions(nvim_bin PRIVATE UNIT_TESTING) endif() target_sources(main_lib INTERFACE @@ -708,32 +710,33 @@ target_sources(main_lib INTERFACE target_sources(nlua0 PUBLIC ${NLUA0_SOURCES}) -target_link_libraries(nvim PRIVATE main_lib PUBLIC libuv) -install_helper(TARGETS nvim) +target_link_libraries(nvim_bin PRIVATE main_lib PUBLIC libuv) +install_helper(TARGETS nvim_bin) if(MSVC) - install(FILES $<TARGET_PDB_FILE:nvim> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) + install(FILES $<TARGET_PDB_FILE:nvim_bin> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) endif() if(ENABLE_LTO) include(CheckIPOSupported) check_ipo_supported(RESULT IPO_SUPPORTED) if(IPO_SUPPORTED) - set_target_properties(nvim PROPERTIES + set_target_properties(nvim_bin PROPERTIES INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE) endif() endif() +add_custom_target(nvim_runtime_deps) if(WIN32) # Copy DLLs and third-party tools to bin/ and install them along with nvim - add_custom_target(nvim_runtime_deps ALL - COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_BINARY_DIR}/windows_runtime_deps/ + add_custom_command(TARGET nvim_runtime_deps + COMMAND ${CMAKE_COMMAND} -E ${COPY_DIRECTORY} ${PROJECT_BINARY_DIR}/windows_runtime_deps/ ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) install(DIRECTORY ${PROJECT_BINARY_DIR}/windows_runtime_deps/ DESTINATION ${CMAKE_INSTALL_BINDIR}) - add_custom_target(nvim_dll_deps DEPENDS nvim + add_custom_target(nvim_dll_deps DEPENDS nvim_bin COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/windows_runtime_deps COMMAND ${CMAKE_COMMAND} -D CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} @@ -763,22 +766,17 @@ if(WIN32) add_custom_target(external_blobs COMMAND ${CMAKE_COMMAND} -P ${PROJECT_BINARY_DIR}/external_blobs.cmake) add_dependencies(nvim_runtime_deps external_blobs) -else() - add_custom_target(nvim_runtime_deps) # Stub target to avoid CMP0046. endif() file(MAKE_DIRECTORY ${BINARY_LIB_DIR}) # install treesitter parser if bundled if(EXISTS ${DEPS_PREFIX}/lib/nvim/parser) - glob_wrapper(TREESITTER_PARSERS ${DEPS_PREFIX}/lib/nvim/parser/*) - foreach(parser_lib ${TREESITTER_PARSERS}) - file(COPY ${parser_lib} DESTINATION ${BINARY_LIB_DIR}/parser) - endforeach() + add_custom_command(TARGET nvim_runtime_deps COMMAND ${CMAKE_COMMAND} -E ${COPY_DIRECTORY} ${DEPS_PREFIX}/lib/nvim/parser ${BINARY_LIB_DIR}/parser) endif() install(DIRECTORY ${BINARY_LIB_DIR} - DESTINATION ${CMAKE_INSTALL_LIBDIR}/nvim/ + DESTINATION ${CMAKE_INSTALL_LIBDIR} USE_SOURCE_PERMISSIONS) if(NOT PREFER_LUA) @@ -850,7 +848,7 @@ add_glob_target( add_custom_target(copy_compile_commands COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/compile_commands.json ${PROJECT_SOURCE_DIR}/compile_commands.json) -add_dependencies(copy_compile_commands nvim) +add_dependencies(copy_compile_commands nvim_bin) add_dependencies(lintc-clang-tidy copy_compile_commands) add_dependencies(clang-analyzer copy_compile_commands) @@ -879,12 +877,11 @@ add_glob_target( FLAGS -c ${UNCRUSTIFY_CONFIG} -q --check FILES ${LINT_NVIM_SOURCES}) -add_custom_target(formatc - COMMAND ${CMAKE_COMMAND} - -D FORMAT_PRG=${UNCRUSTIFY_PRG} - -D LANG=c - -P ${PROJECT_SOURCE_DIR}/cmake/Format.cmake - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) +add_glob_target( + TARGET formatc + COMMAND ${UNCRUSTIFY_PRG} + FLAGS -c ${UNCRUSTIFY_CONFIG} --replace --no-backup + FILES ${LINT_NVIM_SOURCES}) add_dependencies(lintc-uncrustify uncrustify_update_config) add_dependencies(formatc uncrustify_update_config) @@ -893,35 +890,31 @@ add_dependencies(uncrustify_update_config uncrustify) add_custom_target(lintc) add_dependencies(lintc lintc-clint lintc-uncrustify lintc-clang-tidy) +#------------------------------------------------------------------------------- +# Docs +#------------------------------------------------------------------------------- + +add_subdirectory(po) + add_custom_target(generated-sources DEPENDS - ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_SOURCES} ) -add_subdirectory(po) - -#------------------------------------------------------------------------------- -# Docs -#------------------------------------------------------------------------------- - set(VIMDOC_FILES - ${NVIM_RUNTIME_DIR}/doc/api.mpack ${NVIM_RUNTIME_DIR}/doc/api.txt - ${NVIM_RUNTIME_DIR}/doc/diagnostic.mpack ${NVIM_RUNTIME_DIR}/doc/diagnostic.txt - ${NVIM_RUNTIME_DIR}/doc/lsp.mpack ${NVIM_RUNTIME_DIR}/doc/lsp.txt - ${NVIM_RUNTIME_DIR}/doc/lua.mpack ${NVIM_RUNTIME_DIR}/doc/lua.txt - ${NVIM_RUNTIME_DIR}/doc/treesitter.mpack ${NVIM_RUNTIME_DIR}/doc/treesitter.txt ) -glob_wrapper(API_SOURCES ${PROJECT_SOURCE_DIR}/src/nvim/api/*.c) +file(GLOB API_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/nvim/api/*.c) -glob_wrapper(LUA_SOURCES +file(GLOB LUA_SOURCES CONFIGURE_DEPENDS ${NVIM_RUNTIME_DIR}/lua/vim/*.lua + ${NVIM_RUNTIME_DIR}/lua/vim/_meta/*.lua ${NVIM_RUNTIME_DIR}/lua/vim/filetype/*.lua ${NVIM_RUNTIME_DIR}/lua/vim/lsp/*.lua ${NVIM_RUNTIME_DIR}/lua/vim/treesitter/*.lua @@ -929,36 +922,44 @@ glob_wrapper(LUA_SOURCES add_custom_command( OUTPUT ${VIMDOC_FILES} - COMMAND ${PROJECT_SOURCE_DIR}/scripts/gen_vimdoc.py + COMMAND ${CMAKE_COMMAND} -E env "VIMRUNTIME=${NVIM_RUNTIME_DIR}" + $<TARGET_FILE:nvim_bin> -l scripts/gen_vimdoc.lua DEPENDS nvim ${API_SOURCES} ${LUA_SOURCES} + ${PROJECT_SOURCE_DIR}/scripts/gen_vimdoc.lua WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} ) -set(GEN_EVAL_FILES - ${NVIM_RUNTIME_DIR}/lua/vim/_meta/vimfn.lua - ${NVIM_RUNTIME_DIR}/lua/vim/_meta/api.lua - ${NVIM_RUNTIME_DIR}/lua/vim/_meta/api_keysets.lua - ${NVIM_RUNTIME_DIR}/doc/builtin.txt - ${NVIM_RUNTIME_DIR}/lua/vim/_meta/options.lua - ${NVIM_RUNTIME_DIR}/doc/options.txt -) - add_custom_command( - OUTPUT ${GEN_EVAL_FILES} - COMMAND $<TARGET_FILE:nvim> -l ${PROJECT_SOURCE_DIR}/scripts/gen_eval_files.lua + OUTPUT ${GEN_EVAL_TOUCH} + COMMAND ${CMAKE_COMMAND} -E touch ${GEN_EVAL_TOUCH} + COMMAND ${CMAKE_COMMAND} -E env "VIMRUNTIME=${NVIM_RUNTIME_DIR}" + $<TARGET_FILE:nvim_bin> -l ${PROJECT_SOURCE_DIR}/scripts/gen_eval_files.lua DEPENDS - ${API_METADATA} + nvim + ${FUNCS_METADATA} ${PROJECT_SOURCE_DIR}/scripts/gen_eval_files.lua ${PROJECT_SOURCE_DIR}/src/nvim/eval.lua ${PROJECT_SOURCE_DIR}/src/nvim/options.lua - ${NVIM_RUNTIME_DIR}/doc/api.mpack + ${PROJECT_SOURCE_DIR}/src/nvim/vvars.lua WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} ) -add_custom_target(doc-eval DEPENDS ${GEN_EVAL_FILES}) +add_custom_target(doc-eval DEPENDS ${GEN_EVAL_TOUCH}) add_custom_target(doc-vim DEPENDS ${VIMDOC_FILES}) add_custom_target(doc) add_dependencies(doc doc-vim doc-eval) + +set(lintdoc_touch ${TOUCHES_DIR}/lintdoc) +add_custom_command( + OUTPUT ${lintdoc_touch} + COMMAND ${CMAKE_COMMAND} -E touch ${lintdoc_touch} + COMMAND ${CMAKE_COMMAND} -E env "VIMRUNTIME=${NVIM_RUNTIME_DIR}" + $<TARGET_FILE:nvim_bin> --clean -l scripts/lintdoc.lua + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${DOCFILES} + USES_TERMINAL) +add_custom_target(lintdoc DEPENDS ${lintdoc_touch}) +add_dependencies(lintdoc nvim) diff --git a/src/nvim/README.md b/src/nvim/README.md index b484d59cb3..9ef39cc90f 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -255,6 +255,12 @@ region is repainted internally. To also highlight excess internal redraws, use - http://bazaar.launchpad.net/~libvterm/libvterm/trunk/view/head:/doc/seqs.txt - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html +Data structures +--------------- + +Buffer text is stored as a tree of line segments, defined in [memline.c](https://github.com/neovim/neovim/blob/v0.9.5/src/nvim/memline.c#L8-L35). +The central idea is found in [ml_find_line](https://github.com/neovim/neovim/blob/v0.9.5/src/nvim/memline.c#L2800). + Nvim lifecycle -------------- @@ -392,6 +398,68 @@ modes managed by the `state_enter` loop: - insert mode: `insert_{enter,check,execute}()`(`edit.c`) - terminal mode: `terminal_{enter,execute}()`(`terminal.c`) +## Important variables + +The current mode is stored in `State`. The values it can have are `MODE_NORMAL`, +`MODE_INSERT`, `MODE_CMDLINE`, and a few others. + +The current window is `curwin`. The current buffer is `curbuf`. These point +to structures with the cursor position in the window, option values, the file +name, etc. + +All the global variables are declared in `globals.h`. + +### The main loop + +The main loop is implemented in state_enter. The basic idea is that Vim waits +for the user to type a character and processes it until another character is +needed. Thus there are several places where Vim waits for a character to be +typed. The `vgetc()` function is used for this. It also handles mapping. + +Updating the screen is mostly postponed until a command or a sequence of +commands has finished. The work is done by `update_screen()`, which calls +`win_update()` for every window, which calls `win_line()` for every line. +See the start of [drawscreen.c](drawscreen.c) for more explanations. + +### Command-line mode + +When typing a `:`, `normal_cmd()` will call `getcmdline()` to obtain a line with +an Ex command. `getcmdline()` calls a loop that will handle each typed +character. It returns when hitting `<CR>` or `<Esc>` or some other character that +ends the command line mode. + +### Ex commands + +Ex commands are handled by the function `do_cmdline()`. It does the generic +parsing of the `:` command line and calls `do_one_cmd()` for each separate +command. It also takes care of while loops. + +`do_one_cmd()` parses the range and generic arguments and puts them in the +exarg_t and passes it to the function that handles the command. + +The `:` commands are listed in [ex_cmds.lua](ex_cmds.lua). + +### Normal mode commands + +The Normal mode commands are handled by the `normal_cmd()` function. It also +handles the optional count and an extra character for some commands. These +are passed in a `cmdarg_T` to the function that handles the command. + +There is a table `nv_cmds` in [normal.c](normal.c) which +lists the first character of every +command. The second entry of each item is the name of the function that +handles the command. + +### Insert mode commands + +When doing an `i` or `a` command, `normal_cmd()` will call the `edit()` function. +It contains a loop that waits for the next character and handles it. It +returns when leaving Insert mode. + +### Options + +There is a list with all option names in [options.lua](options.lua). + Async event support ------------------- diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 08d9d8e117..d71bcc4bcf 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -14,13 +14,17 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/memory.h" +#include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -30,12 +34,12 @@ #define AUCMD_MAX_PATTERNS 256 // Copy string or array of strings into an empty array. -// Get the event number, unless it is an error. Then goto `goto_name`. -#define GET_ONE_EVENT(event_nr, event_str, goto_name) \ +// Get the event number, unless it is an error. Then do `or_else`. +#define GET_ONE_EVENT(event_nr, event_str, or_else) \ event_T event_nr = \ event_name2nr_str(event_str.data.string); \ VALIDATE_S((event_nr < NUM_EVENTS), "event", event_str.data.string.data, { \ - goto goto_name; \ + or_else; \ }); // ID for associating autocmds created via nvim_create_autocmd @@ -71,7 +75,7 @@ static int64_t next_autocmd_id = 1; /// - buffer: Buffer number or list of buffer numbers for buffer local autocommands /// |autocmd-buflocal|. Cannot be used with {pattern} /// @return Array of autocommands matching the criteria, with each item -/// containing the following fields: +/// containing the following fields: /// - id (number): the autocommand id (only when defined with the API). /// - group (integer): the autocommand group id. /// - group_name (string): the autocommand group name. @@ -79,20 +83,20 @@ static int64_t next_autocmd_id = 1; /// - event (string): the autocommand event. /// - command (string): the autocommand command. Note: this will be empty if a callback is set. /// - callback (function|string|nil): Lua function or name of a Vim script function -/// which is executed when this autocommand is triggered. +/// which is executed when this autocommand is triggered. /// - once (boolean): whether the autocommand is only run once. /// - pattern (string): the autocommand pattern. -/// If the autocommand is buffer local |autocmd-buffer-local|: +/// If the autocommand is buffer local |autocmd-buffer-local|: /// - buflocal (boolean): true if the autocommand is buffer local. /// - buffer (number): the buffer number. -Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) +Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err) FUNC_API_SINCE(9) { // TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... }) - Array autocmd_list = ARRAY_DICT_INIT; + ArrayBuilder autocmd_list = KV_INITIAL_VALUE; + kvi_init(autocmd_list); char *pattern_filters[AUCMD_MAX_PATTERNS]; - char pattern_buflocal[BUFLOCAL_PAT_LEN]; Array buffers = ARRAY_DICT_INIT; @@ -128,7 +132,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) Object v = opts->event; if (v.type == kObjectTypeString) { - GET_ONE_EVENT(event_nr, v, cleanup); + GET_ONE_EVENT(event_nr, v, goto cleanup); event_set[event_nr] = true; } else if (v.type == kObjectTypeArray) { FOREACH_ITEM(v.data.array, event_v, { @@ -136,7 +140,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) goto cleanup; }); - GET_ONE_EVENT(event_nr, event_v, cleanup); + GET_ONE_EVENT(event_nr, event_v, goto cleanup); event_set[event_nr] = true; }) } else { @@ -184,8 +188,9 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) goto cleanup; } - snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); - ADD(buffers, CSTR_TO_OBJ(pattern_buflocal)); + String pat = arena_printf(arena, "<buffer=%d>", (int)buf->handle); + buffers = arena_array(arena, 1); + ADD_C(buffers, STRING_OBJ(pat)); } else if (opts->buffer.type == kObjectTypeArray) { if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) { api_set_error(err, kErrorTypeValidation, "Too many buffers (maximum of %d)", @@ -193,6 +198,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) goto cleanup; } + buffers = arena_array(arena, kv_size(opts->buffer.data.array)); FOREACH_ITEM(opts->buffer.data.array, bufnr, { VALIDATE_EXP((bufnr.type == kObjectTypeInteger || bufnr.type == kObjectTypeBuffer), "buffer", "Integer", api_typename(bufnr.type), { @@ -204,8 +210,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) goto cleanup; } - snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); - ADD(buffers, CSTR_TO_OBJ(pattern_buflocal)); + ADD_C(buffers, STRING_OBJ(arena_printf(arena, "<buffer=%d>", (int)buf->handle))); }); } else if (HAS_KEY(opts, get_autocmds, buffer)) { VALIDATE_EXP(false, "buffer", "Integer or Array", api_typename(opts->buffer.type), { @@ -247,6 +252,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) char *pat = pattern_filters[j]; int patlen = (int)strlen(pat); + char pattern_buflocal[BUFLOCAL_PAT_LEN]; if (aupat_is_buflocal(pat, patlen)) { aupat_normalize_buflocal_pat(pattern_buflocal, pat, patlen, aupat_get_buflocal_nr(pat, patlen)); @@ -264,51 +270,51 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) } } - Dictionary autocmd_info = ARRAY_DICT_INIT; + Dictionary autocmd_info = arena_dict(arena, 11); if (ap->group != AUGROUP_DEFAULT) { - PUT(autocmd_info, "group", INTEGER_OBJ(ap->group)); - PUT(autocmd_info, "group_name", CSTR_TO_OBJ(augroup_name(ap->group))); + PUT_C(autocmd_info, "group", INTEGER_OBJ(ap->group)); + PUT_C(autocmd_info, "group_name", CSTR_AS_OBJ(augroup_name(ap->group))); } if (ac->id > 0) { - PUT(autocmd_info, "id", INTEGER_OBJ(ac->id)); + PUT_C(autocmd_info, "id", INTEGER_OBJ(ac->id)); } if (ac->desc != NULL) { - PUT(autocmd_info, "desc", CSTR_TO_OBJ(ac->desc)); + PUT_C(autocmd_info, "desc", CSTR_AS_OBJ(ac->desc)); } if (ac->exec.type == CALLABLE_CB) { - PUT(autocmd_info, "command", STRING_OBJ(STRING_INIT)); + PUT_C(autocmd_info, "command", STRING_OBJ(STRING_INIT)); Callback *cb = &ac->exec.callable.cb; switch (cb->type) { case kCallbackLua: if (nlua_ref_is_function(cb->data.luaref)) { - PUT(autocmd_info, "callback", LUAREF_OBJ(api_new_luaref(cb->data.luaref))); + PUT_C(autocmd_info, "callback", LUAREF_OBJ(api_new_luaref(cb->data.luaref))); } break; case kCallbackFuncref: case kCallbackPartial: - PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb))); + PUT_C(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb, arena))); break; case kCallbackNone: abort(); } } else { - PUT(autocmd_info, "command", CSTR_TO_OBJ(ac->exec.callable.cmd)); + PUT_C(autocmd_info, "command", CSTR_AS_OBJ(ac->exec.callable.cmd)); } - PUT(autocmd_info, "pattern", CSTR_TO_OBJ(ap->pat)); - PUT(autocmd_info, "event", CSTR_TO_OBJ(event_nr2name(event))); - PUT(autocmd_info, "once", BOOLEAN_OBJ(ac->once)); + PUT_C(autocmd_info, "pattern", CSTR_AS_OBJ(ap->pat)); + PUT_C(autocmd_info, "event", CSTR_AS_OBJ(event_nr2name(event))); + PUT_C(autocmd_info, "once", BOOLEAN_OBJ(ac->once)); if (ap->buflocal_nr) { - PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(true)); - PUT(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr)); + PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(true)); + PUT_C(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr)); } else { - PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(false)); + PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(false)); } // TODO(sctx): It would be good to unify script_ctx to actually work with lua @@ -325,16 +331,15 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) // Once we do that, we can put these into the autocmd_info, but I don't think it's // useful to do that at this time. // - // PUT(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid)); - // PUT(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum)); + // PUT_C(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid)); + // PUT_C(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum)); - ADD(autocmd_list, DICTIONARY_OBJ(autocmd_info)); + kvi_push(autocmd_list, DICTIONARY_OBJ(autocmd_info)); } } cleanup: - api_free_array(buffers); - return autocmd_list; + return arena_take_arraybuilder(arena, &autocmd_list); } /// Creates an |autocommand| event handler, defined by `callback` (Lua function or Vimscript @@ -375,15 +380,16 @@ cleanup: /// |autocmd-buflocal|. Cannot be used with {pattern}. /// - desc (string) optional: description (for documentation and troubleshooting). /// - callback (function|string) optional: Lua function (or Vimscript function name, if -/// string) called when the event(s) is triggered. Lua callback can return true to -/// delete the autocommand, and receives a table argument with these keys: +/// string) called when the event(s) is triggered. Lua callback can return a truthy +/// value (not `false` or `nil`) to delete the autocommand. Receives a table argument +/// with these keys: /// - id: (number) autocommand id /// - event: (string) name of the triggered event |autocmd-events| /// - group: (number|nil) autocommand group id, if any -/// - match: (string) expanded value of |<amatch>| -/// - buf: (number) expanded value of |<abuf>| -/// - file: (string) expanded value of |<afile>| -/// - data: (any) arbitrary data passed from |nvim_exec_autocmds()| +/// - match: (string) expanded value of [<amatch>] +/// - buf: (number) expanded value of [<abuf>] +/// - file: (string) expanded value of [<afile>] +/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()] /// - command (string) optional: Vim command to execute on event. Cannot be used with /// {callback} /// - once (boolean) optional: defaults to false. Run the autocommand @@ -395,17 +401,16 @@ cleanup: /// @see |autocommand| /// @see |nvim_del_autocmd()| Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts, - Error *err) + Arena *arena, Error *err) FUNC_API_SINCE(9) { int64_t autocmd_id = -1; char *desc = NULL; - Array patterns = ARRAY_DICT_INIT; - Array event_array = ARRAY_DICT_INIT; AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT; Callback cb = CALLBACK_NONE; - if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { + Array event_array = unpack_string_or_array(event, "event", true, arena, err); + if (ERROR_SET(err)) { goto cleanup; } @@ -428,7 +433,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc }); cb.type = kCallbackLua; - cb.data.luaref = api_new_luaref(callback->data.luaref); + cb.data.luaref = callback->data.luaref; + callback->data.luaref = LUA_NOREF; break; case kObjectTypeString: cb.type = kCallbackFuncref; @@ -464,7 +470,9 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc goto cleanup; }); - if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) { + Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "*", + arena, err); + if (ERROR_SET(err)) { goto cleanup; } @@ -472,17 +480,13 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc desc = opts->desc.data; } - if (patterns.size == 0) { - ADD(patterns, STATIC_CSTR_TO_OBJ("*")); - } - VALIDATE_R((event_array.size > 0), "event", { goto cleanup; }); autocmd_id = next_autocmd_id++; FOREACH_ITEM(event_array, event_str, { - GET_ONE_EVENT(event_nr, event_str, cleanup); + GET_ONE_EVENT(event_nr, event_str, goto cleanup); int retval; @@ -509,8 +513,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc cleanup: aucmd_exec_free(&aucmd); - api_free_array(event_array); - api_free_array(patterns); return autocmd_id; } @@ -534,9 +536,9 @@ void nvim_del_autocmd(Integer id, Error *err) /// @param opts Parameters /// - event: (string|table) /// Examples: -/// - event: "pat1" -/// - event: { "pat1" } -/// - event: { "pat1", "pat2", "pat3" } +/// - event: "pat1" +/// - event: { "pat1" } +/// - event: { "pat1", "pat2", "pat3" } /// - pattern: (string|table) /// - pattern or patterns to match exactly. /// - For example, if you have `*.py` as that pattern for the autocmd, @@ -550,7 +552,7 @@ void nvim_del_autocmd(Integer id, Error *err) /// - group: (string|int) The augroup name or id. /// - NOTE: If not passed, will only delete autocmds *not* in any group. /// -void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err) +void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Arena *arena, Error *err) FUNC_API_SINCE(9) { // TODO(tjdevries): Future improvements: @@ -559,33 +561,29 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err) // - group: Allow passing "*" or true or something like that to force doing all // autocmds, regardless of their group. - Array patterns = ARRAY_DICT_INIT; - Array event_array = ARRAY_DICT_INIT; - - if (!unpack_string_or_array(&event_array, &opts->event, "event", false, err)) { - goto cleanup; + Array event_array = unpack_string_or_array(opts->event, "event", false, arena, err); + if (ERROR_SET(err)) { + return; } bool has_buffer = HAS_KEY(opts, clear_autocmds, buffer); VALIDATE((!HAS_KEY(opts, clear_autocmds, pattern) || !has_buffer), "%s", "Cannot use both 'pattern' and 'buffer'", { - goto cleanup; + return; }); int au_group = get_augroup_from_object(opts->group, err); if (au_group == AUGROUP_ERROR) { - goto cleanup; - } - - if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) { - goto cleanup; + return; } // When we create the autocmds, we want to say that they are all matched, so that's * // but when we clear them, we want to say that we didn't pass a pattern, so that's NUL - if (patterns.size == 0) { - ADD(patterns, STATIC_CSTR_TO_OBJ("")); + Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "", + arena, err); + if (ERROR_SET(err)) { + return; } // If we didn't pass any events, that means clear all events. @@ -594,26 +592,22 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err) FOREACH_ITEM(patterns, pat_object, { char *pat = pat_object.data.string.data; if (!clear_autocmd(event, pat, au_group, err)) { - goto cleanup; + return; } }); } } else { FOREACH_ITEM(event_array, event_str, { - GET_ONE_EVENT(event_nr, event_str, cleanup); + GET_ONE_EVENT(event_nr, event_str, return ); FOREACH_ITEM(patterns, pat_object, { char *pat = pat_object.data.string.data; if (!clear_autocmd(event_nr, pat, au_group, err)) { - goto cleanup; + return; } }); }); } - -cleanup: - api_free_array(event_array); - api_free_array(patterns); } /// Create or get an autocommand group |autocmd-groups|. @@ -700,11 +694,11 @@ void nvim_del_augroup_by_name(String name, Error *err) /// - buffer (integer) optional: buffer number |autocmd-buflocal|. Cannot be used with /// {pattern}. /// - modeline (bool) optional: defaults to true. Process the -/// modeline after the autocommands |<nomodeline>|. +/// modeline after the autocommands [<nomodeline>]. /// - data (any): arbitrary data to send to the autocommand callback. See /// |nvim_create_autocmd()| for details. /// @see |:doautocmd| -void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) +void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Arena *arena, Error *err) FUNC_API_SINCE(9) { int au_group = AUGROUP_ALL; @@ -712,13 +706,11 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) buf_T *buf = curbuf; - Array patterns = ARRAY_DICT_INIT; - Array event_array = ARRAY_DICT_INIT; - Object *data = NULL; - if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { - goto cleanup; + Array event_array = unpack_string_or_array(event, "event", true, arena, err); + if (ERROR_SET(err)) { + return; } switch (opts->group.type) { @@ -727,19 +719,19 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) case kObjectTypeString: au_group = augroup_find(opts->group.data.string.data); VALIDATE_S((au_group != AUGROUP_ERROR), "group", opts->group.data.string.data, { - goto cleanup; + return; }); break; case kObjectTypeInteger: au_group = (int)opts->group.data.integer; char *name = au_group == 0 ? NULL : augroup_name(au_group); VALIDATE_INT(augroup_exists(name), "group", (int64_t)au_group, { - goto cleanup; + return; }); break; default: VALIDATE_EXP(false, "group", "String or Integer", api_typename(opts->group.type), { - goto cleanup; + return; }); } @@ -747,23 +739,21 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) if (HAS_KEY(opts, exec_autocmds, buffer)) { VALIDATE((!HAS_KEY(opts, exec_autocmds, pattern)), "%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", { - goto cleanup; + return; }); has_buffer = true; buf = find_buffer_by_handle(opts->buffer, err); if (ERROR_SET(err)) { - goto cleanup; + return; } } - if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) { - goto cleanup; - } - - if (patterns.size == 0) { - ADD(patterns, STATIC_CSTR_TO_OBJ("")); + Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "", + arena, err); + if (ERROR_SET(err)) { + return; } if (HAS_KEY(opts, exec_autocmds, data)) { @@ -774,7 +764,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) bool did_aucmd = false; FOREACH_ITEM(event_array, event_str, { - GET_ONE_EVENT(event_nr, event_str, cleanup) + GET_ONE_EVENT(event_nr, event_str, return ) FOREACH_ITEM(patterns, pat, { char *fname = !has_buffer ? pat.data.string.data : NULL; @@ -785,28 +775,26 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) if (did_aucmd && modeline) { do_modelines(0); } - -cleanup: - api_free_array(event_array); - api_free_array(patterns); } -static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err) +static Array unpack_string_or_array(Object v, char *k, bool required, Arena *arena, Error *err) { - if (v->type == kObjectTypeString) { - ADD(*array, copy_object(*v, NULL)); - } else if (v->type == kObjectTypeArray) { - if (!check_string_array(v->data.array, k, true, err)) { - return false; + if (v.type == kObjectTypeString) { + Array arr = arena_array(arena, 1); + ADD_C(arr, v); + return arr; + } else if (v.type == kObjectTypeArray) { + if (!check_string_array(v.data.array, k, true, err)) { + return (Array)ARRAY_DICT_INIT; } - *array = copy_array(v->data.array, NULL); + return v.data.array; } else { - VALIDATE_EXP(!required, k, "Array or String", api_typename(v->type), { - return false; + VALIDATE_EXP(!required, k, "Array or String", api_typename(v.type), { + return (Array)ARRAY_DICT_INIT; }); } - return true; + return (Array)ARRAY_DICT_INIT; } // Returns AUGROUP_ERROR if there was a problem with {group} @@ -838,55 +826,57 @@ static int get_augroup_from_object(Object group, Error *err) } } -static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, bool has_buffer, - Buffer buffer, Error *err) +static Array get_patterns_from_pattern_or_buf(Object pattern, bool has_buffer, Buffer buffer, + char *fallback, Arena *arena, Error *err) { - const char pattern_buflocal[BUFLOCAL_PAT_LEN]; + ArrayBuilder patterns = ARRAY_DICT_INIT; + kvi_init(patterns); if (pattern.type != kObjectTypeNil) { - Object *v = &pattern; - - if (v->type == kObjectTypeString) { - const char *pat = v->data.string.data; + if (pattern.type == kObjectTypeString) { + const char *pat = pattern.data.string.data; size_t patlen = aucmd_pattern_length(pat); while (patlen) { - ADD(*patterns, STRING_OBJ(cbuf_to_string(pat, patlen))); + kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen)); pat = aucmd_next_pattern(pat, patlen); patlen = aucmd_pattern_length(pat); } - } else if (v->type == kObjectTypeArray) { - if (!check_string_array(v->data.array, "pattern", true, err)) { - return false; + } else if (pattern.type == kObjectTypeArray) { + if (!check_string_array(pattern.data.array, "pattern", true, err)) { + return (Array)ARRAY_DICT_INIT; } - Array array = v->data.array; + Array array = pattern.data.array; FOREACH_ITEM(array, entry, { const char *pat = entry.data.string.data; size_t patlen = aucmd_pattern_length(pat); while (patlen) { - ADD(*patterns, STRING_OBJ(cbuf_to_string(pat, patlen))); + kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen)); pat = aucmd_next_pattern(pat, patlen); patlen = aucmd_pattern_length(pat); } }) } else { - VALIDATE_EXP(false, "pattern", "String or Table", api_typename(v->type), { - return false; + VALIDATE_EXP(false, "pattern", "String or Table", api_typename(pattern.type), { + return (Array)ARRAY_DICT_INIT; }); } } else if (has_buffer) { buf_T *buf = find_buffer_by_handle(buffer, err); if (ERROR_SET(err)) { - return false; + return (Array)ARRAY_DICT_INIT; } - snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); - ADD(*patterns, CSTR_TO_OBJ(pattern_buflocal)); + kvi_push(patterns, STRING_OBJ(arena_printf(arena, "<buffer=%d>", (int)buf->handle))); } - return true; + if (kv_size(patterns) == 0 && fallback) { + kvi_push(patterns, CSTR_AS_OBJ(fallback)); + } + + return arena_take_arraybuilder(arena, &patterns); } static bool clear_autocmd(event_T event, char *pat, int au_group, Error *err) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 0df231868d..7f195de959 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -12,10 +12,12 @@ #include "nvim/api/buffer.h" #include "nvim/api/keysets_defs.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" @@ -24,28 +26,32 @@ #include "nvim/drawscreen.h" #include "nvim/ex_cmds.h" #include "nvim/extmark.h" -#include "nvim/func_attr.h" +#include "nvim/extmark_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" +#include "nvim/marktree_defs.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/pos_defs.h" #include "nvim/state_defs.h" #include "nvim/types_defs.h" #include "nvim/undo.h" +#include "nvim/undo_defs.h" #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/buffer.c.generated.h" #endif -/// \defgroup api-buffer -/// -/// \brief For more information on buffers, see |buffers| +/// @brief <pre>help +/// For more information on buffers, see |buffers|. /// /// Unloaded Buffers: ~ /// @@ -57,6 +63,7 @@ /// /// You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()| to check /// whether a buffer is loaded. +/// </pre> /// Returns the number of lines in the given buffer. /// @@ -105,7 +112,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// Not for Lua callbacks. /// @param opts Optional parameters. /// - on_lines: Lua callback invoked on change. -/// Return `true` to detach. Args: +/// Return a truthy value (not `false` or `nil`) to detach. Args: /// - the string "lines" /// - buffer handle /// - b:changedtick @@ -118,8 +125,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// - on_bytes: Lua callback invoked on change. /// This callback receives more granular information about the /// change compared to on_lines. -/// Return `true` to detach. -/// Args: +/// Return a truthy value (not `false` or `nil`) to detach. Args: /// - the string "bytes" /// - buffer handle /// - b:changedtick @@ -127,11 +133,13 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// - start column of the changed text /// - byte offset of the changed text (from the start of /// the buffer) -/// - old end row of the changed text +/// - old end row of the changed text (offset from start row) /// - old end column of the changed text +/// (if old end row = 0, offset from start column) /// - old end byte length of the changed text -/// - new end row of the changed text +/// - new end row of the changed text (offset from start row) /// - new end column of the changed text +/// (if new end row = 0, offset from start column) /// - new end byte length of the changed text /// - on_changedtick: Lua callback invoked on changedtick /// increment without text change. Args: @@ -153,7 +161,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// @return False if attach failed (invalid parameter, or buffer isn't loaded); /// otherwise True. TODO: LUA_API_NO_EVAL Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer, - DictionaryOf(LuaRef) opts, Error *err) + Dict(buf_attach) *opts, Error *err) FUNC_API_SINCE(4) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -162,64 +170,40 @@ Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer, return false; } - bool is_lua = (channel_id == LUA_INTERNAL_CALL); BufUpdateCallbacks cb = BUF_UPDATE_CALLBACKS_INIT; - struct { - const char *name; - LuaRef *dest; - } cbs[] = { - { "on_lines", &cb.on_lines }, - { "on_bytes", &cb.on_bytes }, - { "on_changedtick", &cb.on_changedtick }, - { "on_detach", &cb.on_detach }, - { "on_reload", &cb.on_reload }, - { NULL, NULL }, - }; - - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - bool key_used = false; - if (is_lua) { - for (size_t j = 0; cbs[j].name; j++) { - if (strequal(cbs[j].name, k.data)) { - VALIDATE_T(cbs[j].name, kObjectTypeLuaRef, v->type, { - goto error; - }); - *(cbs[j].dest) = v->data.luaref; - v->data.luaref = LUA_NOREF; - key_used = true; - break; - } - } - if (key_used) { - continue; - } else if (strequal("utf_sizes", k.data)) { - VALIDATE_T("utf_sizes", kObjectTypeBoolean, v->type, { - goto error; - }); - cb.utf_sizes = v->data.boolean; - key_used = true; - } else if (strequal("preview", k.data)) { - VALIDATE_T("preview", kObjectTypeBoolean, v->type, { - goto error; - }); - cb.preview = v->data.boolean; - key_used = true; - } + if (channel_id == LUA_INTERNAL_CALL) { + if (HAS_KEY(opts, buf_attach, on_lines)) { + cb.on_lines = opts->on_lines; + opts->on_lines = LUA_NOREF; } - VALIDATE_S(key_used, "'opts' key", k.data, { - goto error; - }); + if (HAS_KEY(opts, buf_attach, on_bytes)) { + cb.on_bytes = opts->on_bytes; + opts->on_bytes = LUA_NOREF; + } + + if (HAS_KEY(opts, buf_attach, on_changedtick)) { + cb.on_changedtick = opts->on_changedtick; + opts->on_changedtick = LUA_NOREF; + } + + if (HAS_KEY(opts, buf_attach, on_detach)) { + cb.on_detach = opts->on_detach; + opts->on_detach = LUA_NOREF; + } + + if (HAS_KEY(opts, buf_attach, on_reload)) { + cb.on_reload = opts->on_reload; + opts->on_reload = LUA_NOREF; + } + + cb.utf_sizes = opts->utf_sizes; + + cb.preview = opts->preview; } return buf_updates_register(buf, channel_id, cb, send_buffer); - -error: - buffer_update_callbacks_free(cb); - return false; } /// Deactivates buffer-update events on the channel. @@ -245,6 +229,7 @@ Boolean nvim_buf_detach(uint64_t channel_id, Buffer buffer, Error *err) return true; } +/// @nodoc void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -279,6 +264,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, Integer start, Integer end, Boolean strict_indexing, + Arena *arena, lua_State *lstate, Error *err) FUNC_API_SINCE(1) @@ -310,18 +296,10 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, size_t size = (size_t)(end - start); - init_line_array(lstate, &rv, size); - - if (!buf_collect_lines(buf, size, (linenr_T)start, 0, (channel_id != VIML_INTERNAL_CALL), &rv, - lstate, err)) { - goto end; - } + init_line_array(lstate, &rv, size, arena); -end: - if (ERROR_SET(err)) { - api_free_array(rv); - rv.items = NULL; - } + buf_collect_lines(buf, size, (linenr_T)start, 0, (channel_id != VIML_INTERNAL_CALL), &rv, + lstate, arena); return rv; } @@ -348,7 +326,8 @@ end: /// @param replacement Array of lines to use as replacement /// @param[out] err Error details, if any void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integer end, - Boolean strict_indexing, ArrayOf(String) replacement, Error *err) + Boolean strict_indexing, ArrayOf(String) replacement, Arena *arena, + Error *err) FUNC_API_SINCE(1) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { @@ -358,12 +337,10 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ return; } - // load buffer first if it's not loaded - if (buf->b_ml.ml_mfp == NULL) { - if (!buf_ensure_loaded(buf)) { - api_set_error(err, kErrorTypeException, "Failed to load buffer"); - return; - } + // Load buffer if necessary. #22670 + if (!buf_ensure_loaded(buf)) { + api_set_error(err, kErrorTypeException, "Failed to load buffer"); + return; } bool oob = false; @@ -385,14 +362,14 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ size_t new_len = replacement.size; size_t old_len = (size_t)(end - start); ptrdiff_t extra = 0; // lines added to text, can be negative - char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; + char **lines = (new_len != 0) ? arena_alloc(arena, new_len * sizeof(char *), true) : NULL; for (size_t i = 0; i < new_len; i++) { const String l = replacement.items[i].data.string; // Fill lines[i] with l's contents. Convert NULs to newlines as required by // NL-used-for-NUL. - lines[i] = xmemdupz(l.data, l.size); + lines[i] = arena_memdupz(arena, l.data, l.size); memchrsub(lines[i], NUL, NL, l.size); } @@ -437,15 +414,12 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ goto end; }); - if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false) == FAIL) { + if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) { api_set_error(err, kErrorTypeException, "Failed to replace line"); goto end; } inserted_bytes += (bcount_t)strlen(lines[i]) + 1; - // Mark lines that haven't been passed to the buffer as they need - // to be freed later - lines[i] = NULL; } // Now we may need to insert the remaining new old_len @@ -463,9 +437,6 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ inserted_bytes += (bcount_t)strlen(lines[i]) + 1; - // Same as with replacing, but we also need to free lines - xfree(lines[i]); - lines[i] = NULL; extra++; } @@ -488,11 +459,6 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ } end: - for (size_t i = 0; i < new_len; i++) { - xfree(lines[i]); - } - - xfree(lines); try_end(err); } @@ -525,7 +491,8 @@ end: /// @param replacement Array of lines to use as replacement /// @param[out] err Error details, if any void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, Integer start_col, - Integer end_row, Integer end_col, ArrayOf(String) replacement, Error *err) + Integer end_row, Integer end_col, ArrayOf(String) replacement, Arena *arena, + Error *err) FUNC_API_SINCE(7) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { @@ -540,12 +507,10 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In return; } - // load buffer first if it's not loaded - if (buf->b_ml.ml_mfp == NULL) { - if (!buf_ensure_loaded(buf)) { - api_set_error(err, kErrorTypeException, "Failed to load buffer"); - return; - } + // Load buffer if necessary. #22670 + if (!buf_ensure_loaded(buf)) { + api_set_error(err, kErrorTypeException, "Failed to load buffer"); + return; } bool oob = false; @@ -562,33 +527,31 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In return; }); - char *str_at_start = NULL; - char *str_at_end = NULL; - - // Another call to ml_get_buf() may free the line, so make a copy. - str_at_start = xstrdup(ml_get_buf(buf, (linenr_T)start_row)); + // Another call to ml_get_buf() may free the lines, so we make copies + char *str_at_start = ml_get_buf(buf, (linenr_T)start_row); size_t len_at_start = strlen(str_at_start); + str_at_start = arena_memdupz(arena, str_at_start, len_at_start); start_col = start_col < 0 ? (int64_t)len_at_start + start_col + 1 : start_col; VALIDATE_RANGE((start_col >= 0 && (size_t)start_col <= len_at_start), "start_col", { - goto early_end; + return; }); - // Another call to ml_get_buf() may free the line, so make a copy. - str_at_end = xstrdup(ml_get_buf(buf, (linenr_T)end_row)); + char *str_at_end = ml_get_buf(buf, (linenr_T)end_row); size_t len_at_end = strlen(str_at_end); + str_at_end = arena_memdupz(arena, str_at_end, len_at_end); end_col = end_col < 0 ? (int64_t)len_at_end + end_col + 1 : end_col; VALIDATE_RANGE((end_col >= 0 && (size_t)end_col <= len_at_end), "end_col", { - goto early_end; + return; }); VALIDATE((start_row <= end_row && !(end_row == start_row && start_col > end_col)), "%s", "'start' is higher than 'end'", { - goto early_end; + return; }); bool disallow_nl = (channel_id != VIML_INTERNAL_CALL); if (!check_string_array(replacement, "replacement string", disallow_nl, err)) { - goto early_end; + return; } size_t new_len = replacement.size; @@ -618,7 +581,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In if (replacement.size == 1) { firstlen += last_part_len; } - char *first = xmallocz(firstlen); + char *first = arena_allocz(arena, firstlen); char *last = NULL; memcpy(first, str_at_start, (size_t)start_col); memcpy(first + start_col, first_item.data, first_item.size); @@ -626,13 +589,13 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In if (replacement.size == 1) { memcpy(first + start_col + first_item.size, str_at_end + end_col, last_part_len); } else { - last = xmallocz(last_item.size + last_part_len); + last = arena_allocz(arena, last_item.size + last_part_len); memcpy(last, last_item.data, last_item.size); memchrsub(last, NUL, NL, last_item.size); memcpy(last + last_item.size, str_at_end + end_col, last_part_len); } - char **lines = xcalloc(new_len, sizeof(char *)); + char **lines = arena_alloc(arena, new_len * sizeof(char *), true); lines[0] = first; new_byte += (bcount_t)(first_item.size); for (size_t i = 1; i < new_len - 1; i++) { @@ -640,7 +603,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In // Fill lines[i] with l's contents. Convert NULs to newlines as required by // NL-used-for-NUL. - lines[i] = xmemdupz(l.data, l.size); + lines[i] = arena_memdupz(arena, l.data, l.size); memchrsub(lines[i], NUL, NL, l.size); new_byte += (bcount_t)(l.size) + 1; } @@ -692,13 +655,10 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In goto end; }); - if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false) == FAIL) { + if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) { api_set_error(err, kErrorTypeException, "Failed to replace line"); goto end; } - // Mark lines that haven't been passed to the buffer as they need - // to be freed later - lines[i] = NULL; } // Now we may need to insert the remaining new old_len @@ -714,9 +674,6 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In goto end; } - // Same as with replacing, but we also need to free lines - xfree(lines[i]); - lines[i] = NULL; extra++; } @@ -749,15 +706,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In } end: - for (size_t i = 0; i < new_len; i++) { - xfree(lines[i]); - } - xfree(lines); try_end(err); - -early_end: - xfree(str_at_start); - xfree(str_at_end); } /// Gets a range from the buffer. @@ -782,16 +731,12 @@ early_end: ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer, Integer start_row, Integer start_col, Integer end_row, Integer end_col, - Dictionary opts, lua_State *lstate, - Error *err) + Dict(empty) *opts, + Arena *arena, lua_State *lstate, Error *err) FUNC_API_SINCE(9) { Array rv = ARRAY_DICT_INIT; - VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", { - return rv; - }); - buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -822,44 +767,38 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer, size_t size = (size_t)(end_row - start_row) + 1; - init_line_array(lstate, &rv, size); + init_line_array(lstate, &rv, size, arena); if (start_row == end_row) { String line = buf_get_text(buf, start_row, start_col, end_col, err); if (ERROR_SET(err)) { goto end; } - push_linestr(lstate, &rv, line.data, line.size, 0, replace_nl); + push_linestr(lstate, &rv, line.data, line.size, 0, replace_nl, arena); return rv; } String str = buf_get_text(buf, start_row, start_col, MAXCOL - 1, err); - - push_linestr(lstate, &rv, str.data, str.size, 0, replace_nl); - if (ERROR_SET(err)) { goto end; } + push_linestr(lstate, &rv, str.data, str.size, 0, replace_nl, arena); + if (size > 2) { - if (!buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, 1, replace_nl, &rv, lstate, - err)) { - goto end; - } + buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, 1, replace_nl, &rv, lstate, arena); } str = buf_get_text(buf, end_row, 0, end_col, err); - push_linestr(lstate, &rv, str.data, str.size, (int)(size - 1), replace_nl); - if (ERROR_SET(err)) { goto end; } + push_linestr(lstate, &rv, str.data, str.size, (int)(size - 1), replace_nl, arena); + end: if (ERROR_SET(err)) { - api_free_array(rv); - rv.size = 0; - rv.items = NULL; + return (Array)ARRAY_DICT_INIT; } return rv; @@ -905,7 +844,7 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_buf_get_var(Buffer buffer, String name, Error *err) +Object nvim_buf_get_var(Buffer buffer, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -914,7 +853,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err) return (Object)OBJECT_INIT; } - return dict_get_value(buf->b_vars, name, err); + return dict_get_value(buf->b_vars, name, arena, err); } /// Gets a changed tick of a buffer @@ -937,12 +876,12 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) /// Gets a list of buffer-local |mapping| definitions. /// -/// @param mode Mode short-name ("n", "i", "v", ...) /// @param buffer Buffer handle, or 0 for current buffer +/// @param mode Mode short-name ("n", "i", "v", ...) /// @param[out] err Error details, if any /// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key holds the associated buffer handle. -ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err) +ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Arena *arena, Error *err) FUNC_API_SINCE(3) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -951,7 +890,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err) return (Array)ARRAY_DICT_INIT; } - return keymap_array(mode, buf); + return keymap_array(mode, buf, arena); } /// Sets a buffer-local |mapping| for the given mode. @@ -993,7 +932,7 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err) return; } - dict_set_var(buf->b_vars, name, value, false, false, err); + dict_set_var(buf->b_vars, name, value, false, false, NULL, err); } /// Removes a buffer-scoped (b:) variable @@ -1010,7 +949,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err) return; } - dict_set_var(buf->b_vars, name, NIL, true, false, err); + dict_set_var(buf->b_vars, name, NIL, true, false, NULL, err); } /// Gets the full file name for the buffer @@ -1018,7 +957,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err) /// @param buffer Buffer handle, or 0 for current buffer /// @param[out] err Error details, if any /// @return Buffer name -String nvim_buf_get_name(Buffer buffer, Arena *arena, Error *err) +String nvim_buf_get_name(Buffer buffer, Error *err) FUNC_API_SINCE(1) { String rv = STRING_INIT; @@ -1082,7 +1021,7 @@ Boolean nvim_buf_is_loaded(Buffer buffer) /// @param opts Optional parameters. Keys: /// - force: Force deletion and ignore unsaved changes. /// - unload: Unloaded only, do not delete. See |:bunload| -void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err) +void nvim_buf_delete(Buffer buffer, Dict(buf_delete) *opts, Error *err) FUNC_API_SINCE(7) FUNC_API_TEXTLOCK { @@ -1092,25 +1031,9 @@ void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err) return; } - bool force = false; - bool unload = false; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object v = opts.items[i].value; - if (strequal("force", k.data)) { - force = api_object_to_bool(v, "force", false, err); - } else if (strequal("unload", k.data)) { - unload = api_object_to_bool(v, "unload", false, err); - } else { - VALIDATE_S(false, "'opts' key", k.data, { - return; - }); - } - } + bool force = opts->force; - if (ERROR_SET(err)) { - return; - } + bool unload = opts->unload; int result = do_buffer(unload ? DOBUF_UNLOAD : DOBUF_WIPE, DOBUF_FIRST, @@ -1194,7 +1117,7 @@ Boolean nvim_buf_del_mark(Buffer buffer, String name, Error *err) /// @return true if the mark was set, else false. /// @see |nvim_buf_del_mark()| /// @see |nvim_buf_get_mark()| -Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col, Dictionary opts, +Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col, Dict(empty) *opts, Error *err) FUNC_API_SINCE(8) { @@ -1227,7 +1150,7 @@ Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col, /// uppercase/file mark set in another buffer. /// @see |nvim_buf_set_mark()| /// @see |nvim_buf_del_mark()| -ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) +ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; @@ -1257,8 +1180,9 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) pos = fm->mark; } - ADD(rv, INTEGER_OBJ(pos.lnum)); - ADD(rv, INTEGER_OBJ(pos.col)); + rv = arena_array(arena, 2); + ADD_C(rv, INTEGER_OBJ(pos.lnum)); + ADD_C(rv, INTEGER_OBJ(pos.col)); return rv; } @@ -1279,8 +1203,7 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) /// @param fun Function to call inside the buffer (currently Lua callable /// only) /// @param[out] err Error details, if any -/// @return Return value of function. NB: will deepcopy Lua values -/// currently, use upvalues to send Lua references in and out. +/// @return Return value of function. Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) FUNC_API_SINCE(7) FUNC_API_LUA_ONLY @@ -1294,35 +1217,35 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) aucmd_prepbuf(&aco, buf); Array args = ARRAY_DICT_INIT; - Object res = nlua_call_ref(fun, NULL, args, true, err); + Object res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err); aucmd_restbuf(&aco); try_end(err); return res; } -Dictionary nvim__buf_stats(Buffer buffer, Error *err) +/// @nodoc +Dictionary nvim__buf_stats(Buffer buffer, Arena *arena, Error *err) { - Dictionary rv = ARRAY_DICT_INIT; - buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { - return rv; + return (Dictionary)ARRAY_DICT_INIT; } + Dictionary rv = arena_dict(arena, 7); // Number of times the cached line was flushed. // This should generally not increase while editing the same // line in the same mode. - PUT(rv, "flush_count", INTEGER_OBJ(buf->flush_count)); + PUT_C(rv, "flush_count", INTEGER_OBJ(buf->flush_count)); // lnum of current line - PUT(rv, "current_lnum", INTEGER_OBJ(buf->b_ml.ml_line_lnum)); + PUT_C(rv, "current_lnum", INTEGER_OBJ(buf->b_ml.ml_line_lnum)); // whether the line has unflushed changes. - PUT(rv, "line_dirty", BOOLEAN_OBJ(buf->b_ml.ml_flags & ML_LINE_DIRTY)); + PUT_C(rv, "line_dirty", BOOLEAN_OBJ(buf->b_ml.ml_flags & ML_LINE_DIRTY)); // NB: this should be zero at any time API functions are called, // this exists to debug issues - PUT(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes)); - PUT(rv, "dirty_bytes2", INTEGER_OBJ((Integer)buf->deleted_bytes2)); - PUT(rv, "virt_blocks", INTEGER_OBJ((Integer)buf->b_virt_line_blocks)); + PUT_C(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes)); + PUT_C(rv, "dirty_bytes2", INTEGER_OBJ((Integer)buf->deleted_bytes2)); + PUT_C(rv, "virt_blocks", INTEGER_OBJ((Integer)buf_meta_total(buf, kMTMetaLines))); u_header_T *uhp = NULL; if (buf->b_u_curhead != NULL) { @@ -1331,7 +1254,7 @@ Dictionary nvim__buf_stats(Buffer buffer, Error *err) uhp = buf->b_u_newhead; } if (uhp) { - PUT(rv, "uhp_extmark_size", INTEGER_OBJ((Integer)kv_size(uhp->uh_extmark))); + PUT_C(rv, "uhp_extmark_size", INTEGER_OBJ((Integer)kv_size(uhp->uh_extmark))); } return rv; @@ -1434,13 +1357,12 @@ static void fix_cursor_cols(win_T *win, linenr_T start_row, colnr_T start_col, l /// @param lstate Lua state. When NULL the Array is initialized instead. /// @param a Array to initialize /// @param size Size of array -static inline void init_line_array(lua_State *lstate, Array *a, size_t size) +static inline void init_line_array(lua_State *lstate, Array *a, size_t size, Arena *arena) { if (lstate) { lua_createtable(lstate, (int)size, 0); } else { - a->size = size; - a->items = xcalloc(a->size, sizeof(Object)); + *a = arena_array(arena, size); } } @@ -1453,14 +1375,15 @@ static inline void init_line_array(lua_State *lstate, Array *a, size_t size) /// @param a Array to push onto when not using Lua /// @param s String to push /// @param len Size of string -/// @param idx 0-based index to place s +/// @param idx 0-based index to place s (only used for Lua) /// @param replace_nl Replace newlines ('\n') with null ('\0') static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, int idx, - bool replace_nl) + bool replace_nl, Arena *arena) { if (lstate) { // Vim represents NULs as NLs if (s && replace_nl && strchr(s, '\n')) { + // TODO(bfredl): could manage scratch space in the arena, for the NUL case char *tmp = xmemdupz(s, len); strchrsub(tmp, '\n', '\0'); lua_pushlstring(lstate, tmp, len); @@ -1471,15 +1394,15 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, lua_rawseti(lstate, -2, idx + 1); } else { String str = STRING_INIT; - if (s) { - str = cbuf_to_string(s, len); + if (len > 0) { + str = CBUF_TO_ARENA_STR(arena, s, len); if (replace_nl) { // Vim represents NULs as NLs, but this may confuse clients. strchrsub(str.data, '\n', '\0'); } } - a->items[idx] = STRING_OBJ(str); + ADD_C(*a, STRING_OBJ(str)); } } @@ -1490,27 +1413,17 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, /// @param n Number of lines to collect /// @param replace_nl Replace newlines ("\n") with NUL /// @param start Line number to start from -/// @param start_idx First index to push to +/// @param start_idx First index to push to (only used for Lua) /// @param[out] l If not NULL, Lines are copied here /// @param[out] lstate If not NULL, Lines are pushed into a table onto the stack /// @param err[out] Error, if any /// @return true unless `err` was set -bool buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool replace_nl, - Array *l, lua_State *lstate, Error *err) +void buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool replace_nl, + Array *l, lua_State *lstate, Arena *arena) { for (size_t i = 0; i < n; i++) { linenr_T lnum = start + (linenr_T)i; - - if (lnum >= MAXLNUM) { - if (err != NULL) { - api_set_error(err, kErrorTypeValidation, "Line index is too high"); - } - return false; - } - char *bufstr = ml_get_buf(buf, lnum); - push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl); + push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl, arena); } - - return true; } diff --git a/src/nvim/api/buffer.h b/src/nvim/api/buffer.h index f3971c1d30..fe2d104058 100644 --- a/src/nvim/api/buffer.h +++ b/src/nvim/api/buffer.h @@ -5,7 +5,8 @@ #include "nvim/api/keysets_defs.h" // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep -#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/buffer.h.generated.h" diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 2a57ce9a19..779e216c74 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -13,13 +13,14 @@ #include "nvim/api/private/validate.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer_defs.h" #include "nvim/cmdexpand_defs.h" -#include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" @@ -47,16 +48,16 @@ /// @param[out] err Error details, if any. /// @return Dictionary containing command information, with these keys: /// - cmd: (string) Command name. -/// - range: (array) (optional) Command range (|<line1>| |<line2>|). +/// - range: (array) (optional) Command range ([<line1>] [<line2>]). /// Omitted if command doesn't accept a range. /// Otherwise, has no elements if no range was specified, one element if /// only a single range item was specified, or two elements if both range /// items were specified. -/// - count: (number) (optional) Command |<count>|. +/// - count: (number) (optional) Command [<count>]. /// Omitted if command cannot take a count. -/// - reg: (string) (optional) Command |<register>|. +/// - reg: (string) (optional) Command [<register>]. /// Omitted if command cannot take a register. -/// - bang: (boolean) Whether command contains a |<bang>| (!) modifier. +/// - bang: (boolean) Whether command contains a [<bang>] (!) modifier. /// - args: (array) Command arguments. /// - addr: (string) Value of |:command-addr|. Uses short name or "line" for -addr=lines. /// - nargs: (string) Value of |:command-nargs|. @@ -66,7 +67,7 @@ /// - file: (boolean) The command expands filenames. Which means characters such as "%", /// "#" and wildcards are expanded. /// - bar: (boolean) The "|" character is treated as a command separator and the double -/// quote character (\") is treated as the start of a comment. +/// quote character (") is treated as the start of a comment. /// - mods: (dictionary) |:command-modifiers|. /// - filter: (dictionary) |:filter|. /// - pattern: (string) Filter pattern. Empty string if there is no filter. @@ -95,19 +96,15 @@ /// - "belowright": |:belowright|. /// - "topleft": |:topleft|. /// - "botright": |:botright|. -Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) +Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err) FUNC_API_SINCE(10) FUNC_API_FAST { - Dictionary result = ARRAY_DICT_INIT; - - VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", { - return result; - }); + Dict(cmd) result = KEYDICT_INIT; // Parse command line exarg_T ea; CmdParseInfo cmdinfo; - char *cmdline = string_to_cstr(str); + char *cmdline = arena_memdupz(arena, str.data, str.size); const char *errormsg = NULL; if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) { @@ -127,22 +124,23 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) // otherwise split arguments by whitespace. if (ea.argt & EX_NOSPC) { if (*ea.arg != NUL) { - ADD(args, STRING_OBJ(cstrn_to_string(ea.arg, length))); + args = arena_array(arena, 1); + ADD_C(args, STRING_OBJ(cstrn_as_string(ea.arg, length))); } } else { size_t end = 0; size_t len = 0; - char *buf = xcalloc(length, sizeof(char)); + char *buf = arena_alloc(arena, length + 1, false); bool done = false; + args = arena_array(arena, uc_nargs_upper_bound(ea.arg, length)); while (!done) { done = uc_split_args_iter(ea.arg, length, &end, buf, &len); if (len > 0) { - ADD(args, STRING_OBJ(cstrn_to_string(buf, len))); + ADD_C(args, STRING_OBJ(cstrn_as_string(buf, len))); + buf += len + 1; } } - - xfree(buf); } ucmd_T *cmd = NULL; @@ -152,40 +150,32 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) cmd = USER_CMD_GA(&curbuf->b_ucmds, ea.useridx); } - if (cmd != NULL) { - PUT(result, "cmd", CSTR_TO_OBJ(cmd->uc_name)); - } else { - PUT(result, "cmd", CSTR_TO_OBJ(get_command_name(NULL, ea.cmdidx))); - } + char *name = (cmd != NULL ? cmd->uc_name : get_command_name(NULL, ea.cmdidx)); + PUT_KEY(result, cmd, cmd, cstr_as_string(name)); if (ea.argt & EX_RANGE) { - Array range = ARRAY_DICT_INIT; + Array range = arena_array(arena, 2); if (ea.addr_count > 0) { if (ea.addr_count > 1) { - ADD(range, INTEGER_OBJ(ea.line1)); + ADD_C(range, INTEGER_OBJ(ea.line1)); } - ADD(range, INTEGER_OBJ(ea.line2)); + ADD_C(range, INTEGER_OBJ(ea.line2)); } - PUT(result, "range", ARRAY_OBJ(range)); + PUT_KEY(result, cmd, range, range); } if (ea.argt & EX_COUNT) { - if (ea.addr_count > 0) { - PUT(result, "count", INTEGER_OBJ(ea.line2)); - } else if (cmd != NULL) { - PUT(result, "count", INTEGER_OBJ(cmd->uc_def)); - } else { - PUT(result, "count", INTEGER_OBJ(0)); - } + Integer count = ea.addr_count > 0 ? ea.line2 : (cmd != NULL ? cmd->uc_def : 0); + PUT_KEY(result, cmd, count, count); } if (ea.argt & EX_REGSTR) { char reg[2] = { (char)ea.regname, NUL }; - PUT(result, "reg", CSTR_TO_OBJ(reg)); + PUT_KEY(result, cmd, reg, CSTR_TO_ARENA_STR(arena, reg)); } - PUT(result, "bang", BOOLEAN_OBJ(ea.forceit)); - PUT(result, "args", ARRAY_OBJ(args)); + PUT_KEY(result, cmd, bang, ea.forceit); + PUT_KEY(result, cmd, args, args); char nargs[2]; if (ea.argt & EX_EXTRA) { @@ -204,9 +194,9 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) nargs[0] = '0'; } nargs[1] = '\0'; - PUT(result, "nargs", CSTR_TO_OBJ(nargs)); + PUT_KEY(result, cmd, nargs, CSTR_TO_ARENA_OBJ(arena, nargs)); - const char *addr; + char *addr; switch (ea.addr_type) { case ADDR_LINES: addr = "line"; @@ -236,38 +226,37 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) addr = "?"; break; } - PUT(result, "addr", CSTR_TO_OBJ(addr)); - PUT(result, "nextcmd", CSTR_TO_OBJ(ea.nextcmd)); - - Dictionary mods = ARRAY_DICT_INIT; - - Dictionary filter = ARRAY_DICT_INIT; - PUT(filter, "pattern", cmdinfo.cmdmod.cmod_filter_pat - ? CSTR_TO_OBJ(cmdinfo.cmdmod.cmod_filter_pat) - : STATIC_CSTR_TO_OBJ("")); - PUT(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force)); - PUT(mods, "filter", DICTIONARY_OBJ(filter)); - - PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT)); - PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT)); - PUT(mods, "unsilent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_UNSILENT)); - PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX)); - PUT(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOAUTOCMD)); - PUT(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.cmod_tab - 1)); - PUT(mods, "verbose", INTEGER_OBJ(cmdinfo.cmdmod.cmod_verbose - 1)); - PUT(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_BROWSE)); - PUT(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_CONFIRM)); - PUT(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_HIDE)); - PUT(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPALT)); - PUT(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPJUMPS)); - PUT(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPMARKS)); - PUT(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPPATTERNS)); - PUT(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_LOCKMARKS)); - PUT(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOSWAPFILE)); - PUT(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_VERT)); - PUT(mods, "horizontal", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_HOR)); - - const char *split; + PUT_KEY(result, cmd, addr, CSTR_AS_OBJ(addr)); + PUT_KEY(result, cmd, nextcmd, CSTR_AS_OBJ(ea.nextcmd)); + + // TODO(bfredl): nested keydict would be nice.. + Dictionary mods = arena_dict(arena, 20); + + Dictionary filter = arena_dict(arena, 2); + PUT_C(filter, "pattern", CSTR_TO_ARENA_OBJ(arena, cmdinfo.cmdmod.cmod_filter_pat)); + PUT_C(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force)); + PUT_C(mods, "filter", DICTIONARY_OBJ(filter)); + + PUT_C(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT)); + PUT_C(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT)); + PUT_C(mods, "unsilent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_UNSILENT)); + PUT_C(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX)); + PUT_C(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOAUTOCMD)); + PUT_C(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.cmod_tab - 1)); + PUT_C(mods, "verbose", INTEGER_OBJ(cmdinfo.cmdmod.cmod_verbose - 1)); + PUT_C(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_BROWSE)); + PUT_C(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_CONFIRM)); + PUT_C(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_HIDE)); + PUT_C(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPALT)); + PUT_C(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPJUMPS)); + PUT_C(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPMARKS)); + PUT_C(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPPATTERNS)); + PUT_C(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_LOCKMARKS)); + PUT_C(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOSWAPFILE)); + PUT_C(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_VERT)); + PUT_C(mods, "horizontal", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_HOR)); + + char *split; if (cmdinfo.cmdmod.cmod_split & WSP_BOT) { split = "botright"; } else if (cmdinfo.cmdmod.cmod_split & WSP_TOP) { @@ -279,18 +268,17 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) } else { split = ""; } - PUT(mods, "split", CSTR_TO_OBJ(split)); + PUT_C(mods, "split", CSTR_AS_OBJ(split)); - PUT(result, "mods", DICTIONARY_OBJ(mods)); + PUT_KEY(result, cmd, mods, mods); - Dictionary magic = ARRAY_DICT_INIT; - PUT(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file)); - PUT(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar)); - PUT(result, "magic", DICTIONARY_OBJ(magic)); + Dictionary magic = arena_dict(arena, 2); + PUT_C(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file)); + PUT_C(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar)); + PUT_KEY(result, cmd, magic, magic); undo_cmdmod(&cmdinfo.cmdmod); end: - xfree(cmdline); return result; } @@ -317,7 +305,7 @@ end: /// - output: (boolean, default false) Whether to return command output. /// @param[out] err Error details, if any. /// @return Command output (non-error, non-shell |:!|) if `output` is true, else empty string. -String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error *err) +String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena *arena, Error *err) FUNC_API_SINCE(10) { exarg_T ea; @@ -355,7 +343,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error goto end; }); - cmdname = string_to_cstr(cmd->cmd); + cmdname = arena_string(arena, cmd->cmd).data; ea.cmd = cmdname; char *p = find_ex_command(&ea, NULL); @@ -364,9 +352,8 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error // autocommands defined, trigger the matching autocommands. if (p != NULL && ea.cmdidx == CMD_SIZE && ASCII_ISUPPER(*ea.cmd) && has_event(EVENT_CMDUNDEFINED)) { - p = xstrdup(cmdname); + p = arena_string(arena, cmd->cmd).data; int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL); - xfree(p); // If the autocommands did something and didn't cause an error, try // finding the command again. p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd; @@ -395,28 +382,31 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error if (HAS_KEY(cmd, cmd, args)) { // Process all arguments. Convert non-String arguments to String and check if String arguments // have non-whitespace characters. + args = arena_array(arena, cmd->args.size); for (size_t i = 0; i < cmd->args.size; i++) { Object elem = cmd->args.items[i]; char *data_str; switch (elem.type) { case kObjectTypeBoolean: - data_str = xcalloc(2, sizeof(char)); + data_str = arena_alloc(arena, 2, false); data_str[0] = elem.data.boolean ? '1' : '0'; data_str[1] = '\0'; + ADD_C(args, CSTR_AS_OBJ(data_str)); break; case kObjectTypeBuffer: case kObjectTypeWindow: case kObjectTypeTabpage: case kObjectTypeInteger: - data_str = xcalloc(NUMBUFLEN, sizeof(char)); + data_str = arena_alloc(arena, NUMBUFLEN, false); snprintf(data_str, NUMBUFLEN, "%" PRId64, elem.data.integer); + ADD_C(args, CSTR_AS_OBJ(data_str)); break; case kObjectTypeString: VALIDATE_EXP(!string_iswhite(elem.data.string), "command arg", "non-whitespace", NULL, { goto end; }); - data_str = string_to_cstr(elem.data.string); + ADD_C(args, elem); break; default: VALIDATE_EXP(false, "command arg", "valid type", api_typename(elem.type), { @@ -424,8 +414,6 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error }); break; } - - ADD(args, CSTR_AS_OBJ(data_str)); } bool argc_valid; @@ -526,7 +514,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error VALIDATE_MOD((!ea.forceit || (ea.argt & EX_BANG)), "bang", cmd->cmd.data); if (HAS_KEY(cmd, cmd, magic)) { - Dict(cmd_magic) magic[1] = { 0 }; + Dict(cmd_magic) magic[1] = KEYDICT_INIT; if (!api_dict_to_keydict(magic, KeyDict_cmd_magic_get_field, cmd->magic, err)) { goto end; } @@ -544,13 +532,13 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error } if (HAS_KEY(cmd, cmd, mods)) { - Dict(cmd_mods) mods[1] = { 0 }; + Dict(cmd_mods) mods[1] = KEYDICT_INIT; if (!api_dict_to_keydict(mods, KeyDict_cmd_mods_get_field, cmd->mods, err)) { goto end; } if (HAS_KEY(mods, cmd_mods, filter)) { - Dict(cmd_mods_filter) filter[1] = { 0 }; + Dict(cmd_mods_filter) filter[1] = KEYDICT_INIT; if (!api_dict_to_keydict(&filter, KeyDict_cmd_mods_filter_get_field, mods->filter, err)) { @@ -678,26 +666,20 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error } if (opts->output && capture_local.ga_len > 1) { - retv = (String){ - .data = capture_local.ga_data, - .size = (size_t)capture_local.ga_len, - }; + // TODO(bfredl): if there are more cases like this we might want custom xfree-list in arena + retv = CBUF_TO_ARENA_STR(arena, capture_local.ga_data, (size_t)capture_local.ga_len); // redir usually (except :echon) prepends a newline. if (retv.data[0] == '\n') { - memmove(retv.data, retv.data + 1, retv.size - 1); - retv.data[retv.size - 1] = '\0'; - retv.size = retv.size - 1; + retv.data++; + retv.size--; } - goto end; } clear_ga: if (opts->output) { ga_clear(&capture_local); } end: - api_free_array(args); xfree(cmdline); - xfree(cmdname); xfree(ea.args); xfree(ea.arglens); @@ -871,17 +853,17 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin /// from Lua, the command can also be a Lua function. The function is called with a /// single table argument that contains the following keys: /// - name: (string) Command name -/// - args: (string) The args passed to the command, if any |<args>| +/// - args: (string) The args passed to the command, if any [<args>] /// - fargs: (table) The args split by unescaped whitespace (when more than one -/// argument is allowed), if any |<f-args>| +/// argument is allowed), if any [<f-args>] /// - nargs: (string) Number of arguments |:command-nargs| -/// - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>| -/// - line1: (number) The starting line of the command range |<line1>| -/// - line2: (number) The final line of the command range |<line2>| -/// - range: (number) The number of items in the command range: 0, 1, or 2 |<range>| -/// - count: (number) Any count supplied |<count>| -/// - reg: (string) The optional register, if specified |<reg>| -/// - mods: (string) Command modifiers, if any |<mods>| +/// - bang: (boolean) "true" if the command was executed with a ! modifier [<bang>] +/// - line1: (number) The starting line of the command range [<line1>] +/// - line2: (number) The final line of the command range [<line2>] +/// - range: (number) The number of items in the command range: 0, 1, or 2 [<range>] +/// - count: (number) Any count supplied [<count>] +/// - reg: (string) The optional register, if specified [<reg>] +/// - mods: (string) Command modifiers, if any [<mods>] /// - smods: (table) Command modifiers in a structured format. Has the same /// structure as the "mods" key of |nvim_parse_cmd()|. /// @param opts Optional |command-attributes|. @@ -1115,7 +1097,8 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict( if (opts->complete.type == kObjectTypeLuaRef) { context = EXPAND_USER_LUA; - compl_luaref = api_new_luaref(opts->complete.data.luaref); + compl_luaref = opts->complete.data.luaref; + opts->complete.data.luaref = LUA_NOREF; } else if (opts->complete.type == kObjectTypeString) { VALIDATE_S(OK == parse_compl_arg(opts->complete.data.string.data, (int)opts->complete.data.string.size, &context, &argt, @@ -1135,7 +1118,8 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict( }); argt |= EX_PREVIEW; - preview_luaref = api_new_luaref(opts->preview.data.luaref); + preview_luaref = opts->preview.data.luaref; + opts->preview.data.luaref = LUA_NOREF; } switch (command.type) { @@ -1182,10 +1166,10 @@ err: /// @param[out] err Error details, if any. /// /// @returns Map of maps describing commands. -Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err) +Dictionary nvim_get_commands(Dict(get_commands) *opts, Arena *arena, Error *err) FUNC_API_SINCE(4) { - return nvim_buf_get_commands(-1, opts, err); + return nvim_buf_get_commands(-1, opts, arena, err); } /// Gets a map of buffer-local |user-commands|. @@ -1195,7 +1179,7 @@ Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err) /// @param[out] err Error details, if any. /// /// @returns Map of maps describing commands. -Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err) +Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Arena *arena, Error *err) FUNC_API_SINCE(4) { bool global = (buffer == -1); @@ -1208,12 +1192,12 @@ Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error api_set_error(err, kErrorTypeValidation, "builtin=true not implemented"); return (Dictionary)ARRAY_DICT_INIT; } - return commands_array(NULL); + return commands_array(NULL, arena); } buf_T *buf = find_buffer_by_handle(buffer, err); if (opts->builtin || !buf) { return (Dictionary)ARRAY_DICT_INIT; } - return commands_array(buf); + return commands_array(buf, arena); } diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 2ec11236d7..6254e9fbd8 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -6,22 +6,24 @@ #include "nvim/api/deprecated.h" #include "nvim/api/extmark.h" #include "nvim/api/keysets_defs.h" -#include "nvim/api/options.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/api/vimscript.h" #include "nvim/buffer_defs.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/extmark.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/pos_defs.h" +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/deprecated.c.generated.h" @@ -30,8 +32,8 @@ /// @deprecated Use nvim_exec2() instead. /// @see nvim_exec2 String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err) - FUNC_API_SINCE(7) - FUNC_API_DEPRECATED_SINCE(11) + FUNC_API_SINCE(7) FUNC_API_DEPRECATED_SINCE(11) + FUNC_API_RET_ALLOC { Dict(exec_opts) opts = { .output = output }; return exec_impl(channel_id, src, &opts, err); @@ -40,8 +42,8 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err) /// @deprecated /// @see nvim_exec2 String nvim_command_output(uint64_t channel_id, String command, Error *err) - FUNC_API_SINCE(1) - FUNC_API_DEPRECATED_SINCE(7) + FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(7) + FUNC_API_RET_ALLOC { Dict(exec_opts) opts = { .output = true }; return exec_impl(channel_id, command, &opts, err); @@ -49,18 +51,17 @@ String nvim_command_output(uint64_t channel_id, String command, Error *err) /// @deprecated Use nvim_exec_lua() instead. /// @see nvim_exec_lua -Object nvim_execute_lua(String code, Array args, Error *err) +Object nvim_execute_lua(String code, Array args, Arena *arena, Error *err) FUNC_API_SINCE(3) FUNC_API_DEPRECATED_SINCE(7) FUNC_API_REMOTE_ONLY { - return nlua_exec(code, args, err); + return nlua_exec(code, args, kRetObject, arena, err); } /// Gets the buffer number /// -/// @deprecated The buffer number now is equal to the object id, -/// so there is no need to use this function. +/// @deprecated The buffer number now is equal to the object id /// /// @param buffer Buffer handle, or 0 for current buffer /// @param[out] err Error details, if any @@ -98,8 +99,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start, /// Set the virtual text (annotation) for a buffer line. /// -/// @deprecated use nvim_buf_set_extmark to use full virtual text -/// functionality. +/// @deprecated use nvim_buf_set_extmark to use full virtual text functionality. /// /// The text will be placed after the buffer text. Virtual text will never /// cause reflow, rather virtual text will be truncated at the end of the screen @@ -117,7 +117,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start, /// virtual text, the allocated id is then returned. /// /// @param buffer Buffer handle, or 0 for current buffer -/// @param ns_id Namespace to use or 0 to create a namespace, +/// @param src_id Namespace to use or 0 to create a namespace, /// or -1 for a ungrouped annotation /// @param line Line to annotate with virtual text (zero-indexed) /// @param chunks A list of [text, hl_group] arrays, each representing a @@ -127,7 +127,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start, /// @param[out] err Error details, if any /// @return The ns_id that was used Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, Array chunks, - Dictionary opts, Error *err) + Dict(empty) *opts, Error *err) FUNC_API_SINCE(5) FUNC_API_DEPRECATED_SINCE(8) { @@ -141,11 +141,6 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A return 0; } - if (opts.size > 0) { - api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); - return 0; - } - uint32_t ns_id = src2ns(&src_id); int width; @@ -172,7 +167,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A DecorInline decor = { .ext = true, .data.ext.vt = vt, .data.ext.sh_idx = DECOR_ID_INVALID }; extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, 0, true, - false, false, false, NULL); + false, false, false, false, NULL); return src_id; } @@ -228,12 +223,12 @@ Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Arena *arena, Error *er /// the end of the buffer. /// @param lines Array of lines /// @param[out] err Error details, if any -void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Error *err) +void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { // "lnum" will be the index of the line after inserting, // no matter if it is negative or not - nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, err); + nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, arena, err); } /// Gets a buffer line @@ -248,20 +243,18 @@ void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Error *er /// @param index Line index /// @param[out] err Error details, if any /// @return Line string -String buffer_get_line(Buffer buffer, Integer index, Error *err) +String buffer_get_line(Buffer buffer, Integer index, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { String rv = { .size = 0 }; index = convert_index(index); - Array slice = nvim_buf_get_lines(0, buffer, index, index + 1, true, NULL, err); + Array slice = nvim_buf_get_lines(0, buffer, index, index + 1, true, arena, NULL, err); if (!ERROR_SET(err) && slice.size) { rv = slice.items[0].data.string; } - xfree(slice.items); - return rv; } @@ -277,13 +270,13 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// @param index Line index /// @param line Contents of the new line /// @param[out] err Error details, if any -void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) +void buffer_set_line(Buffer buffer, Integer index, String line, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { Object l = STRING_OBJ(line); Array array = { .items = &l, .size = 1 }; index = convert_index(index); - nvim_buf_set_lines(0, buffer, index, index + 1, true, array, err); + nvim_buf_set_lines(0, buffer, index, index + 1, true, array, arena, err); } /// Deletes a buffer line @@ -296,12 +289,12 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) /// @param buffer buffer handle /// @param index line index /// @param[out] err Error details, if any -void buffer_del_line(Buffer buffer, Integer index, Error *err) +void buffer_del_line(Buffer buffer, Integer index, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { Array array = ARRAY_DICT_INIT; index = convert_index(index); - nvim_buf_set_lines(0, buffer, index, index + 1, true, array, err); + nvim_buf_set_lines(0, buffer, index, index + 1, true, array, arena, err); } /// Retrieves a line range from the buffer @@ -322,12 +315,13 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, Integer end, Boolean include_start, Boolean include_end, + Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { start = convert_index(start) + !include_start; end = convert_index(end) + include_end; - return nvim_buf_get_lines(0, buffer, start, end, false, NULL, err); + return nvim_buf_get_lines(0, buffer, start, end, false, arena, NULL, err); } /// Replaces a line range on the buffer @@ -346,12 +340,13 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, // array will delete the line range) /// @param[out] err Error details, if any void buffer_set_line_slice(Buffer buffer, Integer start, Integer end, Boolean include_start, - Boolean include_end, ArrayOf(String) replacement, Error *err) + Boolean include_end, ArrayOf(String) replacement, Arena *arena, + Error *err) FUNC_API_DEPRECATED_SINCE(1) { start = convert_index(start) + !include_start; end = convert_index(end) + include_end; - nvim_buf_set_lines(0, buffer, start, end, false, replacement, err); + nvim_buf_set_lines(0, buffer, start, end, false, replacement, arena, err); } /// Sets a buffer-scoped (b:) variable @@ -366,7 +361,7 @@ void buffer_set_line_slice(Buffer buffer, Integer start, Integer end, Boolean in /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. -Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) +Object buffer_set_var(Buffer buffer, String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -375,7 +370,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) return NIL; } - return dict_set_var(buf->b_vars, name, value, false, true, err); + return dict_set_var(buf->b_vars, name, value, false, true, arena, err); } /// Removes a buffer-scoped (b:) variable @@ -386,7 +381,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Old value -Object buffer_del_var(Buffer buffer, String name, Error *err) +Object buffer_del_var(Buffer buffer, String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -395,7 +390,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err) return NIL; } - return dict_set_var(buf->b_vars, name, NIL, true, true, err); + return dict_set_var(buf->b_vars, name, NIL, true, true, arena, err); } /// Sets a window-scoped (w:) variable @@ -410,7 +405,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err) /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. -Object window_set_var(Window window, String name, Object value, Error *err) +Object window_set_var(Window window, String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -419,7 +414,7 @@ Object window_set_var(Window window, String name, Object value, Error *err) return NIL; } - return dict_set_var(win->w_vars, name, value, false, true, err); + return dict_set_var(win->w_vars, name, value, false, true, arena, err); } /// Removes a window-scoped (w:) variable @@ -430,7 +425,7 @@ Object window_set_var(Window window, String name, Object value, Error *err) /// @param name variable name /// @param[out] err Error details, if any /// @return Old value -Object window_del_var(Window window, String name, Error *err) +Object window_del_var(Window window, String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -439,7 +434,7 @@ Object window_del_var(Window window, String name, Error *err) return NIL; } - return dict_set_var(win->w_vars, name, NIL, true, true, err); + return dict_set_var(win->w_vars, name, NIL, true, true, arena, err); } /// Sets a tab-scoped (t:) variable @@ -454,7 +449,7 @@ Object window_del_var(Window window, String name, Error *err) /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. -Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) +Object tabpage_set_var(Tabpage tabpage, String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -463,7 +458,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) return NIL; } - return dict_set_var(tab->tp_vars, name, value, false, true, err); + return dict_set_var(tab->tp_vars, name, value, false, true, arena, err); } /// Removes a tab-scoped (t:) variable @@ -474,7 +469,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Old value -Object tabpage_del_var(Tabpage tabpage, String name, Error *err) +Object tabpage_del_var(Tabpage tabpage, String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -483,7 +478,7 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err) return NIL; } - return dict_set_var(tab->tp_vars, name, NIL, true, true, err); + return dict_set_var(tab->tp_vars, name, NIL, true, true, arena, err); } /// @deprecated @@ -491,18 +486,18 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err) /// @warning May return nil if there was no previous value /// OR if previous value was `v:null`. /// @return Old value or nil if there was no previous value. -Object vim_set_var(String name, Object value, Error *err) +Object vim_set_var(String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { - return dict_set_var(&globvardict, name, value, false, true, err); + return dict_set_var(&globvardict, name, value, false, true, arena, err); } /// @deprecated /// @see nvim_del_var -Object vim_del_var(String name, Error *err) +Object vim_del_var(String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { - return dict_set_var(&globvardict, name, NIL, true, true, err); + return dict_set_var(&globvardict, name, NIL, true, true, arena, err); } static int64_t convert_index(int64_t index) @@ -517,11 +512,11 @@ static int64_t convert_index(int64_t index) /// @param name Option name /// @param[out] err Error details, if any /// @return Option Information -Dictionary nvim_get_option_info(String name, Error *err) +Dictionary nvim_get_option_info(String name, Arena *arena, Error *err) FUNC_API_SINCE(7) FUNC_API_DEPRECATED_SINCE(11) { - return get_vimoption(name, OPT_GLOBAL, curbuf, curwin, err); + return get_vimoption(name, OPT_GLOBAL, curbuf, curwin, arena, err); } /// Sets the global value of an option. @@ -544,7 +539,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err) /// @param name Option name /// @param[out] err Error details, if any /// @return Option value (global) -Object nvim_get_option(String name, Arena *arena, Error *err) +Object nvim_get_option(String name, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { @@ -558,7 +553,7 @@ Object nvim_get_option(String name, Arena *arena, Error *err) /// @param name Option name /// @param[out] err Error details, if any /// @return Option value -Object nvim_buf_get_option(Buffer buffer, String name, Arena *arena, Error *err) +Object nvim_buf_get_option(Buffer buffer, String name, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { @@ -600,7 +595,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object /// @param name Option name /// @param[out] err Error details, if any /// @return Option value -Object nvim_win_get_option(Window window, String name, Arena *arena, Error *err) +Object nvim_win_get_option(Window window, String name, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { @@ -649,7 +644,7 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er return (Object)OBJECT_INIT; }); - OptVal value = get_option_value_strict(name.data, req_scope, from, err); + OptVal value = get_option_value_strict(find_option(name.data), req_scope, from, err); if (ERROR_SET(err)) { return (Object)OBJECT_INIT; } @@ -675,8 +670,8 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, return; }); - int flags = get_option_attrs(name.data); - VALIDATE_S(flags != 0, "option name", name.data, { + OptIndex opt_idx = find_option(name.data); + VALIDATE_S(opt_idx != kOptInvalid, "option name", name.data, { return; }); @@ -691,13 +686,14 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, return; }); + int attrs = get_option_attrs(opt_idx); // For global-win-local options -> setlocal // For win-local options -> setglobal and setlocal (opt_flags == 0) - const int opt_flags = (req_scope == kOptReqWin && !(flags & SOPT_GLOBAL)) + const int opt_flags = (req_scope == kOptReqWin && !(attrs & SOPT_GLOBAL)) ? 0 : (req_scope == kOptReqGlobal) ? OPT_GLOBAL : OPT_LOCAL; WITH_SCRIPT_CONTEXT(channel_id, { - set_option_value_for(name.data, optval, opt_flags, req_scope, to, err); + set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err); }); } diff --git a/src/nvim/api/deprecated.h b/src/nvim/api/deprecated.h index e20d8304e0..c879794bb3 100644 --- a/src/nvim/api/deprecated.h +++ b/src/nvim/api/deprecated.h @@ -2,6 +2,7 @@ #include <stdint.h> // IWYU pragma: keep +#include "nvim/api/keysets_defs.h" // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/api/dispatch_deprecated.lua b/src/nvim/api/dispatch_deprecated.lua index 5650a77ac0..7a92789f79 100644 --- a/src/nvim/api/dispatch_deprecated.lua +++ b/src/nvim/api/dispatch_deprecated.lua @@ -1,69 +1,69 @@ local deprecated_aliases = { - nvim_buf_add_highlight="buffer_add_highlight", - nvim_buf_clear_highlight="buffer_clear_highlight", - nvim_buf_get_lines="buffer_get_lines", - nvim_buf_get_mark="buffer_get_mark", - nvim_buf_get_name="buffer_get_name", - nvim_buf_get_number="buffer_get_number", - nvim_buf_get_option="buffer_get_option", - nvim_buf_get_var="buffer_get_var", - nvim_buf_is_valid="buffer_is_valid", - nvim_buf_line_count="buffer_line_count", - nvim_buf_set_lines="buffer_set_lines", - nvim_buf_set_name="buffer_set_name", - nvim_buf_set_option="buffer_set_option", - nvim_call_function="vim_call_function", - nvim_command="vim_command", - nvim_command_output="vim_command_output", - nvim_del_current_line="vim_del_current_line", - nvim_err_write="vim_err_write", - nvim_err_writeln="vim_report_error", - nvim_eval="vim_eval", - nvim_feedkeys="vim_feedkeys", - nvim_get_api_info="vim_get_api_info", - nvim_get_color_by_name="vim_name_to_color", - nvim_get_color_map="vim_get_color_map", - nvim_get_current_buf="vim_get_current_buffer", - nvim_get_current_line="vim_get_current_line", - nvim_get_current_tabpage="vim_get_current_tabpage", - nvim_get_current_win="vim_get_current_window", - nvim_get_option="vim_get_option", - nvim_get_var="vim_get_var", - nvim_get_vvar="vim_get_vvar", - nvim_input="vim_input", - nvim_list_bufs="vim_get_buffers", - nvim_list_runtime_paths="vim_list_runtime_paths", - nvim_list_tabpages="vim_get_tabpages", - nvim_list_wins="vim_get_windows", - nvim_out_write="vim_out_write", - nvim_replace_termcodes="vim_replace_termcodes", - nvim_set_current_buf="vim_set_current_buffer", - nvim_set_current_dir="vim_change_directory", - nvim_set_current_line="vim_set_current_line", - nvim_set_current_tabpage="vim_set_current_tabpage", - nvim_set_current_win="vim_set_current_window", - nvim_set_option="vim_set_option", - nvim_strwidth="vim_strwidth", - nvim_subscribe="vim_subscribe", - nvim_tabpage_get_var="tabpage_get_var", - nvim_tabpage_get_win="tabpage_get_window", - nvim_tabpage_is_valid="tabpage_is_valid", - nvim_tabpage_list_wins="tabpage_get_windows", - nvim_ui_detach="ui_detach", - nvim_ui_try_resize="ui_try_resize", - nvim_unsubscribe="vim_unsubscribe", - nvim_win_get_buf="window_get_buffer", - nvim_win_get_cursor="window_get_cursor", - nvim_win_get_height="window_get_height", - nvim_win_get_option="window_get_option", - nvim_win_get_position="window_get_position", - nvim_win_get_tabpage="window_get_tabpage", - nvim_win_get_var="window_get_var", - nvim_win_get_width="window_get_width", - nvim_win_is_valid="window_is_valid", - nvim_win_set_cursor="window_set_cursor", - nvim_win_set_height="window_set_height", - nvim_win_set_option="window_set_option", - nvim_win_set_width="window_set_width", + nvim_buf_add_highlight = 'buffer_add_highlight', + nvim_buf_clear_highlight = 'buffer_clear_highlight', + nvim_buf_get_lines = 'buffer_get_lines', + nvim_buf_get_mark = 'buffer_get_mark', + nvim_buf_get_name = 'buffer_get_name', + nvim_buf_get_number = 'buffer_get_number', + nvim_buf_get_option = 'buffer_get_option', + nvim_buf_get_var = 'buffer_get_var', + nvim_buf_is_valid = 'buffer_is_valid', + nvim_buf_line_count = 'buffer_line_count', + nvim_buf_set_lines = 'buffer_set_lines', + nvim_buf_set_name = 'buffer_set_name', + nvim_buf_set_option = 'buffer_set_option', + nvim_call_function = 'vim_call_function', + nvim_command = 'vim_command', + nvim_command_output = 'vim_command_output', + nvim_del_current_line = 'vim_del_current_line', + nvim_err_write = 'vim_err_write', + nvim_err_writeln = 'vim_report_error', + nvim_eval = 'vim_eval', + nvim_feedkeys = 'vim_feedkeys', + nvim_get_api_info = 'vim_get_api_info', + nvim_get_color_by_name = 'vim_name_to_color', + nvim_get_color_map = 'vim_get_color_map', + nvim_get_current_buf = 'vim_get_current_buffer', + nvim_get_current_line = 'vim_get_current_line', + nvim_get_current_tabpage = 'vim_get_current_tabpage', + nvim_get_current_win = 'vim_get_current_window', + nvim_get_option = 'vim_get_option', + nvim_get_var = 'vim_get_var', + nvim_get_vvar = 'vim_get_vvar', + nvim_input = 'vim_input', + nvim_list_bufs = 'vim_get_buffers', + nvim_list_runtime_paths = 'vim_list_runtime_paths', + nvim_list_tabpages = 'vim_get_tabpages', + nvim_list_wins = 'vim_get_windows', + nvim_out_write = 'vim_out_write', + nvim_replace_termcodes = 'vim_replace_termcodes', + nvim_set_current_buf = 'vim_set_current_buffer', + nvim_set_current_dir = 'vim_change_directory', + nvim_set_current_line = 'vim_set_current_line', + nvim_set_current_tabpage = 'vim_set_current_tabpage', + nvim_set_current_win = 'vim_set_current_window', + nvim_set_option = 'vim_set_option', + nvim_strwidth = 'vim_strwidth', + nvim_subscribe = 'vim_subscribe', + nvim_tabpage_get_var = 'tabpage_get_var', + nvim_tabpage_get_win = 'tabpage_get_window', + nvim_tabpage_is_valid = 'tabpage_is_valid', + nvim_tabpage_list_wins = 'tabpage_get_windows', + nvim_ui_detach = 'ui_detach', + nvim_ui_try_resize = 'ui_try_resize', + nvim_unsubscribe = 'vim_unsubscribe', + nvim_win_get_buf = 'window_get_buffer', + nvim_win_get_cursor = 'window_get_cursor', + nvim_win_get_height = 'window_get_height', + nvim_win_get_option = 'window_get_option', + nvim_win_get_position = 'window_get_position', + nvim_win_get_tabpage = 'window_get_tabpage', + nvim_win_get_var = 'window_get_var', + nvim_win_get_width = 'window_get_width', + nvim_win_is_valid = 'window_is_valid', + nvim_win_set_cursor = 'window_set_cursor', + nvim_win_set_height = 'window_set_height', + nvim_win_set_option = 'window_set_option', + nvim_win_set_width = 'window_set_width', } return deprecated_aliases diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d71498d6ed..1b03a97edb 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -14,16 +14,19 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" #include "nvim/extmark.h" -#include "nvim/func_attr.h" #include "nvim/grid.h" #include "nvim/highlight_group.h" +#include "nvim/map_defs.h" #include "nvim/marktree.h" +#include "nvim/marktree_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/move.h" #include "nvim/pos_defs.h" #include "nvim/sign.h" @@ -40,7 +43,7 @@ void api_extmark_free_all_mem(void) map_destroy(String, &namespace_ids); } -/// Creates a new namespace or gets an existing one. \*namespace\* +/// Creates a new namespace or gets an existing one. [namespace]() /// /// Namespaces are used for buffer highlights and virtual text, see /// |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. @@ -69,15 +72,15 @@ Integer nvim_create_namespace(String name) /// Gets existing, non-anonymous |namespace|s. /// /// @return dict that maps from names to namespace ids. -Dictionary nvim_get_namespaces(void) +Dictionary nvim_get_namespaces(Arena *arena) FUNC_API_SINCE(5) { - Dictionary retval = ARRAY_DICT_INIT; + Dictionary retval = arena_dict(arena, map_size(&namespace_ids)); String name; handle_T id; map_foreach(&namespace_ids, name, id, { - PUT(retval, name.data, INTEGER_OBJ(id)); + PUT_C(retval, name.data, INTEGER_OBJ(id)); }) return retval; @@ -104,73 +107,85 @@ bool ns_initialized(uint32_t ns) return ns < (uint32_t)next_namespace_id; } -Array virt_text_to_array(VirtText vt, bool hl_name) +Array virt_text_to_array(VirtText vt, bool hl_name, Arena *arena) { - Array chunks = ARRAY_DICT_INIT; - Array hl_array = ARRAY_DICT_INIT; + Array chunks = arena_array(arena, kv_size(vt)); for (size_t i = 0; i < kv_size(vt); i++) { - char *text = kv_A(vt, i).text; - int hl_id = kv_A(vt, i).hl_id; - if (text == NULL) { + size_t j = i; + for (; j < kv_size(vt); j++) { + if (kv_A(vt, j).text != NULL) { + break; + } + } + + Array hl_array = arena_array(arena, i < j ? j - i + 1 : 0); + for (; i < j; i++) { + int hl_id = kv_A(vt, i).hl_id; if (hl_id > 0) { - ADD(hl_array, hl_group_name(hl_id, hl_name)); + ADD_C(hl_array, hl_group_name(hl_id, hl_name)); } - continue; } - Array chunk = ARRAY_DICT_INIT; - ADD(chunk, CSTR_TO_OBJ(text)); + + char *text = kv_A(vt, i).text; + int hl_id = kv_A(vt, i).hl_id; + Array chunk = arena_array(arena, 2); + ADD_C(chunk, CSTR_AS_OBJ(text)); if (hl_array.size > 0) { if (hl_id > 0) { - ADD(hl_array, hl_group_name(hl_id, hl_name)); + ADD_C(hl_array, hl_group_name(hl_id, hl_name)); } - ADD(chunk, ARRAY_OBJ(hl_array)); - hl_array = (Array)ARRAY_DICT_INIT; + ADD_C(chunk, ARRAY_OBJ(hl_array)); } else if (hl_id > 0) { - ADD(chunk, hl_group_name(hl_id, hl_name)); + ADD_C(chunk, hl_group_name(hl_id, hl_name)); } - ADD(chunks, ARRAY_OBJ(chunk)); + ADD_C(chunks, ARRAY_OBJ(chunk)); } - assert(hl_array.size == 0); return chunks; } -static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_name) +static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_name, Arena *arena) { MTKey start = extmark.start; - Array rv = ARRAY_DICT_INIT; + Array rv = arena_array(arena, 4); if (id) { - ADD(rv, INTEGER_OBJ((Integer)start.id)); + ADD_C(rv, INTEGER_OBJ((Integer)start.id)); } - ADD(rv, INTEGER_OBJ(start.pos.row)); - ADD(rv, INTEGER_OBJ(start.pos.col)); + ADD_C(rv, INTEGER_OBJ(start.pos.row)); + ADD_C(rv, INTEGER_OBJ(start.pos.col)); if (add_dict) { - Dictionary dict = ARRAY_DICT_INIT; + // TODO(bfredl): coding the size like this is a bit fragile. + // We want ArrayOf(Dict(set_extmark)) as the return type.. + Dictionary dict = arena_dict(arena, ARRAY_SIZE(set_extmark_table)); - PUT(dict, "ns_id", INTEGER_OBJ((Integer)start.ns)); + PUT_C(dict, "ns_id", INTEGER_OBJ((Integer)start.ns)); - PUT(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start))); + PUT_C(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start))); - if (extmark.end_pos.row >= 0) { - PUT(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row)); - PUT(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col)); - PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity)); + if (mt_paired(start)) { + PUT_C(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row)); + PUT_C(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col)); + PUT_C(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity)); } if (mt_no_undo(start)) { - PUT(dict, "undo_restore", BOOLEAN_OBJ(false)); + PUT_C(dict, "undo_restore", BOOLEAN_OBJ(false)); } if (mt_invalidate(start)) { - PUT(dict, "invalidate", BOOLEAN_OBJ(true)); + PUT_C(dict, "invalidate", BOOLEAN_OBJ(true)); } if (mt_invalid(start)) { - PUT(dict, "invalid", BOOLEAN_OBJ(true)); + PUT_C(dict, "invalid", BOOLEAN_OBJ(true)); + } + + if (mt_scoped(start)) { + PUT_C(dict, "scoped", BOOLEAN_OBJ(true)); } - decor_to_dict_legacy(&dict, mt_decor(start), hl_name); + decor_to_dict_legacy(&dict, mt_decor(start), hl_name, arena); - ADD(rv, DICTIONARY_OBJ(dict)); + ADD_C(rv, DICTIONARY_OBJ(dict)); } return rv; @@ -188,8 +203,8 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na /// @return 0-indexed (row, col) tuple or empty list () if extmark id was /// absent ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, - Integer id, Dictionary opts, - Error *err) + Integer id, Dict(get_extmark) *opts, + Arena *arena, Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; @@ -204,37 +219,19 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, return rv; }); - bool details = false; - bool hl_name = true; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - if (strequal("details", k.data)) { - details = api_object_to_bool(*v, "details", false, err); - if (ERROR_SET(err)) { - return rv; - } - } else if (strequal("hl_name", k.data)) { - hl_name = api_object_to_bool(*v, "hl_name", false, err); - if (ERROR_SET(err)) { - return rv; - } - } else { - VALIDATE_S(false, "'opts' key", k.data, { - return rv; - }); - } - } + bool details = opts->details; + + bool hl_name = GET_BOOL_OR_TRUE(opts, get_extmark, hl_name); MTPair extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id); if (extmark.start.pos.row < 0) { return rv; } - return extmark_to_array(extmark, false, details, hl_name); + return extmark_to_array(extmark, false, details, hl_name, arena); } -/// Gets |extmarks| (including |signs|) in "traversal order" from a |charwise| -/// region defined by buffer positions (inclusive, 0-indexed |api-indexing|). +/// Gets |extmarks| in "traversal order" from a |charwise| region defined by +/// buffer positions (inclusive, 0-indexed |api-indexing|). /// /// Region can be given as (row,col) tuples, or valid extmark ids (whose /// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1) @@ -252,6 +249,10 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// the `overlap` option might be useful. Otherwise only the start position /// of an extmark will be considered. /// +/// Note: legacy signs placed through the |:sign| commands are implemented +/// as extmarks and will show up here. Their details array will contain a +/// `sign_name` field. +/// /// Example: /// /// ```lua @@ -283,9 +284,9 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// their start position is less than `start` /// - type: Filter marks by type: "highlight", "sign", "virt_text" and "virt_lines" /// @param[out] err Error details, if any -/// @return List of [extmark_id, row, col] tuples in "traversal order". +/// @return List of `[extmark_id, row, col]` tuples in "traversal order". Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object end, - Dict(get_extmarks) *opts, Error *err) + Dict(get_extmarks) *opts, Arena *arena, Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; @@ -349,8 +350,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col, u_row, u_col, (int64_t)limit, reverse, type, opts->overlap); + rv = arena_array(arena, kv_size(marks)); for (size_t i = 0; i < kv_size(marks); i++) { - ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name))); + ADD_C(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena))); } kv_destroy(marks); @@ -388,7 +390,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// of the screen line (just like for diff and /// cursorline highlight). /// - virt_text : virtual text to link to this mark. -/// A list of [text, highlight] tuples, each representing a +/// A list of `[text, highlight]` tuples, each representing a /// text chunk with specified highlight. `highlight` element /// can either be a single highlight group, or an array of /// multiple highlight groups that will be stacked @@ -410,6 +412,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// text is selected or hidden because of /// scrolling with 'nowrap' or 'smoothscroll'. /// Currently only affects "overlay" virt_text. +/// - virt_text_repeat_linebreak : repeat the virtual text on +/// wrapped lines. /// - hl_mode : control how highlights are combined with the /// highlights of the text. Currently only affects /// virt_text highlights, but might affect `hl_group` @@ -421,7 +425,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// /// - virt_lines : virtual lines to add next to this mark /// This should be an array over lines, where each line in -/// turn is an array over [text, highlight] tuples. In +/// turn is an array over `[text, highlight]` tuples. In /// general, buffer and window options do not affect the /// display of the text. In particular 'wrap' /// and 'linebreak' options do not take effect, so @@ -450,35 +454,28 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// if text around the mark was deleted and then restored by undo. /// Defaults to true. /// - invalidate : boolean that indicates whether to hide the -/// extmark if the entirety of its range is deleted. If +/// extmark if the entirety of its range is deleted. For +/// hidden marks, an "invalid" key is added to the "details" +/// array of |nvim_buf_get_extmarks()| and family. If /// "undo_restore" is false, the extmark is deleted instead. -/// - priority: a priority value for the highlight group or sign -/// attribute. For example treesitter highlighting uses a -/// value of 100. +/// - priority: a priority value for the highlight group, sign +/// attribute or virtual text. For virtual text, item with +/// highest priority is drawn last. For example treesitter +/// highlighting uses a value of 100. /// - strict: boolean that indicates extmark should not be placed /// if the line or column value is past the end of the /// buffer or end of the line respectively. Defaults to true. /// - sign_text: string of length 1-2 used to display in the /// sign column. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row /// - sign_hl_group: name of the highlight group used to /// highlight the sign column text. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row /// - number_hl_group: name of the highlight group used to /// highlight the number column. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row /// - line_hl_group: name of the highlight group used to /// highlight the whole line. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row /// - cursorline_hl_group: name of the highlight group used to -/// highlight the line when the cursor is on the same line -/// as the mark and 'cursorline' is enabled. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row +/// highlight the sign column text when the cursor is on +/// the same line as the mark and 'cursorline' is enabled. /// - conceal: string which should be either empty or a single /// character. Enable concealing similar to |:syn-conceal|. /// When a character is supplied it is used as |:syn-cchar|. @@ -490,6 +487,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// by a UI. When set, the UI will receive win_extmark events. /// Note: the mark is positioned by virt_text attributes. Can be /// used together with virt_text. +/// - url: A URL to associate with this extmark. In the TUI, the OSC 8 control +/// sequence is used to generate a clickable hyperlink to this URL. +/// - scoped: boolean that indicates that the extmark should only be +/// displayed in the namespace scope. (experimental) /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -503,6 +504,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT; DecorVirtText virt_text = DECOR_VIRT_TEXT_INIT; DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT; + char *url = NULL; bool has_hl = false; buf_T *buf = find_buffer_by_handle(buffer, err); @@ -556,36 +558,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = (int)val; } - // uncrustify:off + hl.hl_id = (int)opts->hl_group; + has_hl = hl.hl_id > 0; + sign.hl_id = (int)opts->sign_hl_group; + sign.cursorline_hl_id = (int)opts->cursorline_hl_group; + sign.number_hl_id = (int)opts->number_hl_group; + sign.line_hl_id = (int)opts->line_hl_group; - // TODO(bfredl): keyset type alias for hl_group? (nil|int|string) - struct { - const char *name; - Object *opt; - int *dest; - } hls[] = { - { "hl_group" , &opts->hl_group , &hl.hl_id }, - { "sign_hl_group" , &opts->sign_hl_group , &sign.hl_id }, - { "number_hl_group" , &opts->number_hl_group , &sign.number_hl_id }, - { "line_hl_group" , &opts->line_hl_group , &sign.line_hl_id }, - { "cursorline_hl_group", &opts->cursorline_hl_group, &sign.cursorline_hl_id }, - { NULL, NULL, NULL }, - }; - - // uncrustify:on - - for (int j = 0; hls[j].name && hls[j].dest; j++) { - if (hls[j].opt->type != kObjectTypeNil) { - if (j > 0) { - sign.flags |= kSHIsSign; - } else { - has_hl = true; - } - *hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err); - if (ERROR_SET(err)) { - goto error; - } - } + if (sign.hl_id || sign.cursorline_hl_id || sign.number_hl_id || sign.line_hl_id) { + sign.flags |= kSHIsSign; } if (HAS_KEY(opts, set_extmark, conceal)) { @@ -632,7 +613,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } hl.flags |= opts->hl_eol ? kSHHlEol : 0; - virt_text.flags |= opts->virt_text_hide ? kVTHide : 0; + virt_text.flags |= ((opts->virt_text_hide ? kVTHide : 0) + | (opts->virt_text_repeat_linebreak ? kVTRepeatLinebreak : 0)); if (HAS_KEY(opts, set_extmark, hl_mode)) { String str = opts->hl_mode; @@ -684,9 +666,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } if (HAS_KEY(opts, set_extmark, sign_text)) { - sign.text.ptr = NULL; - VALIDATE_S(init_sign_text(NULL, &sign.text.ptr, opts->sign_text.data), - "sign_text", "", { + sign.text[0] = 0; + VALIDATE_S(init_sign_text(NULL, sign.text, opts->sign_text.data), "sign_text", "", { goto error; }); sign.flags |= kSHIsSign; @@ -708,6 +689,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer has_hl = true; } + if (HAS_KEY(opts, set_extmark, url)) { + url = string_to_cstr(opts->url); + } + if (opts->ui_watched) { hl.flags |= kSHUIWatched; if (virt_text.pos == kVPosOverlay) { @@ -764,6 +749,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) { + if (opts->scoped) { + api_set_error(err, kErrorTypeException, "not yet implemented"); + goto error; + } + int r = (int)line; int c = (int)col; if (line2 == -1) { @@ -771,15 +761,32 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = c; } + DecorPriority subpriority = DECOR_PRIORITY_BASE; + if (HAS_KEY(opts, set_extmark, _subpriority)) { + VALIDATE_RANGE((opts->_subpriority >= 0 && opts->_subpriority <= UINT16_MAX), + "_subpriority", { + goto error; + }); + subpriority = (DecorPriority)opts->_subpriority; + } + if (kv_size(virt_text.data.virt_text)) { - decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true); + decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true, + subpriority); } if (kv_size(virt_lines.data.virt_lines)) { - decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true); + decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true, + subpriority); + } + if (url != NULL) { + DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT; + sh.url = url; + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, 0, 0, subpriority); } if (has_hl) { DecorSignHighlight sh = decor_sh_from_inline(hl); - decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id, + subpriority); } } else { if (opts->ephemeral) { @@ -802,9 +809,16 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } uint32_t decor_indexed = DECOR_ID_INVALID; + if (url != NULL) { + DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT; + sh.url = url; + sh.next = decor_indexed; + decor_indexed = decor_put_sh(sh); + } if (sign.flags & kSHIsSign) { + sign.next = decor_indexed; decor_indexed = decor_put_sh(sign); - if (sign.text.ptr != NULL) { + if (sign.text[0]) { decor_flags |= MT_FLAG_DECOR_SIGNTEXT; } if (sign.number_hl_id || sign.line_hl_id || sign.cursorline_hl_id) { @@ -832,7 +846,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2, decor, decor_flags, right_gravity, opts->end_right_gravity, !GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore), - opts->invalidate, err); + opts->invalidate, opts->scoped, err); if (ERROR_SET(err)) { decor_free(decor); return 0; @@ -844,6 +858,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer error: clear_virttext(&virt_text.data.virt_text); clear_virtlines(&virt_lines.data.virt_lines); + if (url != NULL) { + xfree(url); + } + return 0; } @@ -954,7 +972,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In decor.data.hl.hl_id = hl_id; extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, - decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL); + decor, MT_FLAG_DECOR_HL, true, false, false, false, false, NULL); return ns_id; } @@ -1022,19 +1040,27 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// @param ns_id Namespace id from |nvim_create_namespace()| /// @param opts Table of callbacks: /// - on_start: called first on each screen redraw +/// ``` /// ["start", tick] +/// ``` /// - on_buf: called for each buffer being redrawn (before -/// window callbacks) +/// window callbacks) +/// ``` /// ["buf", bufnr, tick] -/// - on_win: called when starting to redraw a -/// specific window. botline_guess is an approximation -/// that does not exceed the last line number. -/// ["win", winid, bufnr, topline, botline_guess] +/// ``` +/// - on_win: called when starting to redraw a specific window. +/// ``` +/// ["win", winid, bufnr, topline, botline] +/// ``` /// - on_line: called for each buffer line being redrawn. /// (The interaction with fold lines is subject to change) -/// ["win", winid, bufnr, row] +/// ``` +/// ["line", winid, bufnr, row] +/// ``` /// - on_end: called at the end of a redraw cycle +/// ``` /// ["end", tick] +/// ``` void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *opts, Error *err) FUNC_API_SINCE(7) FUNC_API_LUA_ONLY { @@ -1070,7 +1096,7 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) * *v = LUA_NOREF; } - p->active = true; + p->state = kDecorProviderActive; p->hl_valid++; p->hl_cached = false; } @@ -1189,8 +1215,9 @@ free_exit: return virt_text; } +/// @nodoc String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error *err) - FUNC_API_SINCE(7) + FUNC_API_SINCE(7) FUNC_API_RET_ALLOC { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -1199,3 +1226,72 @@ String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error return mt_inspect(buf->b_marktree, keys, dot); } + +/// Adds the namespace scope to the window. +/// +/// @param window Window handle, or 0 for current window +/// @param ns_id the namespace to add +/// @return true if the namespace was added, else false +Boolean nvim_win_add_ns(Window window, Integer ns_id, Error *err) + FUNC_API_SINCE(12) +{ + win_T *win = find_window_by_handle(window, err); + if (!win) { + return false; + } + + VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, { + return false; + }); + + set_put(uint32_t, &win->w_ns_set, (uint32_t)ns_id); + + changed_window_setting_win(win); + + return true; +} + +/// Gets all the namespaces scopes associated with a window. +/// +/// @param window Window handle, or 0 for current window +/// @return a list of namespaces ids +ArrayOf(Integer) nvim_win_get_ns(Window window, Arena *arena, Error *err) + FUNC_API_SINCE(12) +{ + win_T *win = find_window_by_handle(window, err); + if (!win) { + return (Array)ARRAY_DICT_INIT; + } + + Array rv = arena_array(arena, set_size(&win->w_ns_set)); + uint32_t i; + set_foreach(&win->w_ns_set, i, { + ADD_C(rv, INTEGER_OBJ((Integer)(i))); + }); + + return rv; +} + +/// Removes the namespace scope from the window. +/// +/// @param window Window handle, or 0 for current window +/// @param ns_id the namespace to remove +/// @return true if the namespace was removed, else false +Boolean nvim_win_remove_ns(Window window, Integer ns_id, Error *err) + FUNC_API_SINCE(12) +{ + win_T *win = find_window_by_handle(window, err); + if (!win) { + return false; + } + + if (!set_has(uint32_t, &win->w_ns_set, (uint32_t)ns_id)) { + return false; + } + + set_del(uint32_t, &win->w_ns_set, (uint32_t)ns_id); + + changed_window_setting_win(win); + + return true; +} diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index e59eda5686..fe91d9760d 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -3,6 +3,10 @@ #include "nvim/api/private/defs.h" typedef struct { + OptionalKeys is_set__empty_; +} Dict(empty); + +typedef struct { OptionalKeys is_set__context_; Array types; } Dict(context); @@ -24,11 +28,12 @@ typedef struct { Integer end_line; Integer end_row; Integer end_col; - Object hl_group; + HLGroupID hl_group; Array virt_text; String virt_text_pos; Integer virt_text_win_col; Boolean virt_text_hide; + Boolean virt_text_repeat_linebreak; Boolean hl_eol; String hl_mode; Boolean invalidate; @@ -41,17 +46,27 @@ typedef struct { Boolean virt_lines_leftcol; Boolean strict; String sign_text; - Object sign_hl_group; - Object number_hl_group; - Object line_hl_group; - Object cursorline_hl_group; + HLGroupID sign_hl_group; + HLGroupID number_hl_group; + HLGroupID line_hl_group; + HLGroupID cursorline_hl_group; String conceal; Boolean spell; Boolean ui_watched; Boolean undo_restore; + String url; + Boolean scoped; + + Integer _subpriority; } Dict(set_extmark); typedef struct { + OptionalKeys is_set__get_extmark_; + Boolean details; + Boolean hl_name; +} Dict(get_extmark); + +typedef struct { OptionalKeys is_set__get_extmarks_; Integer limit; Boolean details; @@ -94,17 +109,19 @@ typedef struct { } Dict(user_command); typedef struct { - OptionalKeys is_set__float_config_; + OptionalKeys is_set__win_config_; Float row; Float col; Integer width; Integer height; String anchor; String relative; + String split; Window win; Array bufpos; Boolean external; Boolean focusable; + Boolean vertical; Integer zindex; Object border; Object title; @@ -115,7 +132,7 @@ typedef struct { Boolean noautocmd; Boolean fixed; Boolean hide; -} Dict(float_config); +} Dict(win_config); typedef struct { Boolean is_lua; @@ -172,6 +189,7 @@ typedef struct { Boolean fg_indexed; Boolean bg_indexed; Boolean force; + String url; } Dict(highlight); typedef struct { @@ -313,3 +331,47 @@ typedef struct { typedef struct { Boolean output; } Dict(exec_opts); + +typedef struct { + OptionalKeys is_set__buf_attach_; + LuaRef on_lines; + LuaRef on_bytes; + LuaRef on_changedtick; + LuaRef on_detach; + LuaRef on_reload; + Boolean utf_sizes; + Boolean preview; +} Dict(buf_attach); + +typedef struct { + OptionalKeys is_set__buf_delete_; + Boolean force; + Boolean unload; +} Dict(buf_delete); + +typedef struct { + OptionalKeys is_set__open_term_; + LuaRef on_input; + Boolean force_crlf; +} Dict(open_term); + +typedef struct { + OptionalKeys is_set__complete_set_; + String info; +} Dict(complete_set); + +typedef struct { + OptionalKeys is_set__xdl_diff_; + LuaRef on_hunk; + String result_type; + String algorithm; + Integer ctxlen; + Integer interhunkctxlen; + Object linematch; + Boolean ignore_whitespace; + Boolean ignore_whitespace_change; + Boolean ignore_whitespace_change_at_eol; + Boolean ignore_cr_at_eol; + Boolean ignore_blank_lines; + Boolean indent_heuristic; +} Dict(xdl_diff); diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index c012a69c7b..d9bc0ccc92 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -9,24 +9,22 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" -#include "nvim/eval/window.h" -#include "nvim/func_attr.h" +#include "nvim/buffer_defs.h" #include "nvim/globals.h" -#include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/option.h" -#include "nvim/option_vars.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" -#include "nvim/window.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/options.c.generated.h" #endif -static int validate_option_value_args(Dict(option) *opts, char *name, int *scope, - OptReqScope *req_scope, void **from, char **filetype, - Error *err) +static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp, + int *scope, OptReqScope *req_scope, void **from, + char **filetype, Error *err) { #define HAS_KEY_X(d, v) HAS_KEY(d, option, v) if (HAS_KEY_X(opts, scope)) { @@ -80,7 +78,8 @@ static int validate_option_value_args(Dict(option) *opts, char *name, int *scope return FAIL; }); - int flags = get_option_attrs(name); + *opt_idxp = find_option(name); + int flags = get_option_attrs(*opt_idxp); if (flags == 0) { // hidden or unknown option api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name); @@ -120,10 +119,10 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err) aucmd_prepbuf(aco, ftbuf); TRY_WRAP(err, { - set_option_value("bufhidden", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); - set_option_value("buftype", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); - set_option_value("swapfile", BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value("modeline", BOOLEAN_OPTVAL(false), OPT_LOCAL); // 'nomodeline' + set_option_value(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); + set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); + set_option_value(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value(kOptModeline, BOOLEAN_OPTVAL(false), OPT_LOCAL); // 'nomodeline' ftbuf->b_p_ft = xstrdup(filetype); do_filetype_autocmd(ftbuf, false); @@ -151,25 +150,24 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err) /// @param[out] err Error details, if any /// @return Option value Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) - FUNC_API_SINCE(9) + FUNC_API_SINCE(9) FUNC_API_RET_ALLOC { - Object rv = OBJECT_INIT; - OptVal value = NIL_OPTVAL; - + OptIndex opt_idx = 0; int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *from = NULL; char *filetype = NULL; - if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &from, &filetype, err)) { - goto err; + if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, &filetype, + err)) { + return (Object)OBJECT_INIT; } aco_save_T aco; buf_T *ftbuf = do_ft_buf(filetype, &aco, err); if (ERROR_SET(err)) { - goto err; + return (Object)OBJECT_INIT; } if (ftbuf != NULL) { @@ -177,8 +175,8 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) from = ftbuf; } - bool hidden; - value = get_option_value_for(name.data, NULL, scope, &hidden, req_scope, from, err); + OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err); + bool hidden = is_option_hidden(opt_idx); if (ftbuf != NULL) { // restore curwin/curbuf and a few other things @@ -199,7 +197,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) return optval_as_object(value); err: optval_free(value); - return rv; + return (Object)OBJECT_INIT; } /// Sets the value of an option. The behavior of this function matches that of @@ -220,10 +218,11 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( Error *err) FUNC_API_SINCE(9) { + OptIndex opt_idx = 0; int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *to = NULL; - if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &to, NULL, err)) { + if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) { return; } @@ -234,7 +233,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( // // Then force scope to local since we don't want to change the global option if (req_scope == kOptReqWin && scope == 0) { - int flags = get_option_attrs(name.data); + int flags = get_option_attrs(opt_idx); if (flags & SOPT_GLOBAL) { scope = OPT_LOCAL; } @@ -252,7 +251,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( }); WITH_SCRIPT_CONTEXT(channel_id, { - set_option_value_for(name.data, optval, scope, req_scope, to, err); + set_option_value_for(name.data, opt_idx, optval, scope, req_scope, to, err); }); } @@ -264,30 +263,30 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( /// @see |nvim_get_commands()| /// /// @return dictionary of all options -Dictionary nvim_get_all_options_info(Error *err) +Dictionary nvim_get_all_options_info(Arena *arena, Error *err) FUNC_API_SINCE(7) { - return get_all_vimoptions(); + return get_all_vimoptions(arena); } /// Gets the option information for one option from arbitrary buffer or window /// /// Resulting dictionary has keys: -/// - name: Name of the option (like 'filetype') -/// - shortname: Shortened name of the option (like 'ft') -/// - type: type of option ("string", "number" or "boolean") -/// - default: The default value for the option -/// - was_set: Whether the option was set. +/// - name: Name of the option (like 'filetype') +/// - shortname: Shortened name of the option (like 'ft') +/// - type: type of option ("string", "number" or "boolean") +/// - default: The default value for the option +/// - was_set: Whether the option was set. /// -/// - last_set_sid: Last set script id (if any) -/// - last_set_linenr: line number where option was set -/// - last_set_chan: Channel where option was set (0 for local) +/// - last_set_sid: Last set script id (if any) +/// - last_set_linenr: line number where option was set +/// - last_set_chan: Channel where option was set (0 for local) /// -/// - scope: one of "global", "win", or "buf" -/// - global_local: whether win or buf option has a global value +/// - scope: one of "global", "win", or "buf" +/// - global_local: whether win or buf option has a global value /// -/// - commalist: List of comma separated values -/// - flaglist: List of single char flags +/// - commalist: List of comma separated values +/// - flaglist: List of single char flags /// /// When {scope} is not provided, the last set information applies to the local /// value in the current buffer or window if it is available, otherwise the @@ -303,275 +302,20 @@ Dictionary nvim_get_all_options_info(Error *err) /// Implies {scope} is "local". /// @param[out] err Error details, if any /// @return Option Information -Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err) +Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error *err) FUNC_API_SINCE(11) { + OptIndex opt_idx = 0; int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *from = NULL; - if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &from, NULL, err)) { + if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL, + err)) { return (Dictionary)ARRAY_DICT_INIT; } buf_T *buf = (req_scope == kOptReqBuf) ? (buf_T *)from : curbuf; win_T *win = (req_scope == kOptReqWin) ? (win_T *)from : curwin; - return get_vimoption(name, scope, buf, win, err); -} - -/// Switch current context to get/set option value for window/buffer. -/// -/// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// @param[in] from Target buffer/window. -/// @param[out] err Error message, if any. -/// -/// @return true if context was switched, false otherwise. -static bool switch_option_context(void *const ctx, OptReqScope req_scope, void *const from, - Error *err) -{ - switch (req_scope) { - case kOptReqWin: { - win_T *const win = (win_T *)from; - switchwin_T *const switchwin = (switchwin_T *)ctx; - - if (win == curwin) { - return false; - } - - if (switch_win_noblock(switchwin, win, win_find_tabpage(win), true) - == FAIL) { - restore_win_noblock(switchwin, true); - - if (try_end(err)) { - return false; - } - api_set_error(err, kErrorTypeException, "Problem while switching windows"); - return false; - } - return true; - } - case kOptReqBuf: { - buf_T *const buf = (buf_T *)from; - aco_save_T *const aco = (aco_save_T *)ctx; - - if (buf == curbuf) { - return false; - } - aucmd_prepbuf(aco, buf); - return true; - } - case kOptReqGlobal: - return false; - } - UNREACHABLE; -} - -/// Restore context after getting/setting option for window/buffer. See switch_option_context() for -/// params. -static void restore_option_context(void *const ctx, OptReqScope req_scope) -{ - switch (req_scope) { - case kOptReqWin: - restore_win_noblock((switchwin_T *)ctx, true); - break; - case kOptReqBuf: - aucmd_restbuf((aco_save_T *)ctx); - break; - case kOptReqGlobal: - break; - } -} - -/// Get attributes for an option. -/// -/// @param name Option name. -/// -/// @return Option attributes. -/// 0 for hidden or unknown option. -/// See SOPT_* in option_defs.h for other flags. -int get_option_attrs(char *name) -{ - int opt_idx = findoption(name); - - if (opt_idx < 0) { - return 0; - } - - vimoption_T *opt = get_option(opt_idx); - - if (is_tty_option(opt->fullname)) { - return SOPT_STRING | SOPT_GLOBAL; - } - - // Hidden option - if (opt->var == NULL) { - return 0; - } - - int attrs = 0; - - if (opt->flags & P_BOOL) { - attrs |= SOPT_BOOL; - } else if (opt->flags & P_NUM) { - attrs |= SOPT_NUM; - } else if (opt->flags & P_STRING) { - attrs |= SOPT_STRING; - } - - if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) { - attrs |= SOPT_GLOBAL; - } - if (opt->indir & PV_WIN) { - attrs |= SOPT_WIN; - } else if (opt->indir & PV_BUF) { - attrs |= SOPT_BUF; - } - - return attrs; -} - -/// Check if option has a value in the requested scope. -/// -/// @param name Option name. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// -/// @return true if option has a value in the requested scope, false otherwise. -static bool option_has_scope(char *name, OptReqScope req_scope) -{ - int opt_idx = findoption(name); - - if (opt_idx < 0) { - return false; - } - - vimoption_T *opt = get_option(opt_idx); - - // Hidden option. - if (opt->var == NULL) { - return false; - } - // TTY option. - if (is_tty_option(opt->fullname)) { - return req_scope == kOptReqGlobal; - } - - switch (req_scope) { - case kOptReqGlobal: - return opt->var != VAR_WIN; - case kOptReqBuf: - return opt->indir & PV_BUF; - case kOptReqWin: - return opt->indir & PV_WIN; - } - UNREACHABLE; -} - -/// Get the option value in the requested scope. -/// -/// @param name Option name. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// @param[in] from Pointer to buffer or window for local option value. -/// @param[out] err Error message, if any. -/// -/// @return Option value in the requested scope. Returns a Nil option value if option is not found, -/// hidden or if it isn't present in the requested scope. (i.e. has no global, window-local or -/// buffer-local value depending on opt_scope). -OptVal get_option_value_strict(char *name, OptReqScope req_scope, void *from, Error *err) -{ - OptVal retv = NIL_OPTVAL; - - if (!option_has_scope(name, req_scope)) { - return retv; - } - if (get_tty_option(name, &retv.data.string.data)) { - retv.type = kOptValTypeString; - return retv; - } - - int opt_idx = findoption(name); - assert(opt_idx != 0); // option_has_scope() already verifies if option name is valid. - - vimoption_T *opt = get_option(opt_idx); - switchwin_T switchwin; - aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); - bool switched = switch_option_context(ctx, req_scope, from, err); - if (ERROR_SET(err)) { - return retv; - } - - char *varp = get_varp_scope(opt, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL); - retv = optval_from_varp(opt_idx, varp); - - if (switched) { - restore_option_context(ctx, req_scope); - } - - return retv; -} - -/// Get option value for buffer / window. -/// -/// @param[in] name Option name. -/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL). -/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). -/// @param[out] hidden Whether option is hidden. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// @param[in] from Target buffer/window. -/// @param[out] err Error message, if any. -/// -/// @return Option value. Must be freed by caller. -OptVal get_option_value_for(const char *const name, uint32_t *flagsp, int scope, bool *hidden, - const OptReqScope req_scope, void *const from, Error *err) -{ - switchwin_T switchwin; - aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); - - bool switched = switch_option_context(ctx, req_scope, from, err); - if (ERROR_SET(err)) { - return NIL_OPTVAL; - } - - OptVal retv = get_option_value(name, flagsp, scope, hidden); - - if (switched) { - restore_option_context(ctx, req_scope); - } - - return retv; -} - -/// Set option value for buffer / window. -/// -/// @param[in] name Option name. -/// @param[in] value Option value. -/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// @param[in] from Target buffer/window. -/// @param[out] err Error message, if any. -void set_option_value_for(const char *const name, OptVal value, const int opt_flags, - const OptReqScope req_scope, void *const from, Error *err) -{ - switchwin_T switchwin; - aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); - - bool switched = switch_option_context(ctx, req_scope, from, err); - if (ERROR_SET(err)) { - return; - } - - const char *const errmsg = set_option_value(name, value, opt_flags); - if (errmsg) { - api_set_error(err, kErrorTypeException, "%s", errmsg); - } - - if (switched) { - restore_option_context(ctx, req_scope); - } + return get_vimoption(name, scope, buf, win, arena, err); } diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 90023171e5..a70ef1e50b 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -11,7 +11,6 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" -#include "nvim/func_attr.h" #include "nvim/lua/executor.h" #include "nvim/memory.h" #include "nvim/types_defs.h" @@ -20,6 +19,8 @@ /// Helper structure for vim_to_object typedef struct { kvec_withinit_t(Object, 2) stack; ///< Object stack. + Arena *arena; ///< arena where objects will be allocated + bool reuse_strdata; } EncodedData; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -42,12 +43,21 @@ typedef struct { #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ kvi_push(edata->stack, FLOAT_OBJ((Float)(flt))) +static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t len) +{ + if (edata->reuse_strdata) { + return STRING_OBJ(cbuf_as_string((char *)(len ? data : ""), len)); + } else { + return CBUF_TO_ARENA_OBJ(edata->arena, data, len); + } +} + #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ do { \ const size_t len_ = (size_t)(len); \ const char *const str_ = (str); \ assert(len_ == 0 || str_ != NULL); \ - kvi_push(edata->stack, STRING_OBJ(cbuf_to_string((len_ ? str_ : ""), len_))); \ + kvi_push(edata->stack, typval_cbuf_to_obj(edata, str_, len_)); \ } while (0) #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING @@ -59,10 +69,7 @@ typedef struct { do { \ const size_t len_ = (size_t)(len); \ const blob_T *const blob_ = (blob); \ - kvi_push(edata->stack, STRING_OBJ(((String) { \ - .data = len_ != 0 ? xmemdupz(blob_->bv_ga.ga_data, len_) : xstrdup(""), \ - .size = len_ \ - }))); \ + kvi_push(edata->stack, typval_cbuf_to_obj(edata, len_ ? blob_->bv_ga.ga_data : "", len_)); \ } while (0) #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ @@ -91,11 +98,7 @@ typedef struct { static inline void typval_encode_list_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kvi_push(edata->stack, ARRAY_OBJ(((Array) { - .capacity = len, - .size = 0, - .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.array.items)), - }))); + kvi_push(edata->stack, ARRAY_OBJ(arena_array(edata->arena, len))); } #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ @@ -110,7 +113,7 @@ static inline void typval_encode_between_list_items(EncodedData *const edata) Object *const list = &kv_last(edata->stack); assert(list->type == kObjectTypeArray); assert(list->data.array.size < list->data.array.capacity); - list->data.array.items[list->data.array.size++] = item; + ADD_C(list->data.array, item); } #define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \ @@ -132,11 +135,7 @@ static inline void typval_encode_list_end(EncodedData *const edata) static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kvi_push(edata->stack, DICTIONARY_OBJ(((Dictionary) { - .capacity = len, - .size = 0, - .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.dictionary.items)), - }))); + kvi_push(edata->stack, DICTIONARY_OBJ(arena_dict(edata->arena, len))); } #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ @@ -157,9 +156,8 @@ static inline void typval_encode_after_key(EncodedData *const edata) dict->data.dictionary.items[dict->data.dictionary.size].key = key.data.string; } else { - api_free_object(key); dict->data.dictionary.items[dict->data.dictionary.size].key - = STATIC_CSTR_TO_STRING("__INVALID_KEY__"); + = STATIC_CSTR_AS_STRING("__INVALID_KEY__"); } } @@ -234,17 +232,22 @@ static inline void typval_encode_dict_end(EncodedData *const edata) #undef TYPVAL_ENCODE_CONV_RECURSE #undef TYPVAL_ENCODE_ALLOW_SPECIALS -/// Convert a vim object to an `Object` instance, recursively expanding +/// Convert a vim object to an `Object` instance, recursively converting /// Arrays/Dictionaries. /// /// @param obj The source object +/// @param arena if NULL, use direct allocation +/// @param reuse_strdata when true, don't copy string data to Arena but reference +/// typval strings directly. takes no effect when arena is +/// NULL /// @return The converted value -Object vim_to_object(typval_T *obj) +Object vim_to_object(typval_T *obj, Arena *arena, bool reuse_strdata) { EncodedData edata; kvi_init(edata.stack); - const int evo_ret = encode_vim_to_object(&edata, obj, - "vim_to_object argument"); + edata.arena = arena; + edata.reuse_strdata = reuse_strdata; + const int evo_ret = encode_vim_to_object(&edata, obj, "vim_to_object argument"); (void)evo_ret; assert(evo_ret == OK); Object ret = kv_A(edata.stack, 0); @@ -259,14 +262,20 @@ Object vim_to_object(typval_T *obj) /// @param tv Conversion result is placed here. On failure member v_type is /// set to VAR_UNKNOWN (no allocation was made for this variable). /// @param err Error object. +void object_to_vim(Object obj, typval_T *tv, Error *err) +{ + object_to_vim_take_luaref(&obj, tv, false, err); +} + +/// same as object_to_vim but consumes all luarefs (nested) in `obj` /// -/// @returns true if conversion is successful, otherwise false. -bool object_to_vim(Object obj, typval_T *tv, Error *err) +/// useful when `obj` is allocated on an arena +void object_to_vim_take_luaref(Object *obj, typval_T *tv, bool take_luaref, Error *err) { tv->v_type = VAR_UNKNOWN; tv->v_lock = VAR_UNLOCKED; - switch (obj.type) { + switch (obj->type) { case kObjectTypeNil: tv->v_type = VAR_SPECIAL; tv->vval.v_special = kSpecialVarNull; @@ -274,46 +283,40 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeBoolean: tv->v_type = VAR_BOOL; - tv->vval.v_bool = obj.data.boolean ? kBoolVarTrue : kBoolVarFalse; + tv->vval.v_bool = obj->data.boolean ? kBoolVarTrue : kBoolVarFalse; break; case kObjectTypeBuffer: case kObjectTypeWindow: case kObjectTypeTabpage: case kObjectTypeInteger: - STATIC_ASSERT(sizeof(obj.data.integer) <= sizeof(varnumber_T), + STATIC_ASSERT(sizeof(obj->data.integer) <= sizeof(varnumber_T), "Integer size must be <= Vimscript number size"); tv->v_type = VAR_NUMBER; - tv->vval.v_number = (varnumber_T)obj.data.integer; + tv->vval.v_number = (varnumber_T)obj->data.integer; break; case kObjectTypeFloat: tv->v_type = VAR_FLOAT; - tv->vval.v_float = obj.data.floating; + tv->vval.v_float = obj->data.floating; break; case kObjectTypeString: tv->v_type = VAR_STRING; - if (obj.data.string.data == NULL) { + if (obj->data.string.data == NULL) { tv->vval.v_string = NULL; } else { - tv->vval.v_string = xmemdupz(obj.data.string.data, - obj.data.string.size); + tv->vval.v_string = xmemdupz(obj->data.string.data, + obj->data.string.size); } break; case kObjectTypeArray: { - list_T *const list = tv_list_alloc((ptrdiff_t)obj.data.array.size); + list_T *const list = tv_list_alloc((ptrdiff_t)obj->data.array.size); - for (uint32_t i = 0; i < obj.data.array.size; i++) { - Object item = obj.data.array.items[i]; + for (uint32_t i = 0; i < obj->data.array.size; i++) { typval_T li_tv; - - if (!object_to_vim(item, &li_tv, err)) { - tv_list_free(list); - return false; - } - + object_to_vim_take_luaref(&obj->data.array.items[i], &li_tv, take_luaref, err); tv_list_append_owned_tv(list, li_tv); } tv_list_ref(list); @@ -326,27 +329,11 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeDictionary: { dict_T *const dict = tv_dict_alloc(); - for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { - KeyValuePair item = obj.data.dictionary.items[i]; - String key = item.key; - - if (key.size == 0) { - api_set_error(err, kErrorTypeValidation, - "Empty dictionary keys aren't allowed"); - // cleanup - tv_dict_free(dict); - return false; - } - + for (uint32_t i = 0; i < obj->data.dictionary.size; i++) { + KeyValuePair *item = &obj->data.dictionary.items[i]; + String key = item->key; dictitem_T *const di = tv_dict_item_alloc(key.data); - - if (!object_to_vim(item.value, &di->di_tv, err)) { - // cleanup - tv_dict_item_free(di); - tv_dict_free(dict); - return false; - } - + object_to_vim_take_luaref(&item->value, &di->di_tv, take_luaref, err); tv_dict_add(dict, di); } dict->dv_refcount++; @@ -357,12 +344,16 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) } case kObjectTypeLuaRef: { - char *name = register_luafunc(api_new_luaref(obj.data.luaref)); + LuaRef ref = obj->data.luaref; + if (take_luaref) { + obj->data.luaref = LUA_NOREF; + } else { + ref = api_new_luaref(ref); + } + char *name = register_luafunc(ref); tv->v_type = VAR_FUNC; tv->vval.v_string = xstrdup(name); break; } } - - return true; } diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 25c8377518..ca088d7a55 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -105,6 +105,14 @@ typedef enum { kObjectTypeTabpage, } ObjectType; +/// Value by which objects represented as EXT type are shifted +/// +/// Subtracted when packing, added when unpacking. Used to allow moving +/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be +/// split or reordered. +#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer +#define EXT_OBJECT_TYPE_MAX (kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT) + struct object { ObjectType type; union { @@ -124,6 +132,7 @@ struct key_value_pair { }; typedef uint64_t OptionalKeys; +typedef Integer HLGroupID; // this is the prefix of all keysets with optional keys typedef struct { @@ -135,6 +144,7 @@ typedef struct { size_t ptr_off; ObjectType type; // kObjectTypeNil == untyped int opt_index; + bool is_hlgroup; } KeySetLink; typedef KeySetLink *(*FieldHashfn)(const char *str, size_t len); diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index 6a2c9eaf54..288f368fba 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -3,7 +3,7 @@ #include <stdbool.h> #include <stdint.h> -#include "nvim/api/private/defs.h" +#include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/memory_defs.h" #include "nvim/types_defs.h" @@ -14,18 +14,18 @@ typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, Array args, Arena *are struct MsgpackRpcRequestHandler { const char *name; ApiDispatchWrapper fn; - bool fast; // Function is safe to be executed immediately while running the - // uv loop (the loop is run very frequently due to breakcheck). - // If "fast" is false, the function is deferred, i e the call will - // be put in the event queue, for safe handling later. - bool arena_return; // return value is allocated in the arena (or statically) - // and should not be freed as such. + bool fast; ///< Function is safe to be executed immediately while running the + ///< uv loop (the loop is run very frequently due to breakcheck). + ///< If "fast" is false, the function is deferred, i e the call will + ///< be put in the event queue, for safe handling later. + bool ret_alloc; ///< return value is allocated and should be freed using api_free_object + ///< otherwise it uses arena and/or static memory }; extern const MsgpackRpcRequestHandler method_handlers[]; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/dispatch.h.generated.h" -# include "api/private/dispatch_wrappers.h.generated.h" // IWYU pragma: export +# include "api/private/dispatch_wrappers.h.generated.h" # include "keysets_defs.generated.h" #endif diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index be39836a5b..1cd98aa0c4 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -17,11 +17,9 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/ex_eval.h" -#include "nvim/func_attr.h" -#include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/globals.h" #include "nvim/highlight_group.h" #include "nvim/lua/executor.h" @@ -29,17 +27,18 @@ #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" -#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/unpacker.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/version.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "api/private/funcs_metadata.generated.h" +# include "api/private/api_metadata.generated.h" # include "api/private/helpers.c.generated.h" -# include "api/private/ui_events_metadata.generated.h" #endif /// Start block that may cause Vimscript exceptions while evaluating another code @@ -140,7 +139,7 @@ bool try_end(Error *err) api_set_error(err, kErrorTypeException, "Keyboard interrupt"); got_int = false; } else if (msg_list != NULL && *msg_list != NULL) { - int should_free; + bool should_free; char *msg = get_exception_string(*msg_list, ET_ERROR, NULL, @@ -151,7 +150,7 @@ bool try_end(Error *err) if (should_free) { xfree(msg); } - } else if (did_throw) { + } else if (did_throw || need_rethrow) { if (*current_exception->throw_name != NUL) { if (current_exception->throw_lnum != 0) { api_set_error(err, kErrorTypeException, "%s, line %" PRIdLINENR ": %s", @@ -175,7 +174,7 @@ bool try_end(Error *err) /// @param dict The vimscript dict /// @param key The key /// @param[out] err Details of an error that may have occurred -Object dict_get_value(dict_T *dict, String key, Error *err) +Object dict_get_value(dict_T *dict, String key, Arena *arena, Error *err) { dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); @@ -184,7 +183,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err) return (Object)OBJECT_INIT; } - return vim_to_object(&di->di_tv); + return vim_to_object(&di->di_tv, arena, true); } dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err) @@ -221,7 +220,8 @@ dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err) /// @param retval If true the old value will be converted and returned. /// @param[out] err Details of an error that may have occurred /// @return The old value if `retval` is true and the key was present, else NIL -Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retval, Error *err) +Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retval, Arena *arena, + Error *err) { Object rv = OBJECT_INIT; dictitem_T *di = dict_check_writable(dict, key, del, err); @@ -244,7 +244,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva } // Return the old value if (retval) { - rv = vim_to_object(&di->di_tv); + rv = vim_to_object(&di->di_tv, arena, false); } // Delete the entry tv_dict_item_remove(dict, di); @@ -254,9 +254,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva typval_T tv; // Convert the object to a vimscript type in the temporary variable - if (!object_to_vim(value, &tv, err)) { - return rv; - } + object_to_vim(value, &tv, err); typval_T oldtv = TV_INITIAL_VALUE; @@ -267,7 +265,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva } else { // Return the old value if (retval) { - rv = vim_to_object(&di->di_tv); + rv = vim_to_object(&di->di_tv, arena, false); } bool type_error = false; if (dict == &vimvardict @@ -427,12 +425,12 @@ String cstrn_as_string(char *str, size_t maxsize) /// @param str the C string to use /// @return The resulting String, or an empty String if /// str was NULL -String cstr_as_string(char *str) FUNC_ATTR_PURE +String cstr_as_string(const char *str) FUNC_ATTR_PURE { if (str == NULL) { return (String)STRING_INIT; } - return (String){ .data = str, .size = strlen(str) }; + return (String){ .data = (char *)str, .size = strlen(str) }; } /// Return the owned memory of a ga as a String @@ -456,9 +454,10 @@ String ga_take_string(garray_T *ga) /// @param input Binary string /// @param crlf Also break lines at CR and CRLF. /// @return [allocated] String array -Array string_to_array(const String input, bool crlf) +Array string_to_array(const String input, bool crlf, Arena *arena) { - Array ret = ARRAY_DICT_INIT; + ArrayBuilder ret = ARRAY_DICT_INIT; + kvi_init(ret); for (size_t i = 0; i < input.size; i++) { const char *start = input.data + i; const char *end = start; @@ -473,20 +472,17 @@ Array string_to_array(const String input, bool crlf) if (crlf && *end == CAR && i + 1 < input.size && *(end + 1) == NL) { i += 1; // Advance past CRLF. } - String s = { - .size = line_len, - .data = xmemdupz(start, line_len), - }; + String s = CBUF_TO_ARENA_STR(arena, start, line_len); memchrsub(s.data, NUL, NL, line_len); - ADD(ret, STRING_OBJ(s)); + kvi_push(ret, STRING_OBJ(s)); // If line ends at end-of-buffer, add empty final item. // This is "readfile()-style", see also ":help channel-lines". if (i + 1 == input.size && (*end == NL || (crlf && *end == CAR))) { - ADD(ret, STRING_OBJ(STRING_INIT)); + kvi_push(ret, STRING_OBJ(STRING_INIT)); } } - return ret; + return arena_take_arraybuilder(arena, &ret); } /// Normalizes 0-based indexes to buffer line numbers. @@ -578,10 +574,19 @@ String arena_string(Arena *arena, String str) if (str.size) { return cbuf_as_string(arena_memdupz(arena, str.data, str.size), str.size); } else { - return (String)STRING_INIT; + return (String){ .data = arena ? "" : xstrdup(""), .size = 0 }; } } +Array arena_take_arraybuilder(Arena *arena, ArrayBuilder *arr) +{ + Array ret = arena_array(arena, kv_size(*arr)); + ret.size = kv_size(*arr); + memcpy(ret.items, arr->items, sizeof(ret.items[0]) * ret.size); + kvi_destroy(*arr); + return ret; +} + void api_free_object(Object value) { switch (value.type) { @@ -642,102 +647,30 @@ void api_clear_error(Error *value) value->type = kErrorTypeNone; } -/// @returns a shared value. caller must not modify it! -Dictionary api_metadata(void) -{ - static Dictionary metadata = ARRAY_DICT_INIT; - - if (!metadata.size) { - PUT(metadata, "version", DICTIONARY_OBJ(version_dict())); - init_function_metadata(&metadata); - init_ui_event_metadata(&metadata); - init_error_type_metadata(&metadata); - init_type_metadata(&metadata); - } - - return metadata; -} +// initialized once, never freed +static ArenaMem mem_for_metadata = NULL; -static void init_function_metadata(Dictionary *metadata) +/// @returns a shared value. caller must not modify it! +Object api_metadata(void) { - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - if (msgpack_unpack_next(&unpacked, - (const char *)funcs_metadata, - sizeof(funcs_metadata), - NULL) != MSGPACK_UNPACK_SUCCESS) { - abort(); - } - Object functions; - msgpack_rpc_to_object(&unpacked.data, &functions); - msgpack_unpacked_destroy(&unpacked); - PUT(*metadata, "functions", functions); -} + static Object metadata = OBJECT_INIT; -static void init_ui_event_metadata(Dictionary *metadata) -{ - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - if (msgpack_unpack_next(&unpacked, - (const char *)ui_events_metadata, - sizeof(ui_events_metadata), - NULL) != MSGPACK_UNPACK_SUCCESS) { - abort(); - } - Object ui_events; - msgpack_rpc_to_object(&unpacked.data, &ui_events); - msgpack_unpacked_destroy(&unpacked); - PUT(*metadata, "ui_events", ui_events); - Array ui_options = ARRAY_DICT_INIT; - ADD(ui_options, CSTR_TO_OBJ("rgb")); - for (UIExtension i = 0; i < kUIExtCount; i++) { - if (ui_ext_names[i][0] != '_') { - ADD(ui_options, CSTR_TO_OBJ(ui_ext_names[i])); + if (metadata.type == kObjectTypeNil) { + Arena arena = ARENA_EMPTY; + Error err = ERROR_INIT; + metadata = unpack((char *)packed_api_metadata, sizeof(packed_api_metadata), &arena, &err); + if (ERROR_SET(&err) || metadata.type != kObjectTypeDictionary) { + abort(); } + mem_for_metadata = arena_finish(&arena); } - PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options)); -} - -static void init_error_type_metadata(Dictionary *metadata) -{ - Dictionary types = ARRAY_DICT_INIT; - - Dictionary exception_metadata = ARRAY_DICT_INIT; - PUT(exception_metadata, "id", INTEGER_OBJ(kErrorTypeException)); - - Dictionary validation_metadata = ARRAY_DICT_INIT; - PUT(validation_metadata, "id", INTEGER_OBJ(kErrorTypeValidation)); - PUT(types, "Exception", DICTIONARY_OBJ(exception_metadata)); - PUT(types, "Validation", DICTIONARY_OBJ(validation_metadata)); - - PUT(*metadata, "error_types", DICTIONARY_OBJ(types)); + return metadata; } -static void init_type_metadata(Dictionary *metadata) +String api_metadata_raw(void) { - Dictionary types = ARRAY_DICT_INIT; - - Dictionary buffer_metadata = ARRAY_DICT_INIT; - PUT(buffer_metadata, "id", - INTEGER_OBJ(kObjectTypeBuffer - EXT_OBJECT_TYPE_SHIFT)); - PUT(buffer_metadata, "prefix", CSTR_TO_OBJ("nvim_buf_")); - - Dictionary window_metadata = ARRAY_DICT_INIT; - PUT(window_metadata, "id", - INTEGER_OBJ(kObjectTypeWindow - EXT_OBJECT_TYPE_SHIFT)); - PUT(window_metadata, "prefix", CSTR_TO_OBJ("nvim_win_")); - - Dictionary tabpage_metadata = ARRAY_DICT_INIT; - PUT(tabpage_metadata, "id", - INTEGER_OBJ(kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT)); - PUT(tabpage_metadata, "prefix", CSTR_TO_OBJ("nvim_tabpage_")); - - PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata)); - PUT(types, "Window", DICTIONARY_OBJ(window_metadata)); - PUT(types, "Tabpage", DICTIONARY_OBJ(tabpage_metadata)); - - PUT(*metadata, "types", DICTIONARY_OBJ(types)); + return cbuf_as_string((char *)packed_api_metadata, sizeof(packed_api_metadata)); } // all the copy_[object] functions allow arena=NULL, @@ -938,13 +871,26 @@ bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error char *mem = ((char *)retval + field->ptr_off); Object *value = &dict.items[i].value; + if (field->type == kObjectTypeNil) { *(Object *)mem = *value; } else if (field->type == kObjectTypeInteger) { - VALIDATE_T(field->str, kObjectTypeInteger, value->type, { - return false; - }); - *(Integer *)mem = value->data.integer; + if (field->is_hlgroup) { + int hl_id = 0; + if (value->type != kObjectTypeNil) { + hl_id = object_to_hl_id(*value, k.data, err); + if (ERROR_SET(err)) { + return false; + } + } + *(Integer *)mem = hl_id; + } else { + VALIDATE_T(field->str, kObjectTypeInteger, value->type, { + return false; + }); + + *(Integer *)mem = value->data.integer; + } } else if (field->type == kObjectTypeFloat) { Float *val = (Float *)mem; if (value->type == kObjectTypeInteger) { @@ -1003,24 +949,104 @@ bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error return true; } -void api_free_keydict(void *dict, KeySetLink *table) +Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, Arena *arena) +{ + Dictionary rv = arena_dict(arena, max_size); + for (size_t i = 0; table[i].str; i++) { + KeySetLink *field = &table[i]; + bool is_set = true; + if (field->opt_index >= 0) { + OptKeySet *ks = (OptKeySet *)value; + is_set = ks->is_set_ & (1ULL << field->opt_index); + } + + if (!is_set) { + continue; + } + + char *mem = ((char *)value + field->ptr_off); + Object val = NIL; + + if (field->type == kObjectTypeNil) { + val = *(Object *)mem; + } else if (field->type == kObjectTypeInteger) { + val = INTEGER_OBJ(*(Integer *)mem); + } else if (field->type == kObjectTypeFloat) { + val = FLOAT_OBJ(*(Float *)mem); + } else if (field->type == kObjectTypeBoolean) { + val = BOOLEAN_OBJ(*(Boolean *)mem); + } else if (field->type == kObjectTypeString) { + val = STRING_OBJ(*(String *)mem); + } else if (field->type == kObjectTypeArray) { + val = ARRAY_OBJ(*(Array *)mem); + } else if (field->type == kObjectTypeDictionary) { + val = DICTIONARY_OBJ(*(Dictionary *)mem); + } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow + || field->type == kObjectTypeTabpage) { + val.data.integer = *(handle_T *)mem; + val.type = field->type; + } else if (field->type == kObjectTypeLuaRef) { + // do nothing + } else { + abort(); + } + + PUT_C(rv, field->str, val); + } + + return rv; +} + +void api_luarefs_free_object(Object value) +{ + // TODO(bfredl): this is more complicated than it needs to be. + // we should be able to lock down more specifically where luarefs can be + switch (value.type) { + case kObjectTypeLuaRef: + api_free_luaref(value.data.luaref); + break; + + case kObjectTypeArray: + api_luarefs_free_array(value.data.array); + break; + + case kObjectTypeDictionary: + api_luarefs_free_dict(value.data.dictionary); + break; + + default: + break; + } +} + +void api_luarefs_free_keydict(void *dict, KeySetLink *table) { for (size_t i = 0; table[i].str; i++) { char *mem = ((char *)dict + table[i].ptr_off); if (table[i].type == kObjectTypeNil) { - api_free_object(*(Object *)mem); - } else if (table[i].type == kObjectTypeString) { - api_free_string(*(String *)mem); - } else if (table[i].type == kObjectTypeArray) { - api_free_array(*(Array *)mem); - } else if (table[i].type == kObjectTypeDictionary) { - api_free_dictionary(*(Dictionary *)mem); + api_luarefs_free_object(*(Object *)mem); } else if (table[i].type == kObjectTypeLuaRef) { api_free_luaref(*(LuaRef *)mem); + } else if (table[i].type == kObjectTypeDictionary) { + api_luarefs_free_dict(*(Dictionary *)mem); } } } +void api_luarefs_free_array(Array value) +{ + for (size_t i = 0; i < value.size; i++) { + api_luarefs_free_object(value.items[i]); + } +} + +void api_luarefs_free_dict(Dictionary value) +{ + for (size_t i = 0; i < value.size; i++) { + api_luarefs_free_object(value.items[i].value); + } +} + /// Set a named mark /// buffer and mark name must be validated already /// @param buffer Buffer to set the mark on @@ -1048,7 +1074,7 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err) } } assert(INT32_MIN <= line && line <= INT32_MAX); - pos_T pos = { (linenr_T)line, (int)col, (int)col }; + pos_T pos = { (linenr_T)line, (int)col, 0 }; res = setmark_pos(*name.data, &pos, buf->handle, NULL); if (!res) { if (deleting) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index e61dd5f992..7eda8ffaf6 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -2,21 +2,15 @@ #include <stdbool.h> #include <stddef.h> -#include <stdint.h> #include "klib/kvec.h" -#include "nvim/api/private/defs.h" -#include "nvim/api/private/dispatch.h" -#include "nvim/decoration.h" -#include "nvim/eval/typval_defs.h" +#include "nvim/api/private/defs.h" // IWYU pragma: keep +#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_eval_defs.h" -#include "nvim/getchar.h" -#include "nvim/gettext.h" -#include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/map_defs.h" -#include "nvim/memory.h" -#include "nvim/message.h" +#include "nvim/message_defs.h" // IWYU pragma: keep #define OBJECT_OBJ(o) o @@ -38,6 +32,10 @@ #define CSTR_AS_OBJ(s) STRING_OBJ(cstr_as_string(s)) #define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s)) +#define CSTR_TO_ARENA_STR(arena, s) arena_string(arena, cstr_as_string(s)) +#define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(CSTR_TO_ARENA_STR(arena, s)) +#define CBUF_TO_ARENA_STR(arena, s, len) arena_string(arena, cbuf_as_string((char *)(s), len)) +#define CBUF_TO_ARENA_OBJ(arena, s, len) STRING_OBJ(CBUF_TO_ARENA_STR(arena, s, len)) #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ @@ -76,6 +74,9 @@ #define PUT_C(dict, k, v) \ kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v })) +#define PUT_KEY(d, typ, key, v) \ + do { (d).is_set__##typ##_ |= (1 << KEYSET_OPTIDX_##typ##__##key); (d).key = v; } while (0) + #define ADD(array, item) \ kv_push(array, item) @@ -94,6 +95,8 @@ name.capacity = maxsize; \ name.items = name##__items; \ +typedef kvec_withinit_t(Object, 16) ArrayBuilder; + #define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) #define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof("" s) - 1 }) @@ -120,12 +123,7 @@ #define api_init_array = ARRAY_DICT_INIT #define api_init_dictionary = ARRAY_DICT_INIT -#define api_free_boolean(value) -#define api_free_integer(value) -#define api_free_float(value) -#define api_free_buffer(value) -#define api_free_window(value) -#define api_free_tabpage(value) +#define KEYDICT_INIT { 0 } EXTERN PMap(int) buffer_handles INIT( = MAP_INIT); EXTERN PMap(int) window_handles INIT( = MAP_INIT); diff --git a/src/nvim/api/private/validate.h b/src/nvim/api/private/validate.h index d1c977cd6e..2c1d1a241d 100644 --- a/src/nvim/api/private/validate.h +++ b/src/nvim/api/private/validate.h @@ -3,7 +3,7 @@ #include <stdbool.h> #include <stddef.h> -#include "nvim/api/private/defs.h" +#include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/api/private/helpers.h" #include "nvim/assert_defs.h" #include "nvim/macros_defs.h" diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index c854a22477..040abb1e3f 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -6,17 +6,20 @@ #include "nvim/api/tabpage.h" #include "nvim/api/vim.h" #include "nvim/buffer_defs.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/window.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/tabpage.c.generated.h" +#endif + /// Gets the windows in a tabpage /// /// @param tabpage Tabpage handle, or 0 for current tabpage /// @param[out] err Error details, if any /// @return List of windows in `tabpage` -ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) +ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; @@ -26,15 +29,15 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) return rv; } + size_t n = 0; FOR_ALL_WINDOWS_IN_TAB(wp, tab) { - rv.size++; + n++; } - rv.items = xmalloc(sizeof(Object) * rv.size); - size_t i = 0; + rv = arena_array(arena, n); FOR_ALL_WINDOWS_IN_TAB(wp, tab) { - rv.items[i++] = WINDOW_OBJ(wp->handle); + ADD_C(rv, WINDOW_OBJ(wp->handle)); } return rv; @@ -46,7 +49,7 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err) +Object nvim_tabpage_get_var(Tabpage tabpage, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -55,7 +58,7 @@ Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err) return (Object)OBJECT_INIT; } - return dict_get_value(tab->tp_vars, name, err); + return dict_get_value(tab->tp_vars, name, arena, err); } /// Sets a tab-scoped (t:) variable @@ -73,7 +76,7 @@ void nvim_tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err return; } - dict_set_var(tab->tp_vars, name, value, false, false, err); + dict_set_var(tab->tp_vars, name, value, false, false, NULL, err); } /// Removes a tab-scoped (t:) variable @@ -90,7 +93,7 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err) return; } - dict_set_var(tab->tp_vars, name, NIL, true, false, err); + dict_set_var(tab->tp_vars, name, NIL, true, false, NULL, err); } /// Gets the current window in a tabpage @@ -119,6 +122,37 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err) abort(); } +/// Sets the current window in a tabpage +/// +/// @param tabpage Tabpage handle, or 0 for current tabpage +/// @param win Window handle, must already belong to {tabpage} +/// @param[out] err Error details, if any +void nvim_tabpage_set_win(Tabpage tabpage, Window win, Error *err) + FUNC_API_SINCE(12) +{ + tabpage_T *tp = find_tab_by_handle(tabpage, err); + if (!tp) { + return; + } + + win_T *wp = find_window_by_handle(win, err); + if (!wp) { + return; + } + + if (!tabpage_win_valid(tp, wp)) { + api_set_error(err, kErrorTypeException, "Window does not belong to tabpage %d", tp->handle); + return; + } + + if (tp == curtab) { + win_enter(wp, true); + } else if (tp->tp_curwin != wp) { + tp->tp_prevwin = tp->tp_curwin; + tp->tp_curwin = wp; + } +} + /// Gets the tabpage number /// /// @param tabpage Tabpage handle, or 0 for current tabpage diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 836a68546c..692e3f95fc 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -12,27 +12,34 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/api/ui.h" +#include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/eval.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/wstream.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/map_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/msgpack_rpc/channel.h" -#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/channel_defs.h" +#include "nvim/msgpack_rpc/packer.h" #include "nvim/option.h" #include "nvim/types_defs.h" #include "nvim/ui.h" -#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf)) +#define BUF_POS(ui) ((size_t)((ui)->packer.ptr - (ui)->packer.startptr)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" @@ -41,55 +48,6 @@ static PMap(uint64_t) connected_uis = MAP_INIT; -#define mpack_w(b, byte) *(*b)++ = (char)(byte); -static void mpack_w2(char **b, uint32_t v) -{ - *(*b)++ = (char)((v >> 8) & 0xff); - *(*b)++ = (char)(v & 0xff); -} - -static void mpack_w4(char **b, uint32_t v) -{ - *(*b)++ = (char)((v >> 24) & 0xff); - *(*b)++ = (char)((v >> 16) & 0xff); - *(*b)++ = (char)((v >> 8) & 0xff); - *(*b)++ = (char)(v & 0xff); -} - -static void mpack_uint(char **buf, uint32_t val) -{ - if (val > 0xffff) { - mpack_w(buf, 0xce); - mpack_w4(buf, val); - } else if (val > 0xff) { - mpack_w(buf, 0xcd); - mpack_w2(buf, val); - } else if (val > 0x7f) { - mpack_w(buf, 0xcc); - mpack_w(buf, val); - } else { - mpack_w(buf, val); - } -} - -static void mpack_bool(char **buf, bool val) -{ - mpack_w(buf, 0xc2 | (val ? 1 : 0)); -} - -static void mpack_array(char **buf, uint32_t len) -{ - if (len < 0x10) { - mpack_w(buf, 0x90 | len); - } else if (len < 0x10000) { - mpack_w(buf, 0xdc); - mpack_w2(buf, len); - } else { - mpack_w(buf, 0xdd); - mpack_w4(buf, len); - } -} - static char *mpack_array_dyn16(char **buf) { mpack_w(buf, 0xdc); @@ -98,30 +56,44 @@ static char *mpack_array_dyn16(char **buf) return pos; } -static void mpack_str(char **buf, const char *str) +static void mpack_str_small(char **buf, const char *str, size_t len) { - assert(sizeof(schar_T) - 1 < 0x20); - size_t len = strlen(str); + assert(len < 0x20); mpack_w(buf, 0xa0 | len); memcpy(*buf, str, len); *buf += len; } +static void remote_ui_destroy(RemoteUI *ui) + FUNC_ATTR_NONNULL_ALL +{ + kv_destroy(ui->call_buf); + xfree(ui->packer.startptr); + XFREE_CLEAR(ui->term_name); + xfree(ui); +} + void remote_ui_disconnect(uint64_t channel_id) { - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui) { return; } - UIData *data = ui->data; - kv_destroy(data->call_buf); pmap_del(uint64_t)(&connected_uis, channel_id, NULL); ui_detach_impl(ui, channel_id); + remote_ui_destroy(ui); +} - // Destroy `ui`. - XFREE_CLEAR(ui->term_name); - xfree(ui); +#ifdef EXITFREE +void remote_ui_free_all_mem(void) +{ + RemoteUI *ui; + map_foreach_value(&connected_uis, ui, { + remote_ui_destroy(ui); + }); + map_destroy(uint64_t, &connected_uis); } +#endif /// Wait until ui has connected on stdio channel if only_stdio /// is true, otherwise any channel. @@ -145,7 +117,7 @@ void remote_ui_wait_for_attach(bool only_stdio) /// Activates UI events on the channel. /// -/// Entry point of all UI clients. Allows |\-\-embed| to continue startup. +/// Entry point of all UI clients. Allows |--embed| to continue startup. /// Implies that the client is ready to show the UI. Adds the client to the /// list of UIs. |nvim_list_uis()| /// @@ -173,7 +145,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona "Expected width > 0 and height > 0"); return; } - UI *ui = xcalloc(1, sizeof(UI)); + RemoteUI *ui = xcalloc(1, sizeof(RemoteUI)); ui->width = (int)width; ui->height = (int)height; ui->pum_row = -1.0; @@ -200,22 +172,26 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona ui->ui_ext[kUICmdline] = true; } - UIData *data = ui->data; - data->channel_id = channel_id; - data->cur_event = NULL; - data->hl_id = 0; - data->client_col = -1; - data->nevents_pos = NULL; - data->nevents = 0; - data->flushed_events = false; - data->ncalls_pos = NULL; - data->ncalls = 0; - data->ncells_pending = 0; - data->buf_wptr = data->buf; - data->temp_buf = NULL; - data->wildmenu_active = false; - data->call_buf = (Array)ARRAY_DICT_INIT; - kv_ensure_space(data->call_buf, 16); + ui->channel_id = channel_id; + ui->cur_event = NULL; + ui->hl_id = 0; + ui->client_col = -1; + ui->nevents_pos = NULL; + ui->nevents = 0; + ui->flushed_events = false; + ui->ncalls_pos = NULL; + ui->ncalls = 0; + ui->ncells_pending = 0; + ui->packer = (PackerBuffer) { + .startptr = NULL, + .ptr = NULL, + .endptr = NULL, + .packer_flush = ui_flush_callback, + .anydata = ui, + }; + ui->wildmenu_active = false; + ui->call_buf = (Array)ARRAY_DICT_INIT; + kv_ensure_space(ui->call_buf, 16); pmap_put(uint64_t)(&connected_uis, channel_id, ui); ui_attach_impl(ui, channel_id); @@ -227,10 +203,9 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona void ui_attach(uint64_t channel_id, Integer width, Integer height, Boolean enable_rgb, Error *err) FUNC_API_DEPRECATED_SINCE(1) { - Dictionary opts = ARRAY_DICT_INIT; - PUT(opts, "rgb", BOOLEAN_OBJ(enable_rgb)); + MAXSIZE_TEMP_DICT(opts, 1); + PUT_C(opts, "rgb", BOOLEAN_OBJ(enable_rgb)); nvim_ui_attach(channel_id, width, height, opts, err); - api_free_dictionary(opts); } /// Tells the nvim server if focus was gained or lost by the GUI @@ -268,7 +243,7 @@ void nvim_ui_detach(uint64_t channel_id, Error *err) } // TODO(bfredl): use me to detach a specific ui from the server -void remote_ui_stop(UI *ui) +void remote_ui_stop(RemoteUI *ui) { } @@ -287,7 +262,7 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width, Integer height, Erro return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui->width = (int)width; ui->height = (int)height; ui_refresh(); @@ -301,12 +276,12 @@ void nvim_ui_set_option(uint64_t channel_id, String name, Object value, Error *e "UI not attached to channel: %" PRId64, channel_id); return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui_set_option(ui, false, name, value, error); } -static void ui_set_option(UI *ui, bool init, String name, Object value, Error *err) +static void ui_set_option(RemoteUI *ui, bool init, String name, Object value, Error *err) { if (strequal(name.data, "override")) { VALIDATE_T("override", kObjectTypeBoolean, value.type, { @@ -435,7 +410,7 @@ void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, I } /// Tells Nvim the number of elements displaying in the popupmenu, to decide -/// <PageUp> and <PageDown> movement. +/// [<PageUp>] and [<PageDown>] movement. /// /// @param channel_id /// @param height Popupmenu height, must be greater than zero. @@ -454,7 +429,7 @@ void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err) return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "It must support the ext_popupmenu option"); @@ -490,7 +465,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "UI must support the ext_popupmenu option"); @@ -522,7 +497,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa /// /// @param channel_id /// @param event Event name -/// @param payload Event payload +/// @param value Event payload /// @param[out] err Error details, if any. void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err) FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY @@ -539,126 +514,75 @@ void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error * } } -static void flush_event(UIData *data) +static void flush_event(RemoteUI *ui) { - if (data->cur_event) { - mpack_w2(&data->ncalls_pos, data->ncalls); - data->cur_event = NULL; - } - if (!data->nevents_pos) { - assert(BUF_POS(data) == 0); - char **buf = &data->buf_wptr; - // [2, "redraw", [...]] - mpack_array(buf, 3); - mpack_uint(buf, 2); - mpack_str(buf, "redraw"); - data->nevents_pos = mpack_array_dyn16(buf); + if (ui->cur_event) { + mpack_w2(&ui->ncalls_pos, 1 + ui->ncalls); + ui->cur_event = NULL; + ui->ncalls_pos = NULL; + ui->ncalls = 0; } } -static inline int write_cb(void *vdata, const char *buf, size_t len) +static void ui_alloc_buf(RemoteUI *ui) { - UIData *data = (UIData *)vdata; - if (!buf) { - return 0; - } - - data->pack_totlen += len; - if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) { - data->buf_overflow = true; - return 0; - } - - memcpy(data->buf_wptr, buf, len); - data->buf_wptr += len; - - return 0; + ui->packer.startptr = alloc_block(); + ui->packer.ptr = ui->packer.startptr; + ui->packer.endptr = ui->packer.startptr + UI_BUF_SIZE; } -static bool prepare_call(UI *ui, const char *name) +static void prepare_call(RemoteUI *ui, const char *name) { - UIData *data = ui->data; + if (ui->packer.startptr && BUF_POS(ui) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + ui_flush_buf(ui); + } - if (BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { - remote_ui_flush_buf(ui); + if (ui->packer.startptr == NULL) { + ui_alloc_buf(ui); } // To optimize data transfer(especially for "grid_line"), we bundle adjacent // calls to same method together, so only add a new call entry if the last // method call is different from "name" - if (!data->cur_event || !strequal(data->cur_event, name)) { - flush_event(data); - data->cur_event = name; - char **buf = &data->buf_wptr; - data->ncalls_pos = mpack_array_dyn16(buf); - mpack_str(buf, name); - data->nevents++; - data->ncalls = 1; - return true; + if (!ui->cur_event || !strequal(ui->cur_event, name)) { + char **buf = &ui->packer.ptr; + if (!ui->nevents_pos) { + // [2, "redraw", [...]] + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str_small(buf, S_LEN("redraw")); + ui->nevents_pos = mpack_array_dyn16(buf); + assert(ui->cur_event == NULL); + } + flush_event(ui); + ui->cur_event = name; + ui->ncalls_pos = mpack_array_dyn16(buf); + mpack_str_small(buf, name, strlen(name)); + ui->nevents++; + ui->ncalls = 1; + } else { + ui->ncalls++; } - - return false; } -/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). -static void push_call(UI *ui, const char *name, Array args) +/// Pushes data into RemoteUI, to be consumed later by remote_ui_flush(). +static void push_call(RemoteUI *ui, const char *name, Array args) { - UIData *data = ui->data; - bool pending = data->nevents_pos; - char *buf_pos_save = data->buf_wptr; - - bool new_event = prepare_call(ui, name); - - msgpack_packer pac; - data->pack_totlen = 0; - data->buf_overflow = false; - msgpack_packer_init(&pac, data, write_cb); - msgpack_rpc_from_array(args, &pac); - if (data->buf_overflow) { - data->buf_wptr = buf_pos_save; - if (new_event) { - data->cur_event = NULL; - data->nevents--; - } - if (pending) { - remote_ui_flush_buf(ui); - } + prepare_call(ui, name); + mpack_object_array(args, &ui->packer); +} - if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) { - // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) - data->temp_buf = xmalloc(20 + strlen(name) + data->pack_totlen); - data->buf_wptr = data->temp_buf; - char **buf = &data->buf_wptr; - mpack_array(buf, 3); - mpack_uint(buf, 2); - mpack_str(buf, "redraw"); - mpack_array(buf, 1); - mpack_array(buf, 2); - mpack_str(buf, name); - } else { - prepare_call(ui, name); - } - data->pack_totlen = 0; - data->buf_overflow = false; - msgpack_rpc_from_array(args, &pac); - - if (data->temp_buf) { - size_t size = (size_t)(data->buf_wptr - data->temp_buf); - WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree); - rpc_write_raw(data->channel_id, buf); - data->temp_buf = NULL; - data->buf_wptr = data->buf; - data->nevents_pos = NULL; - } - } - data->ncalls++; +static void ui_flush_callback(PackerBuffer *packer) +{ + RemoteUI *ui = packer->anydata; + ui_flush_buf(ui); + ui_alloc_buf(ui); } -void remote_ui_grid_clear(UI *ui, Integer grid) +void remote_ui_grid_clear(RemoteUI *ui, Integer grid) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } @@ -666,14 +590,13 @@ void remote_ui_grid_clear(UI *ui, Integer grid) push_call(ui, name, args); } -void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) +void remote_ui_grid_resize(RemoteUI *ui, Integer grid, Integer width, Integer height) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } else { - data->client_col = -1; // force cursor update + ui->client_col = -1; // force cursor update } ADD_C(args, INTEGER_OBJ(width)); ADD_C(args, INTEGER_OBJ(height)); @@ -681,12 +604,11 @@ void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) push_call(ui, name, args); } -void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left, +void remote_ui_grid_scroll(RemoteUI *ui, Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) { - UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(grid)); ADD_C(args, INTEGER_OBJ(top)); ADD_C(args, INTEGER_OBJ(bot)); @@ -696,20 +618,20 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ ADD_C(args, INTEGER_OBJ(cols)); push_call(ui, "grid_scroll", args); } else { - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(top)); ADD_C(args, INTEGER_OBJ(bot - 1)); ADD_C(args, INTEGER_OBJ(left)); ADD_C(args, INTEGER_OBJ(right - 1)); push_call(ui, "set_scroll_region", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(rows)); push_call(ui, "scroll", args); // some clients have "clear" being affected by scroll region, // so reset it. - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(0)); ADD_C(args, INTEGER_OBJ(ui->height - 1)); ADD_C(args, INTEGER_OBJ(0)); @@ -718,14 +640,13 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ } } -void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, +void remote_ui_default_colors_set(RemoteUI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg, Integer cterm_bg) { if (!ui->ui_ext[kUITermColors]) { HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); } - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(rgb_fg)); ADD_C(args, INTEGER_OBJ(rgb_bg)); ADD_C(args, INTEGER_OBJ(rgb_sp)); @@ -735,34 +656,41 @@ void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Intege // Deprecated if (!ui->ui_ext[kUILinegrid]) { - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); push_call(ui, "update_fg", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); push_call(ui, "update_bg", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); push_call(ui, "update_sp", args); } } -void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, +void remote_ui_hl_attr_define(RemoteUI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info) { if (!ui->ui_ext[kUILinegrid]) { return; } - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(id)); MAXSIZE_TEMP_DICT(rgb, HLATTRS_DICT_SIZE); MAXSIZE_TEMP_DICT(cterm, HLATTRS_DICT_SIZE); hlattrs2dict(&rgb, NULL, rgb_attrs, true, false); hlattrs2dict(&cterm, NULL, rgb_attrs, false, false); + + // URLs are not added in hlattrs2dict since they are used only by UIs and not by the highlight + // system. So we add them here. + if (rgb_attrs.url >= 0) { + const char *url = hl_get_url((uint32_t)rgb_attrs.url); + PUT_C(rgb, "url", CSTR_AS_OBJ(url)); + } + ADD_C(args, DICTIONARY_OBJ(rgb)); ADD_C(args, DICTIONARY_OBJ(cterm)); @@ -775,15 +703,14 @@ void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cte push_call(ui, "hl_attr_define", args); } -void remote_ui_highlight_set(UI *ui, int id) +void remote_ui_highlight_set(RemoteUI *ui, int id) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; - if (data->hl_id == id) { + if (ui->hl_id == id) { return; } - data->hl_id = id; + ui->hl_id = id; MAXSIZE_TEMP_DICT(dict, HLATTRS_DICT_SIZE); hlattrs2dict(&dict, NULL, syn_attr2entry(id), ui->rgb, false); ADD_C(args, DICTIONARY_OBJ(dict)); @@ -791,57 +718,55 @@ void remote_ui_highlight_set(UI *ui, int id) } /// "true" cursor used only for input focus -void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) +void remote_ui_grid_cursor_goto(RemoteUI *ui, Integer grid, Integer row, Integer col) { if (ui->ui_ext[kUILinegrid]) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(grid)); ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "grid_cursor_goto", args); } else { - UIData *data = ui->data; - data->cursor_row = row; - data->cursor_col = col; + ui->cursor_row = row; + ui->cursor_col = col; remote_ui_cursor_goto(ui, row, col); } } /// emulated cursor used both for drawing and for input focus -void remote_ui_cursor_goto(UI *ui, Integer row, Integer col) +void remote_ui_cursor_goto(RemoteUI *ui, Integer row, Integer col) { - UIData *data = ui->data; - if (data->client_row == row && data->client_col == col) { + if (ui->client_row == row && ui->client_col == col) { return; } - data->client_row = row; - data->client_col = col; - Array args = data->call_buf; + ui->client_row = row; + ui->client_col = col; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "cursor_goto", args); } -void remote_ui_put(UI *ui, const char *cell) +void remote_ui_put(RemoteUI *ui, const char *cell) { - UIData *data = ui->data; - data->client_col++; - Array args = data->call_buf; - ADD_C(args, CSTR_AS_OBJ((char *)cell)); + ui->client_col++; + Array args = ui->call_buf; + ADD_C(args, CSTR_AS_OBJ(cell)); push_call(ui, "put", args); } -void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, +void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) { - UIData *data = ui->data; + // If MAX_SCHAR_SIZE is made larger, we need to refactor implementation below + // to not only use FIXSTR (only up to 0x20 bytes) + STATIC_ASSERT(MAX_SCHAR_SIZE - 1 < 0x20, "SCHAR doesn't fit in fixstr"); + if (ui->ui_ext[kUILinegrid]) { prepare_call(ui, "grid_line"); - data->ncalls++; - char **buf = &data->buf_wptr; + char **buf = &ui->packer.ptr; mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -856,7 +781,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] || chunk[i] != chunk[i + 1]) { - if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) { + if (UI_BUF_SIZE - BUF_POS(ui) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) { // close to overflowing the redraw buffer. finish this event, // flush, and start a new "grid_line" event at the current position. // For simplicity leave place for the final "clear" element @@ -865,10 +790,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // We only ever set the wrap field on the final "grid_line" event for the line. mpack_bool(buf, false); - remote_ui_flush_buf(ui); + ui_flush_buf(ui); prepare_call(ui, "grid_line"); - data->ncalls++; mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -880,16 +804,16 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1); nelem++; mpack_array(buf, csize); - char sc_buf[MAX_SCHAR_SIZE]; - schar_get(sc_buf, chunk[i]); - mpack_str(buf, sc_buf); + char *size_byte = (*buf)++; + size_t len = schar_get_adv(buf, chunk[i]); + *size_byte = (char)(0xa0 | len); if (csize >= 2) { mpack_uint(buf, (uint32_t)attrs[i]); if (csize >= 3) { mpack_uint(buf, repeat); } } - data->ncells_pending += MIN(repeat, 2); + ui->ncells_pending += MIN(repeat, 2); last_hl = attrs[i]; repeat = 0; was_space = chunk[i] == schar_from_ascii(' '); @@ -899,18 +823,18 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // no more cells to clear, so there is no ambiguity about what to clear. if (endcol < clearcol || was_space) { nelem++; - data->ncells_pending += 1; + ui->ncells_pending += 1; mpack_array(buf, 3); - mpack_str(buf, " "); + mpack_str_small(buf, S_LEN(" ")); mpack_uint(buf, (uint32_t)clearattr); mpack_uint(buf, (uint32_t)(clearcol - endcol)); } mpack_w2(&lenpos, nelem); mpack_bool(buf, flags & kLineFlagWrap); - if (data->ncells_pending > 500) { + if (ui->ncells_pending > 500) { // pass off cells to UI to let it start processing them - remote_ui_flush_buf(ui); + ui_flush_buf(ui); } } else { for (int i = 0; i < endcol - startcol; i++) { @@ -920,7 +844,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int schar_get(sc_buf, chunk[i]); remote_ui_put(ui, sc_buf); if (utf_ambiguous_width(utf_ptr2char(sc_buf))) { - data->client_col = -1; // force cursor update + ui->client_col = -1; // force cursor update } } if (endcol < clearcol) { @@ -944,49 +868,47 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int /// /// This might happen multiple times before the actual ui_flush, if the /// total redraw size is large! -void remote_ui_flush_buf(UI *ui) +static void ui_flush_buf(RemoteUI *ui) { - UIData *data = ui->data; - if (!data->nevents_pos) { + if (!ui->packer.startptr || !BUF_POS(ui)) { return; } - if (data->cur_event) { - flush_event(data); + + flush_event(ui); + if (ui->nevents_pos != NULL) { + mpack_w2(&ui->nevents_pos, ui->nevents); + ui->nevents = 0; + ui->nevents_pos = NULL; } - mpack_w2(&data->nevents_pos, data->nevents); - data->nevents = 0; - data->nevents_pos = NULL; - - // TODO(bfredl): elide copy by a length one free-list like the arena - size_t size = BUF_POS(data); - WBuffer *buf = wstream_new_buffer(xmemdup(data->buf, size), size, 1, xfree); - rpc_write_raw(data->channel_id, buf); - data->buf_wptr = data->buf; - // we have sent events to the client, but possibly not yet the final "flush" - // event. - data->flushed_events = true; - - data->ncells_pending = 0; + + WBuffer *buf = wstream_new_buffer(ui->packer.startptr, BUF_POS(ui), 1, free_block); + rpc_write_raw(ui->channel_id, buf); + + ui->packer.startptr = NULL; + ui->packer.ptr = NULL; + + // we have sent events to the client, but possibly not yet the final "flush" event. + ui->flushed_events = true; + ui->ncells_pending = 0; } /// An intentional flush (vsync) when Nvim is finished redrawing the screen /// /// Clients can know this happened by a final "flush" event at the end of the /// "redraw" batch. -void remote_ui_flush(UI *ui) +void remote_ui_flush(RemoteUI *ui) { - UIData *data = ui->data; - if (data->nevents > 0 || data->flushed_events) { + if (ui->nevents > 0 || ui->flushed_events) { if (!ui->ui_ext[kUILinegrid]) { - remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); + remote_ui_cursor_goto(ui, ui->cursor_row, ui->cursor_col); } push_call(ui, "flush", (Array)ARRAY_DICT_INIT); - remote_ui_flush_buf(ui); - data->flushed_events = false; + ui_flush_buf(ui); + ui->flushed_events = false; } } -static Array translate_contents(UI *ui, Array contents, Arena *arena) +static Array translate_contents(RemoteUI *ui, Array contents, Arena *arena) { Array new_contents = arena_array(arena, contents.size); for (size_t i = 0; i < contents.size; i++) { @@ -996,32 +918,31 @@ static Array translate_contents(UI *ui, Array contents, Arena *arena) if (attr) { Dictionary rgb_attrs = arena_dict(arena, HLATTRS_DICT_SIZE); hlattrs2dict(&rgb_attrs, NULL, syn_attr2entry(attr), ui->rgb, false); - ADD(new_item, DICTIONARY_OBJ(rgb_attrs)); + ADD_C(new_item, DICTIONARY_OBJ(rgb_attrs)); } else { - ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD_C(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); } - ADD(new_item, item.items[1]); - ADD(new_contents, ARRAY_OBJ(new_item)); + ADD_C(new_item, item.items[1]); + ADD_C(new_contents, ARRAY_OBJ(new_item)); } return new_contents; } -static Array translate_firstarg(UI *ui, Array args, Arena *arena) +static Array translate_firstarg(RemoteUI *ui, Array args, Arena *arena) { Array new_args = arena_array(arena, args.size); Array contents = args.items[0].data.array; ADD_C(new_args, ARRAY_OBJ(translate_contents(ui, contents, arena))); for (size_t i = 1; i < args.size; i++) { - ADD(new_args, args.items[i]); + ADD_C(new_args, args.items[i]); } return new_args; } -void remote_ui_event(UI *ui, char *name, Array args) +void remote_ui_event(RemoteUI *ui, char *name, Array args) { Arena arena = ARENA_EMPTY; - UIData *data = ui->data; if (!ui->ui_ext[kUILinegrid]) { // the representation of highlights in cmdline changed, translate back // never consumes args @@ -1030,7 +951,7 @@ void remote_ui_event(UI *ui, char *name, Array args) push_call(ui, name, new_args); goto free_ret; } else if (strequal(name, "cmdline_block_show")) { - Array new_args = data->call_buf; + Array new_args = ui->call_buf; Array block = args.items[0].data.array; Array new_block = arena_array(&arena, block.size); for (size_t i = 0; i < block.size; i++) { @@ -1049,10 +970,10 @@ void remote_ui_event(UI *ui, char *name, Array args) // Back-compat: translate popupmenu_xx to legacy wildmenu_xx. if (ui->ui_ext[kUIWildmenu]) { if (strequal(name, "popupmenu_show")) { - data->wildmenu_active = (args.items[4].data.integer == -1) - || !ui->ui_ext[kUIPopupmenu]; - if (data->wildmenu_active) { - Array new_args = data->call_buf; + ui->wildmenu_active = (args.items[4].data.integer == -1) + || !ui->ui_ext[kUIPopupmenu]; + if (ui->wildmenu_active) { + Array new_args = ui->call_buf; Array items = args.items[0].data.array; Array new_items = arena_array(&arena, items.size); for (size_t i = 0; i < items.size; i++) { @@ -1061,18 +982,18 @@ void remote_ui_event(UI *ui, char *name, Array args) ADD_C(new_args, ARRAY_OBJ(new_items)); push_call(ui, "wildmenu_show", new_args); if (args.items[1].data.integer != -1) { - Array new_args2 = data->call_buf; + Array new_args2 = ui->call_buf; ADD_C(new_args2, args.items[1]); push_call(ui, "wildmenu_select", new_args2); } goto free_ret; } } else if (strequal(name, "popupmenu_select")) { - if (data->wildmenu_active) { + if (ui->wildmenu_active) { name = "wildmenu_select"; } } else if (strequal(name, "popupmenu_hide")) { - if (data->wildmenu_active) { + if (ui->wildmenu_active) { name = "wildmenu_hide"; } } @@ -1084,9 +1005,3 @@ void remote_ui_event(UI *ui, char *name, Array args) free_ret: arena_mem_free(arena_finish(&arena)); } - -void remote_ui_inspect(UI *ui, Dictionary *info) -{ - UIData *data = ui->data; - PUT(*info, "chan", INTEGER_OBJ((Integer)data->channel_id)); -} diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h index 26a91d0dbc..cdccc27ba4 100644 --- a/src/nvim/api/ui.h +++ b/src/nvim/api/ui.h @@ -4,11 +4,24 @@ #include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/highlight_defs.h" // IWYU pragma: keep -#include "nvim/map_defs.h" #include "nvim/types_defs.h" // IWYU pragma: keep -#include "nvim/ui.h" +#include "nvim/ui_defs.h" // IWYU pragma: keep + +/// Keep in sync with UIExtension in ui_defs.h +EXTERN const char *ui_ext_names[] INIT( = { + "ext_cmdline", + "ext_popupmenu", + "ext_tabline", + "ext_wildmenu", + "ext_messages", + "ext_linegrid", + "ext_multigrid", + "ext_hlstate", + "ext_termcolors", + "_debug_float", +}); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.h.generated.h" -# include "ui_events_remote.h.generated.h" // IWYU pragma: export +# include "ui_events_remote.h.generated.h" #endif diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index bda0c72423..c2f02c34f8 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -39,6 +39,8 @@ void screenshot(String path) FUNC_API_SINCE(7); void option_set(String name, Object value) FUNC_API_SINCE(4); +void chdir(String path) + FUNC_API_SINCE(12); // Stop event is not exported as such, represented by EOF in the msgpack stream. void stop(void) FUNC_API_NOEXPORT; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d631b10af9..84a2f24dbc 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -21,21 +21,26 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/context.h" #include "nvim/cursor.h" #include "nvim/decoration.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" +#include "nvim/getchar_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/keycodes.h" #include "nvim/log.h" @@ -43,25 +48,33 @@ #include "nvim/macros_defs.h" #include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" +#include "nvim/message_defs.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/msgpack_rpc/unpacker.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/input.h" +#include "nvim/os/os_defs.h" #include "nvim/os/process.h" #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/runtime.h" -#include "nvim/sign.h" +#include "nvim/sign_defs.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/statusline.h" +#include "nvim/statusline_defs.h" #include "nvim/strings.h" #include "nvim/terminal.h" #include "nvim/types_defs.h" @@ -110,7 +123,7 @@ Dictionary nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, E /// /// @note Unlike the `:highlight` command which can update a highlight group, /// this function completely replaces the definition. For example: -/// ``nvim_set_hl(0, 'Visual', {})`` will clear the highlight group +/// `nvim_set_hl(0, 'Visual', {})` will clear the highlight group /// 'Visual'. /// /// @note The fg and bg keys also accept the string values `"fg"` or `"bg"` @@ -128,9 +141,9 @@ Dictionary nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, E /// |nvim_set_hl_ns()| or |nvim_win_set_hl_ns()| to activate them. /// @param name Highlight group name, e.g. "ErrorMsg" /// @param val Highlight definition map, accepts the following keys: -/// - fg (or foreground): color name or "#RRGGBB", see note. -/// - bg (or background): color name or "#RRGGBB", see note. -/// - sp (or special): color name or "#RRGGBB" +/// - fg: color name or "#RRGGBB", see note. +/// - bg: color name or "#RRGGBB", see note. +/// - sp: color name or "#RRGGBB" /// - blend: integer between 0 and 100 /// - bold: boolean /// - standout: boolean @@ -163,6 +176,12 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err) }); int link_id = -1; + // Setting URLs directly through highlight attributes is not supported + if (HAS_KEY(val, highlight, url)) { + api_free_string(val->url); + val->url = NULL_STRING; + } + HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); if (!ERROR_SET(err)) { ns_hl_def((NS)ns_id, hl_id, attrs, link_id, val); @@ -230,7 +249,7 @@ void nvim_set_hl_ns_fast(Integer ns_id, Error *err) /// /// On execution error: does not fail, but updates v:errmsg. /// -/// To input sequences like <C-o> use |nvim_replace_termcodes()| (typically +/// To input sequences like [<C-o>] use |nvim_replace_termcodes()| (typically /// with escape_ks=false) to replace |keycodes|, then pass the result to /// nvim_feedkeys(). /// @@ -318,11 +337,11 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) /// /// On execution error: does not fail, but updates v:errmsg. /// -/// @note |keycodes| like <CR> are translated, so "<" is special. -/// To input a literal "<", send <LT>. +/// @note |keycodes| like [<CR>] are translated, so "<" is special. +/// To input a literal "<", send [<LT>]. /// /// @note For mouse events use |nvim_input_mouse()|. The pseudokey form -/// "<LeftMouse><col,row>" is deprecated since |api-level| 6. +/// `<LeftMouse><col,row>` is deprecated since |api-level| 6. /// /// @param keys to be typed /// @return Number of bytes actually written (can be fewer than @@ -343,9 +362,10 @@ Integer nvim_input(String keys) /// by calling it multiple times in a loop: the intermediate mouse /// positions will be ignored. It should be used to implement real-time /// mouse input in a GUI. The deprecated pseudokey form -/// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitation. +/// (`<LeftMouse><col,row>`) of |nvim_input()| has the same limitation. /// -/// @param button Mouse button: one of "left", "right", "middle", "wheel", "move". +/// @param button Mouse button: one of "left", "right", "middle", "wheel", "move", +/// "x1", "x2". /// @param action For ordinary buttons, one of "press", "drag", "release". /// For the wheel, one of "up", "down", "left", "right". Ignored for "move". /// @param modifier String of modifiers each represented by a single char. @@ -376,6 +396,10 @@ void nvim_input_mouse(String button, String action, String modifier, Integer gri code = KE_RIGHTMOUSE; } else if (strequal(button.data, "wheel")) { code = KE_MOUSEDOWN; + } else if (strequal(button.data, "x1")) { + code = KE_X1MOUSE; + } else if (strequal(button.data, "x2")) { + code = KE_X2MOUSE; } else if (strequal(button.data, "move")) { code = KE_MOUSEMOVE; } else { @@ -427,17 +451,17 @@ error: "invalid button or action"); } -/// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with +/// Replaces terminal codes and |keycodes| ([<CR>], [<Esc>], ...) in a string with /// the internal representation. /// /// @param str String to be converted. /// @param from_part Legacy Vim parameter. Usually true. -/// @param do_lt Also translate <lt>. Ignored if `special` is false. -/// @param special Replace |keycodes|, e.g. <CR> becomes a "\r" char. +/// @param do_lt Also translate [<lt>]. Ignored if `special` is false. +/// @param special Replace |keycodes|, e.g. [<CR>] becomes a "\r" char. /// @see replace_termcodes /// @see cpoptions String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Boolean special) - FUNC_API_SINCE(1) + FUNC_API_SINCE(1) FUNC_API_RET_ALLOC { if (str.size == 0) { // Empty string @@ -456,7 +480,7 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool } char *ptr = NULL; - replace_termcodes(str.data, str.size, &ptr, 0, flags, NULL, CPO_TO_CPO_FLAGS); + replace_termcodes(str.data, str.size, &ptr, 0, flags, NULL, p_cpo); return cstr_as_string(ptr); } @@ -472,11 +496,12 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool /// or executing the Lua code. /// /// @return Return value of Lua code if present or NIL. -Object nvim_exec_lua(String code, Array args, Error *err) +Object nvim_exec_lua(String code, Array args, Arena *arena, Error *err) FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY { - return nlua_exec(code, args, err); + // TODO(bfredl): convert directly from msgpack to lua and then back again + return nlua_exec(code, args, kRetObject, arena, err); } /// Notify the user with a message @@ -488,7 +513,7 @@ Object nvim_exec_lua(String code, Array args, Error *err) /// @param log_level The log level /// @param opts Reserved for future use. /// @param[out] err Error details, if any -Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err) +Object nvim_notify(String msg, Integer log_level, Dictionary opts, Arena *arena, Error *err) FUNC_API_SINCE(7) { MAXSIZE_TEMP_ARRAY(args, 3); @@ -496,11 +521,11 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err) ADD_C(args, INTEGER_OBJ(log_level)); ADD_C(args, DICTIONARY_OBJ(opts)); - return NLUA_EXEC_STATIC("return vim.notify(...)", args, err); + return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err); } /// Calculates the number of display cells occupied by `text`. -/// Control characters including <Tab> count as one cell. +/// Control characters including [<Tab>] count as one cell. /// /// @param text Some text /// @param[out] err Error details, if any @@ -518,17 +543,23 @@ Integer nvim_strwidth(String text, Error *err) /// Gets the paths contained in |runtime-search-path|. /// /// @return List of paths -ArrayOf(String) nvim_list_runtime_paths(Error *err) +ArrayOf(String) nvim_list_runtime_paths(Arena *arena, Error *err) FUNC_API_SINCE(1) { - return nvim_get_runtime_file(NULL_STRING, true, err); + return nvim_get_runtime_file(NULL_STRING, true, arena, err); } -Array nvim__runtime_inspect(void) +/// @nodoc +Array nvim__runtime_inspect(Arena *arena) { - return runtime_inspect(); + return runtime_inspect(arena); } +typedef struct { + ArrayBuilder rv; + Arena *arena; +} RuntimeCookie; + /// Find files in runtime directories /// /// "name" can contain wildcards. For example @@ -541,25 +572,27 @@ Array nvim__runtime_inspect(void) /// @param name pattern of files to search for /// @param all whether to return all matches or only the first /// @return list of absolute paths to the found files -ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err) +ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Arena *arena, Error *err) FUNC_API_SINCE(7) FUNC_API_FAST { - Array rv = ARRAY_DICT_INIT; + RuntimeCookie cookie = { .rv = ARRAY_DICT_INIT, .arena = arena, }; + kvi_init(cookie.rv); int flags = DIP_DIRFILE | (all ? DIP_ALL : 0); TRY_WRAP(err, { - do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &rv); + do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie); }); - return rv; + return arena_take_arraybuilder(arena, &cookie.rv); } -static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *cookie) +static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *c) { - Array *rv = (Array *)cookie; + RuntimeCookie *cookie = (RuntimeCookie *)c; for (int i = 0; i < num_fnames; i++) { - ADD(*rv, CSTR_TO_OBJ(fnames[i])); + // TODO(bfredl): consider memory management of gen_expand_wildcards() itself + kvi_push(cookie->rv, CSTR_TO_ARENA_OBJ(cookie->arena, fnames[i])); if (!all) { return true; } @@ -568,7 +601,9 @@ static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *cooki return num_fnames > 0; } +/// @nodoc String nvim__get_lib_dir(void) + FUNC_API_RET_ALLOC { return cstr_as_string(get_lib_dir()); } @@ -579,7 +614,8 @@ String nvim__get_lib_dir(void) /// @param all whether to return all matches or only the first /// @param opts is_lua: only search Lua subdirs /// @return list of absolute paths to the found files -ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Error *err) +ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Arena *arena, + Error *err) FUNC_API_SINCE(8) FUNC_API_FAST { @@ -589,12 +625,12 @@ ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, E return (Array)ARRAY_DICT_INIT; } - ArrayOf(String) res = runtime_get_named(opts->is_lua, pat, all); + ArrayOf(String) res = runtime_get_named(opts->is_lua, pat, all, arena); if (opts->do_source) { for (size_t i = 0; i < res.size; i++) { String name = res.items[i].data.string; - (void)do_source(name.data, false, DOSO_NONE, NULL); + do_source(name.data, false, DOSO_NONE, NULL); } } @@ -632,31 +668,31 @@ void nvim_set_current_dir(String dir, Error *err) /// /// @param[out] err Error details, if any /// @return Current line string -String nvim_get_current_line(Error *err) +String nvim_get_current_line(Arena *arena, Error *err) FUNC_API_SINCE(1) { - return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); + return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, arena, err); } /// Sets the current line. /// /// @param line Line contents /// @param[out] err Error details, if any -void nvim_set_current_line(String line, Error *err) +void nvim_set_current_line(String line, Arena *arena, Error *err) FUNC_API_SINCE(1) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { - buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err); + buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, arena, err); } /// Deletes the current line. /// /// @param[out] err Error details, if any -void nvim_del_current_line(Error *err) +void nvim_del_current_line(Arena *arena, Error *err) FUNC_API_SINCE(1) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { - buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); + buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, arena, err); } /// Gets a global (g:) variable. @@ -664,7 +700,7 @@ void nvim_del_current_line(Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_get_var(String name, Error *err) +Object nvim_get_var(String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { dictitem_T *di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size); @@ -678,7 +714,7 @@ Object nvim_get_var(String name, Error *err) VALIDATE((di != NULL), "Key not found: %s", name.data, { return (Object)OBJECT_INIT; }); - return vim_to_object(&di->di_tv); + return vim_to_object(&di->di_tv, arena, true); } /// Sets a global (g:) variable. @@ -689,7 +725,7 @@ Object nvim_get_var(String name, Error *err) void nvim_set_var(String name, Object value, Error *err) FUNC_API_SINCE(1) { - dict_set_var(&globvardict, name, value, false, false, err); + dict_set_var(&globvardict, name, value, false, false, NULL, err); } /// Removes a global (g:) variable. @@ -699,7 +735,7 @@ void nvim_set_var(String name, Object value, Error *err) void nvim_del_var(String name, Error *err) FUNC_API_SINCE(1) { - dict_set_var(&globvardict, name, NIL, true, false, err); + dict_set_var(&globvardict, name, NIL, true, false, NULL, err); } /// Gets a v: variable. @@ -707,10 +743,10 @@ void nvim_del_var(String name, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_get_vvar(String name, Error *err) +Object nvim_get_vvar(String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { - return dict_get_value(&vimvardict, name, err); + return dict_get_value(&vimvardict, name, arena, err); } /// Sets a v: variable, if it is not readonly. @@ -721,12 +757,12 @@ Object nvim_get_vvar(String name, Error *err) void nvim_set_vvar(String name, Object value, Error *err) FUNC_API_SINCE(6) { - dict_set_var(&vimvardict, name, value, false, false, err); + dict_set_var(&vimvardict, name, value, false, false, NULL, err); } /// Echo a message. /// -/// @param chunks A list of [text, hl_group] arrays, each representing a +/// @param chunks A list of `[text, hl_group]` arrays, each representing a /// text chunk with specified highlight. `hl_group` element /// can be omitted for no highlight. /// @param history if true, add to |message-history|. @@ -799,20 +835,19 @@ void nvim_err_writeln(String str) /// Use |nvim_buf_is_loaded()| to check if a buffer is loaded. /// /// @return List of buffer handles -ArrayOf(Buffer) nvim_list_bufs(void) +ArrayOf(Buffer) nvim_list_bufs(Arena *arena) FUNC_API_SINCE(1) { - Array rv = ARRAY_DICT_INIT; + size_t n = 0; FOR_ALL_BUFFERS(b) { - rv.size++; + n++; } - rv.items = xmalloc(sizeof(Object) * rv.size); - size_t i = 0; + Array rv = arena_array(arena, n); FOR_ALL_BUFFERS(b) { - rv.items[i++] = BUFFER_OBJ(b->handle); + ADD_C(rv, BUFFER_OBJ(b->handle)); } return rv; @@ -854,20 +889,19 @@ void nvim_set_current_buf(Buffer buffer, Error *err) /// Gets the current list of window handles. /// /// @return List of window handles -ArrayOf(Window) nvim_list_wins(void) +ArrayOf(Window) nvim_list_wins(Arena *arena) FUNC_API_SINCE(1) { - Array rv = ARRAY_DICT_INIT; + size_t n = 0; FOR_ALL_TAB_WINDOWS(tp, wp) { - rv.size++; + n++; } - rv.items = xmalloc(sizeof(Object) * rv.size); - size_t i = 0; + Array rv = arena_array(arena, n); FOR_ALL_TAB_WINDOWS(tp, wp) { - rv.items[i++] = WINDOW_OBJ(wp->handle); + ADD_C(rv, WINDOW_OBJ(wp->handle)); } return rv; @@ -949,8 +983,8 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); if (scratch) { - set_string_option_direct_in_buf(buf, "bufhidden", -1, "hide", OPT_LOCAL, 0); - set_string_option_direct_in_buf(buf, "buftype", -1, "nofile", OPT_LOCAL, 0); + set_string_option_direct_in_buf(buf, kOptBufhidden, "hide", OPT_LOCAL, 0); + set_string_option_direct_in_buf(buf, kOptBuftype, "nofile", OPT_LOCAL, 0); assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already buf->b_p_swf = false; buf->b_p_ml = false; @@ -985,10 +1019,11 @@ fail: /// master end. For instance, a carriage return is sent /// as a "\r", not as a "\n". |textlock| applies. It is possible /// to call |nvim_chan_send()| directly in the callback however. -/// ["input", term, bufnr, data] +/// `["input", term, bufnr, data]` +/// - force_crlf: (boolean, default true) Convert "\n" to "\r\n". /// @param[out] err Error details, if any /// @return Channel id, or 0 on error -Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) +Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err) FUNC_API_SINCE(7) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { @@ -997,39 +1032,31 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) return 0; } - if (cmdwin_type != 0 && buf == curbuf) { + if (buf == cmdwin_buf) { api_set_error(err, kErrorTypeException, "%s", e_cmdwin); return 0; } LuaRef cb = LUA_NOREF; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - if (strequal("on_input", k.data)) { - VALIDATE_T("on_input", kObjectTypeLuaRef, v->type, { - return 0; - }); - cb = v->data.luaref; - v->data.luaref = LUA_NOREF; - break; - } else { - VALIDATE_S(false, "'opts' key", k.data, {}); - } + if (HAS_KEY(opts, open_term, on_input)) { + cb = opts->on_input; + opts->on_input = LUA_NOREF; } - TerminalOptions topts; Channel *chan = channel_alloc(kChannelStreamInternal); chan->stream.internal.cb = cb; chan->stream.internal.closed = false; - topts.data = chan; - // NB: overridden in terminal_check_size if a window is already - // displaying the buffer - topts.width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0); - topts.height = (uint16_t)curwin->w_height_inner; - topts.write_cb = term_write; - topts.resize_cb = term_resize; - topts.close_cb = term_close; + TerminalOptions topts = { + .data = chan, + // NB: overridden in terminal_check_size if a window is already + // displaying the buffer + .width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0), + .height = (uint16_t)curwin->w_height_inner, + .write_cb = term_write, + .resize_cb = term_resize, + .close_cb = term_close, + .force_crlf = GET_BOOL_OR_TRUE(opts, open_term, force_crlf), + }; channel_incref(chan); terminal_open(&chan->term, buf, topts); if (chan->term != NULL) { @@ -1039,7 +1066,7 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) return (Integer)chan->id; } -static void term_write(char *buf, size_t size, void *data) // NOLINT(readability-non-const-parameter) +static void term_write(const char *buf, size_t size, void *data) { Channel *chan = data; LuaRef cb = chan->stream.internal.cb; @@ -1049,9 +1076,9 @@ static void term_write(char *buf, size_t size, void *data) // NOLINT(readabilit MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ((Integer)chan->id)); ADD_C(args, BUFFER_OBJ(terminal_buf(chan->term))); - ADD_C(args, STRING_OBJ(((String){ .data = buf, .size = size }))); + ADD_C(args, STRING_OBJ(((String){ .data = (char *)buf, .size = size }))); textlock++; - nlua_call_ref(cb, "input", args, false, NULL); + nlua_call_ref(cb, "input", args, kRetNilBool, NULL, NULL); textlock--; } @@ -1098,20 +1125,19 @@ void nvim_chan_send(Integer chan, String data, Error *err) /// Gets the current list of tabpage handles. /// /// @return List of tabpage handles -ArrayOf(Tabpage) nvim_list_tabpages(void) +ArrayOf(Tabpage) nvim_list_tabpages(Arena *arena) FUNC_API_SINCE(1) { - Array rv = ARRAY_DICT_INIT; + size_t n = 0; FOR_ALL_TABS(tp) { - rv.size++; + n++; } - rv.items = xmalloc(sizeof(Object) * rv.size); - size_t i = 0; + Array rv = arena_array(arena, n); FOR_ALL_TABS(tp) { - rv.items[i++] = TABPAGE_OBJ(tp->handle); + ADD_C(rv, TABPAGE_OBJ(tp->handle)); } return rv; @@ -1172,7 +1198,7 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) /// @return /// - true: Client may continue pasting. /// - false: Client must cancel the paste. -Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) +Boolean nvim_paste(String data, Boolean crlf, Integer phase, Arena *arena, Error *err) FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { @@ -1182,19 +1208,17 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) VALIDATE_INT((phase >= -1 && phase <= 3), "phase", phase, { return false; }); - Array args = ARRAY_DICT_INIT; - Object rv = OBJECT_INIT; if (phase == -1 || phase == 1) { // Start of paste-stream. draining = false; } else if (draining) { // Skip remaining chunks. Report error only once per "stream". goto theend; } - Array lines = string_to_array(data, crlf); - ADD(args, ARRAY_OBJ(lines)); - ADD(args, INTEGER_OBJ(phase)); - rv = nvim_exec_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args, - err); + Array lines = string_to_array(data, crlf, arena); + MAXSIZE_TEMP_ARRAY(args, 2); + ADD_C(args, ARRAY_OBJ(lines)); + ADD_C(args, INTEGER_OBJ(phase)); + Object rv = NLUA_EXEC_STATIC("return vim.paste(...)", args, kRetNilBool, arena, err); if (ERROR_SET(err)) { draining = true; goto theend; @@ -1221,8 +1245,6 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) AppendCharToRedobuff(ESC); // Dot-repeat. } theend: - api_free_object(rv); - api_free_array(args); if (cancel || phase == -1 || phase == 3) { // End of paste-stream. draining = false; } @@ -1243,24 +1265,27 @@ theend: /// @param after If true insert after cursor (like |p|), or before (like |P|). /// @param follow If true place cursor at end of inserted text. /// @param[out] err Error details, if any -void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, Error *err) +void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, Arena *arena, + Error *err) FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { - yankreg_T *reg = xcalloc(1, sizeof(yankreg_T)); + yankreg_T reg[1] = { 0 }; VALIDATE_S((prepare_yankreg_from_object(reg, type, lines.size)), "type", type.data, { - goto cleanup; + return; }); if (lines.size == 0) { - goto cleanup; // Nothing to do. + return; // Nothing to do. } + reg->y_array = arena_alloc(arena, lines.size * sizeof(uint8_t *), true); + reg->y_size = lines.size; for (size_t i = 0; i < lines.size; i++) { VALIDATE_T("line", kObjectTypeString, lines.items[i].type, { - goto cleanup; + return; }); String line = lines.items[i].data.string; - reg->y_array[i] = xmemdupz(line.data, line.size); + reg->y_array[i] = arena_memdupz(arena, line.data, line.size); memchrsub(reg->y_array[i], NUL, NL, line.size); } @@ -1273,10 +1298,6 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, msg_silent--; VIsual_active = VIsual_was_active; }); - -cleanup: - free_register(reg); - xfree(reg); } /// Subscribes to event broadcasts. @@ -1334,14 +1355,13 @@ Integer nvim_get_color_by_name(String name) /// (e.g. 65535). /// /// @return Map of color names and RGB values. -Dictionary nvim_get_color_map(void) +Dictionary nvim_get_color_map(Arena *arena) FUNC_API_SINCE(1) { - Dictionary colors = ARRAY_DICT_INIT; + Dictionary colors = arena_dict(arena, ARRAY_SIZE(color_name_table)); for (int i = 0; color_name_table[i].name != NULL; i++) { - PUT(colors, color_name_table[i].name, - INTEGER_OBJ(color_name_table[i].color)); + PUT_C(colors, color_name_table[i].name, INTEGER_OBJ(color_name_table[i].color)); } return colors; } @@ -1354,7 +1374,7 @@ Dictionary nvim_get_color_map(void) /// @param[out] err Error details, if any /// /// @return map of global |context|. -Dictionary nvim_get_context(Dict(context) *opts, Error *err) +Dictionary nvim_get_context(Dict(context) *opts, Arena *arena, Error *err) FUNC_API_SINCE(6) { Array types = ARRAY_DICT_INIT; @@ -1390,7 +1410,7 @@ Dictionary nvim_get_context(Dict(context) *opts, Error *err) Context ctx = CONTEXT_INIT; ctx_save(&ctx, int_types); - Dictionary dict = ctx_to_dict(&ctx); + Dictionary dict = ctx_to_dict(&ctx, arena); ctx_free(&ctx); return dict; } @@ -1421,16 +1441,16 @@ Object nvim_load_context(Dictionary dict, Error *err) /// "blocking" is true if Nvim is waiting for input. /// /// @returns Dictionary { "mode": String, "blocking": Boolean } -Dictionary nvim_get_mode(void) +Dictionary nvim_get_mode(Arena *arena) FUNC_API_SINCE(2) FUNC_API_FAST { - Dictionary rv = ARRAY_DICT_INIT; - char modestr[MODE_MAX_LENGTH]; + Dictionary rv = arena_dict(arena, 2); + char *modestr = arena_alloc(arena, MODE_MAX_LENGTH, false); get_mode(modestr); bool blocked = input_blocking(); - PUT(rv, "mode", CSTR_TO_OBJ(modestr)); - PUT(rv, "blocking", BOOLEAN_OBJ(blocked)); + PUT_C(rv, "mode", CSTR_AS_OBJ(modestr)); + PUT_C(rv, "blocking", BOOLEAN_OBJ(blocked)); return rv; } @@ -1440,10 +1460,10 @@ Dictionary nvim_get_mode(void) /// @param mode Mode short-name ("n", "i", "v", ...) /// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key is always zero. -ArrayOf(Dictionary) nvim_get_keymap(String mode) +ArrayOf(Dictionary) nvim_get_keymap(String mode, Arena *arena) FUNC_API_SINCE(3) { - return keymap_array(mode, NULL); + return keymap_array(mode, NULL, arena); } /// Sets a global |mapping| for the given mode. @@ -1451,7 +1471,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode) /// To set a buffer-local mapping, use |nvim_buf_set_keymap()|. /// /// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs} or {rhs}. -/// Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual. +/// Empty {rhs} is [<Nop>]. |keycodes| are replaced as usual. /// /// Example: /// @@ -1471,7 +1491,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode) /// "ia", "ca" or "!a" for abbreviation in Insert mode, Cmdline mode, or both, respectively /// @param lhs Left-hand-side |{lhs}| of the mapping. /// @param rhs Right-hand-side |{rhs}| of the mapping. -/// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except |<buffer>|, +/// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except [<buffer>], /// values are booleans (default false). Also: /// - "noremap" disables |recursive_mapping|, like |:noremap| /// - "desc" human-readable description. @@ -1501,7 +1521,7 @@ void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err) /// Returns a 2-tuple (Array), where item 0 is the current channel id and item /// 1 is the |api-metadata| map (Dictionary). /// -/// @returns 2-tuple [{channel-id}, {api-metadata}] +/// @returns 2-tuple `[{channel-id}, {api-metadata}]` Array nvim_get_api_info(uint64_t channel_id, Arena *arena) FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_REMOTE_ONLY { @@ -1509,7 +1529,7 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) assert(channel_id <= INT64_MAX); ADD_C(rv, INTEGER_OBJ((int64_t)channel_id)); - ADD_C(rv, DICTIONARY_OBJ(api_metadata())); + ADD_C(rv, api_metadata()); return rv; } @@ -1529,14 +1549,14 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) /// @param channel_id /// @param name Short name for the connected client /// @param version Dictionary describing the version, with these -/// (optional) keys: +/// (optional) keys: /// - "major" major version (defaults to 0 if not set, for no release yet) /// - "minor" minor version /// - "patch" patch number /// - "prerelease" string describing a prerelease, like "dev" or "beta1" /// - "commit" hash or similar identifier of commit /// @param type Must be one of the following values. Client libraries should -/// default to "remote" unless overridden by the user. +/// default to "remote" unless overridden by the user. /// - "remote" remote client connected "Nvim flavored" MessagePack-RPC (responses /// must be in reverse order of requests). |msgpack-rpc| /// - "msgpack-rpc" remote client connected to Nvim via fully MessagePack-RPC @@ -1547,12 +1567,12 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) /// - "host" plugin host, typically started by nvim /// - "plugin" single plugin, started by nvim /// @param methods Builtin methods in the client. For a host, this does not -/// include plugin methods which will be discovered later. -/// The key should be the method name, the values are dicts with -/// these (optional) keys (more keys may be added in future -/// versions of Nvim, thus unknown keys are ignored. Clients -/// must only use keys defined in this or later versions of -/// Nvim): +/// include plugin methods which will be discovered later. +/// The key should be the method name, the values are dicts with +/// these (optional) keys (more keys may be added in future +/// versions of Nvim, thus unknown keys are ignored. Clients +/// must only use keys defined in this or later versions of +/// Nvim): /// - "async" if true, send as a notification. If false or unspecified, /// use a blocking request /// - "nargs" Number of arguments. Could be a single integer or an array @@ -1567,13 +1587,12 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) /// /// @param[out] err Error details, if any void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, String type, - Dictionary methods, Dictionary attributes, Error *err) + Dictionary methods, Dictionary attributes, Arena *arena, Error *err) FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY { - Dictionary info = ARRAY_DICT_INIT; - PUT(info, "name", copy_object(STRING_OBJ(name), NULL)); + MAXSIZE_TEMP_DICT(info, 5); + PUT_C(info, "name", STRING_OBJ(name)); - version = copy_dictionary(version, NULL); bool has_major = false; for (size_t i = 0; i < version.size; i++) { if (strequal(version.items[i].key.data, "major")) { @@ -1582,19 +1601,26 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, } } if (!has_major) { - PUT(version, "major", INTEGER_OBJ(0)); + Dictionary v = arena_dict(arena, version.size + 1); + if (version.size) { + memcpy(v.items, version.items, version.size * sizeof(v.items[0])); + v.size = version.size; + } + PUT_C(v, "major", INTEGER_OBJ(0)); + version = v; } - PUT(info, "version", DICTIONARY_OBJ(version)); + PUT_C(info, "version", DICTIONARY_OBJ(version)); - PUT(info, "type", copy_object(STRING_OBJ(type), NULL)); - PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods, NULL))); - PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes, NULL))); + PUT_C(info, "type", STRING_OBJ(type)); + PUT_C(info, "methods", DICTIONARY_OBJ(methods)); + PUT_C(info, "attributes", DICTIONARY_OBJ(attributes)); - rpc_set_client_info(channel_id, info); + rpc_set_client_info(channel_id, copy_dictionary(info, NULL)); } /// Gets information about a channel. /// +/// @param chan channel_id, or 0 for current channel /// @returns Dictionary describing a channel, with these keys: /// - "id" Channel id. /// - "argv" (optional) Job arguments list. @@ -1616,23 +1642,28 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, /// the RPC channel), if provided by it via /// |nvim_set_client_info()|. /// -Dictionary nvim_get_chan_info(Integer chan, Error *err) +Dictionary nvim_get_chan_info(uint64_t channel_id, Integer chan, Arena *arena, Error *err) FUNC_API_SINCE(4) { if (chan < 0) { return (Dictionary)ARRAY_DICT_INIT; } - return channel_info((uint64_t)chan); + + if (chan == 0 && !is_internal_call(channel_id)) { + assert(channel_id <= INT64_MAX); + chan = (Integer)channel_id; + } + return channel_info((uint64_t)chan, arena); } /// Get information about all open channels. /// /// @returns Array of Dictionaries, each describing a channel with /// the format specified at |nvim_get_chan_info()|. -Array nvim_list_chans(void) +Array nvim_list_chans(Arena *arena) FUNC_API_SINCE(4) { - return channel_all_info(); + return channel_all_info(arena); } /// Calls many API methods atomically. @@ -1698,7 +1729,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *er // directly here. But `result` might become invalid when next api function // is called in the loop. ADD_C(results, copy_object(result, arena)); - if (!handler.arena_return) { + if (handler.ret_alloc) { api_free_object(result); } } @@ -1778,9 +1809,9 @@ static void write_msg(String message, bool to_err, bool writeln) /// @param[in] obj Object to return. /// /// @return its argument. -Object nvim__id(Object obj) +Object nvim__id(Object obj, Arena *arena) { - return copy_object(obj, NULL); + return copy_object(obj, arena); } /// Returns array given as argument. @@ -1791,9 +1822,9 @@ Object nvim__id(Object obj) /// @param[in] arr Array to return. /// /// @return its argument. -Array nvim__id_array(Array arr) +Array nvim__id_array(Array arr, Arena *arena) { - return copy_array(arr, NULL); + return copy_array(arr, arena); } /// Returns dictionary given as argument. @@ -1804,9 +1835,9 @@ Array nvim__id_array(Array arr) /// @param[in] dct Dictionary to return. /// /// @return its argument. -Dictionary nvim__id_dictionary(Dictionary dct) +Dictionary nvim__id_dictionary(Dictionary dct, Arena *arena) { - return copy_dictionary(dct, NULL); + return copy_dictionary(dct, arena); } /// Returns floating-point value given as argument. @@ -1825,14 +1856,14 @@ Float nvim__id_float(Float flt) /// Gets internal stats. /// /// @return Map of various internal stats. -Dictionary nvim__stats(void) -{ - Dictionary rv = ARRAY_DICT_INIT; - PUT(rv, "fsync", INTEGER_OBJ(g_stats.fsync)); - PUT(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip)); - PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count())); - PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw)); - PUT(rv, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count)); +Dictionary nvim__stats(Arena *arena) +{ + Dictionary rv = arena_dict(arena, 5); + PUT_C(rv, "fsync", INTEGER_OBJ(g_stats.fsync)); + PUT_C(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip)); + PUT_C(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count())); + PUT_C(rv, "redraw", INTEGER_OBJ(g_stats.redraw)); + PUT_C(rv, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count)); return rv; } @@ -1844,16 +1875,16 @@ Dictionary nvim__stats(void) /// - "rgb" true if the UI uses RGB colors (false implies |cterm-colors|) /// - "ext_..." Requested UI extensions, see |ui-option| /// - "chan" |channel-id| of remote UI -Array nvim_list_uis(void) +Array nvim_list_uis(Arena *arena) FUNC_API_SINCE(4) { - return ui_array(); + return ui_array(arena); } /// Gets the immediate children of process `pid`. /// /// @return Array of child process ids, empty if process not found. -Array nvim_get_proc_children(Integer pid, Error *err) +Array nvim_get_proc_children(Integer pid, Arena *arena, Error *err) FUNC_API_SINCE(4) { Array rvobj = ARRAY_DICT_INIT; @@ -1869,8 +1900,8 @@ Array nvim_get_proc_children(Integer pid, Error *err) // syscall failed (possibly because of kernel options), try shelling out. DLOG("fallback to vim._os_proc_children()"); MAXSIZE_TEMP_ARRAY(a, 1); - ADD(a, INTEGER_OBJ(pid)); - Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, err); + ADD_C(a, INTEGER_OBJ(pid)); + Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, kRetObject, arena, err); if (o.type == kObjectTypeArray) { rvobj = o.data.array; } else if (!ERROR_SET(err)) { @@ -1878,11 +1909,11 @@ Array nvim_get_proc_children(Integer pid, Error *err) "Failed to get process children. pid=%" PRId64 " error=%d", pid, rv); } - goto end; - } - - for (size_t i = 0; i < proc_count; i++) { - ADD(rvobj, INTEGER_OBJ(proc_list[i])); + } else { + rvobj = arena_array(arena, proc_count); + for (size_t i = 0; i < proc_count; i++) { + ADD_C(rvobj, INTEGER_OBJ(proc_list[i])); + } } end: @@ -1893,19 +1924,17 @@ end: /// Gets info describing process `pid`. /// /// @return Map of process properties, or NIL if process not found. -Object nvim_get_proc(Integer pid, Error *err) +Object nvim_get_proc(Integer pid, Arena *arena, Error *err) FUNC_API_SINCE(4) { - Object rvobj = OBJECT_INIT; - rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; - rvobj.type = kObjectTypeDictionary; + Object rvobj = NIL; VALIDATE_INT((pid > 0 && pid <= INT_MAX), "pid", pid, { return NIL; }); #ifdef MSWIN - rvobj.data.dictionary = os_proc_info((int)pid); + rvobj = DICTIONARY_OBJ(os_proc_info((int)pid, arena)); if (rvobj.data.dictionary.size == 0) { // Process not found. return NIL; } @@ -1913,11 +1942,11 @@ Object nvim_get_proc(Integer pid, Error *err) // Cross-platform process info APIs are miserable, so use `ps` instead. MAXSIZE_TEMP_ARRAY(a, 1); ADD(a, INTEGER_OBJ(pid)); - Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, err); + Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, kRetObject, arena, err); if (o.type == kObjectTypeArray && o.data.array.size == 0) { return NIL; // Process not found. } else if (o.type == kObjectTypeDictionary) { - rvobj.data.dictionary = o.data.dictionary; + rvobj = o; } else if (!ERROR_SET(err)) { api_set_error(err, kErrorTypeException, "Failed to get process info. pid=%" PRId64, pid); @@ -1931,7 +1960,7 @@ Object nvim_get_proc(Integer pid, Error *err) /// If neither |ins-completion| nor |cmdline-completion| popup menu is active /// this API call is silently ignored. /// Useful for an external UI using |ui-popupmenu| to control the popup menu with the mouse. -/// Can also be used in a mapping; use <Cmd> |:map-cmd| or a Lua mapping to ensure the mapping +/// Can also be used in a mapping; use [<Cmd>] |:map-cmd| or a Lua mapping to ensure the mapping /// doesn't end completion mode. /// /// @param item Index (zero-based) of the item to select. Value of -1 selects nothing @@ -1941,14 +1970,10 @@ Object nvim_get_proc(Integer pid, Error *err) /// @param finish Finish the completion and dismiss the popup menu. Implies {insert}. /// @param opts Optional parameters. Reserved for future use. /// @param[out] err Error details, if any -void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Dictionary opts, +void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Dict(empty) *opts, Error *err) FUNC_API_SINCE(6) { - VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", { - return; - }); - if (finish) { insert = true; } @@ -1956,7 +1981,7 @@ void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Di pum_ext_select_item((int)item, insert, finish); } -/// NB: if your UI doesn't use hlstate, this will not return hlstate first time +/// NB: if your UI doesn't use hlstate, this will not return hlstate first time. Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, Error *err) { Array ret = ARRAY_DICT_INIT; @@ -1987,11 +2012,12 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, E ADD_C(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, arena, err))); // will not work first time if (!highlight_use_hlstate()) { - ADD_C(ret, ARRAY_OBJ(hl_inspect(attr))); + ADD_C(ret, ARRAY_OBJ(hl_inspect(attr, arena))); } return ret; } +/// @nodoc void nvim__screenshot(String path) FUNC_API_FAST { @@ -2006,10 +2032,11 @@ void nvim__invalidate_glyph_cache(void) must_redraw = UPD_CLEAR; } -Object nvim__unpack(String str, Error *err) +/// @nodoc +Object nvim__unpack(String str, Arena *arena, Error *err) FUNC_API_FAST { - return unpack(str.data, str.size, err); + return unpack(str.data, str.size, arena, err); } /// Deletes an uppercase/file named mark. See |mark-motions|. @@ -2049,7 +2076,7 @@ Boolean nvim_del_mark(String name, Error *err) /// not set. /// @see |nvim_buf_set_mark()| /// @see |nvim_del_mark()| -Array nvim_get_mark(String name, Dictionary opts, Error *err) +Array nvim_get_mark(String name, Dict(empty) *opts, Arena *arena, Error *err) FUNC_API_SINCE(8) { Array rv = ARRAY_DICT_INIT; @@ -2097,10 +2124,11 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err) col = pos.col; } - ADD(rv, INTEGER_OBJ(row)); - ADD(rv, INTEGER_OBJ(col)); - ADD(rv, INTEGER_OBJ(bufnr)); - ADD(rv, CSTR_TO_OBJ(filename)); + rv = arena_array(arena, 4); + ADD_C(rv, INTEGER_OBJ(row)); + ADD_C(rv, INTEGER_OBJ(col)); + ADD_C(rv, INTEGER_OBJ(bufnr)); + ADD_C(rv, CSTR_TO_ARENA_OBJ(arena, filename)); if (allocated) { xfree(filename); @@ -2132,15 +2160,14 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err) /// |Dictionary| with these keys: /// - start: (number) Byte index (0-based) of first character that uses the highlight. /// - group: (string) Name of highlight group. -Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *err) +Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena, Error *err) FUNC_API_SINCE(8) FUNC_API_FAST { Dictionary result = ARRAY_DICT_INIT; int maxwidth; - int fillchar = 0; + schar_T fillchar = 0; int statuscol_lnum = 0; - Window window = 0; if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) { const char *const errmsg = check_stl_option(str.data); @@ -2149,16 +2176,17 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * }); } - if (HAS_KEY(opts, eval_statusline, winid)) { - window = opts->winid; - } + Window window = opts->winid; + if (HAS_KEY(opts, eval_statusline, fillchar)) { VALIDATE_EXP((*opts->fillchar.data != 0 - && ((size_t)utf_ptr2len(opts->fillchar.data) == opts->fillchar.size)), + && ((size_t)utfc_ptr2len(opts->fillchar.data) == opts->fillchar.size)), "fillchar", "single character", NULL, { return result; }); - fillchar = utf_ptr2char(opts->fillchar.data); + int c; + fillchar = utfc_ptr2schar(opts->fillchar.data, &c); + // TODO(bfredl): actually check c is single width } int use_bools = (int)opts->use_winbar + (int)opts->use_tabline; @@ -2186,50 +2214,44 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * statuscol_T statuscol = { 0 }; SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 }; - if (opts->use_tabline) { - fillchar = ' '; - } else { - if (fillchar == 0) { - if (opts->use_winbar) { - fillchar = wp->w_p_fcs_chars.wbr; - } else { - int attr; - fillchar = fillchar_status(&attr, wp); + if (statuscol_lnum) { + int line_id = 0; + int cul_id = 0; + int num_id = 0; + linenr_T lnum = statuscol_lnum; + decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id); + + statuscol.sattrs = sattrs; + statuscol.foldinfo = fold_info(wp, lnum); + wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0; + + if (wp->w_p_cul) { + if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) { + wp->w_cursorline = statuscol.foldinfo.fi_lnum; } + statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR); } - if (statuscol_lnum) { - int line_id = 0; - int cul_id = 0; - int num_id = 0; - linenr_T lnum = statuscol_lnum; - wp->w_scwidth = win_signcol_count(wp); - decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id); - - statuscol.sattrs = sattrs; - statuscol.foldinfo = fold_info(wp, lnum); - wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0; - - if (wp->w_p_cul) { - if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) { - wp->w_cursorline = statuscol.foldinfo.fi_lnum; - } - statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR); - } - statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0; - if (num_id) { - stc_hl_id = num_id; - } else if (statuscol.use_cul) { - stc_hl_id = HLF_CLN + 1; - } else if (wp->w_p_rnu) { - stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB) + 1; - } else { - stc_hl_id = HLF_N + 1; - } + statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0; + if (num_id) { + stc_hl_id = num_id; + } else if (statuscol.use_cul) { + stc_hl_id = HLF_CLN + 1; + } else if (wp->w_p_rnu) { + stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB) + 1; + } else { + stc_hl_id = HLF_N + 1; + } - set_vim_var_nr(VV_LNUM, lnum); - set_vim_var_nr(VV_RELNUM, labs(get_cursor_rel_lnum(wp, lnum))); - set_vim_var_nr(VV_VIRTNUM, 0); + set_vim_var_nr(VV_LNUM, lnum); + set_vim_var_nr(VV_RELNUM, labs(get_cursor_rel_lnum(wp, lnum))); + set_vim_var_nr(VV_VIRTNUM, 0); + } else if (fillchar == 0 && !opts->use_tabline) { + if (opts->use_winbar) { + fillchar = wp->w_p_fcs_chars.wbr; + } else { + int attr; + fillchar = fillchar_status(&attr, wp); } } @@ -2242,70 +2264,66 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * && global_stl_height() > 0)) ? Columns : wp->w_width; } - char buf[MAXPATHL]; + result = arena_dict(arena, 3); + char *buf = arena_alloc(arena, MAXPATHL, false); stl_hlrec_t *hltab; + size_t hltab_len = 0; // Temporarily reset 'cursorbind' to prevent side effects from moving the cursor away and back. int p_crb_save = wp->w_p_crb; wp->w_p_crb = false; - int width = build_stl_str_hl(wp, - buf, - sizeof(buf), - str.data, - NULL, - 0, - fillchar, - maxwidth, - opts->highlights ? &hltab : NULL, - NULL, + int width = build_stl_str_hl(wp, buf, MAXPATHL, str.data, -1, 0, fillchar, maxwidth, + opts->highlights ? &hltab : NULL, &hltab_len, NULL, statuscol_lnum ? &statuscol : NULL); - PUT(result, "width", INTEGER_OBJ(width)); + PUT_C(result, "width", INTEGER_OBJ(width)); // Restore original value of 'cursorbind' wp->w_p_crb = p_crb_save; if (opts->highlights) { - Array hl_values = ARRAY_DICT_INIT; - const char *grpname; + Array hl_values = arena_array(arena, hltab_len + 1); char user_group[15]; // strlen("User") + strlen("2147483647") + NUL // If first character doesn't have a defined highlight, // add the default highlight at the beginning of the highlight list if (hltab->start == NULL || (hltab->start - buf) != 0) { - Dictionary hl_info = ARRAY_DICT_INIT; - grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id); + Dictionary hl_info = arena_dict(arena, 2); + const char *grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, + opts->use_winbar, stc_hl_id); - PUT(hl_info, "start", INTEGER_OBJ(0)); - PUT(hl_info, "group", CSTR_TO_OBJ(grpname)); + PUT_C(hl_info, "start", INTEGER_OBJ(0)); + PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname)); - ADD(hl_values, DICTIONARY_OBJ(hl_info)); + ADD_C(hl_values, DICTIONARY_OBJ(hl_info)); } for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) { - Dictionary hl_info = ARRAY_DICT_INIT; + Dictionary hl_info = arena_dict(arena, 2); - PUT(hl_info, "start", INTEGER_OBJ(sp->start - buf)); + PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf)); + const char *grpname; if (sp->userhl == 0) { grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id); } else if (sp->userhl < 0) { grpname = syn_id2name(-sp->userhl); } else { snprintf(user_group, sizeof(user_group), "User%d", sp->userhl); - grpname = user_group; + grpname = arena_memdupz(arena, user_group, strlen(user_group)); } - PUT(hl_info, "group", CSTR_TO_OBJ(grpname)); - ADD(hl_values, DICTIONARY_OBJ(hl_info)); + PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname)); + ADD_C(hl_values, DICTIONARY_OBJ(hl_info)); } - PUT(result, "highlights", ARRAY_OBJ(hl_values)); + PUT_C(result, "highlights", ARRAY_OBJ(hl_values)); } - PUT(result, "str", CSTR_TO_OBJ(buf)); + PUT_C(result, "str", CSTR_AS_OBJ(buf)); return result; } +/// @nodoc void nvim_error_event(uint64_t channel_id, Integer lvl, String data) FUNC_API_REMOTE_ONLY { @@ -2313,3 +2331,29 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data) // if we fork nvim processes as async workers ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : ""); } + +/// Set info for the completion candidate index. +/// if the info was shown in a window, then the +/// window and buffer ids are returned for further +/// customization. If the text was not shown, an +/// empty dict is returned. +/// +/// @param index the completion candidate index +/// @param opts Optional parameters. +/// - info: (string) info text. +/// @return Dictionary containing these keys: +/// - winid: (number) floating window id +/// - bufnr: (number) buffer id in floating window +Dictionary nvim_complete_set(Integer index, Dict(complete_set) *opts, Arena *arena) + FUNC_API_SINCE(12) +{ + Dictionary rv = arena_dict(arena, 2); + if (HAS_KEY(opts, complete_set, info)) { + win_T *wp = pum_set_info((int)index, opts->info.data); + if (wp) { + PUT_C(rv, "winid", WINDOW_OBJ(wp->handle)); + PUT_C(rv, "bufnr", BUFFER_OBJ(wp->w_buffer->handle)); + } + } + return rv; +} diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index c75bf21572..477cbe2428 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -11,18 +11,21 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/vimscript.h" #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/runtime.h" #include "nvim/vim_defs.h" #include "nvim/viml/parser/expressions.h" #include "nvim/viml/parser/parser.h" +#include "nvim/viml/parser/parser_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/vimscript.c.generated.h" @@ -48,7 +51,7 @@ /// @return Dictionary containing information about execution, with these keys: /// - output: (string|nil) Output if `opts.output` is true. Dictionary nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *err) - FUNC_API_SINCE(11) + FUNC_API_SINCE(11) FUNC_API_RET_ALLOC { Dictionary result = ARRAY_DICT_INIT; @@ -145,7 +148,7 @@ void nvim_command(String command, Error *err) /// @param expr Vimscript expression string /// @param[out] err Error details, if any /// @return Evaluation result or expanded object -Object nvim_eval(String expr, Error *err) +Object nvim_eval(String expr, Arena *arena, Error *err) FUNC_API_SINCE(1) { static int recursive = 0; // recursion depth @@ -176,7 +179,7 @@ Object nvim_eval(String expr, Error *err) api_set_error(err, kErrorTypeException, "Failed to evaluate expression: '%.*s'", 256, expr.data); } else { - rv = vim_to_object(&rettv); + rv = vim_to_object(&rettv, arena, false); } } @@ -193,7 +196,7 @@ Object nvim_eval(String expr, Error *err) /// @param self `self` dict, or NULL for non-dict functions /// @param[out] err Error details, if any /// @return Result of the function call -static Object _call_function(String fn, Array args, dict_T *self, Error *err) +static Object _call_function(String fn, Array args, dict_T *self, Arena *arena, Error *err) { static int recursive = 0; // recursion depth Object rv = OBJECT_INIT; @@ -208,9 +211,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) typval_T vim_args[MAX_FUNC_ARGS + 1]; size_t i = 0; // also used for freeing the variables for (; i < args.size; i++) { - if (!object_to_vim(args.items[i], &vim_args[i], err)) { - goto free_vim_args; - } + object_to_vim(args.items[i], &vim_args[i], err); } // Initialize `force_abort` and `suppress_errthrow` at the top level. @@ -233,18 +234,17 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) TRY_WRAP(err, { // call_func() retval is deceptive, ignore it. Instead we set `msg_list` // (see above) to capture abort-causing non-exception errors. - (void)call_func(fn.data, (int)fn.size, &rettv, (int)args.size, - vim_args, &funcexe); + call_func(fn.data, (int)fn.size, &rettv, (int)args.size, + vim_args, &funcexe); }); if (!ERROR_SET(err)) { - rv = vim_to_object(&rettv); + rv = vim_to_object(&rettv, arena, false); } tv_clear(&rettv); recursive--; -free_vim_args: while (i > 0) { tv_clear(&vim_args[--i]); } @@ -260,10 +260,10 @@ free_vim_args: /// @param args Function arguments packed in an Array /// @param[out] err Error details, if any /// @return Result of the function call -Object nvim_call_function(String fn, Array args, Error *err) +Object nvim_call_function(String fn, Array args, Arena *arena, Error *err) FUNC_API_SINCE(1) { - return _call_function(fn, args, NULL, err); + return _call_function(fn, args, NULL, arena, err); } /// Calls a Vimscript |Dictionary-function| with the given arguments. @@ -275,7 +275,7 @@ Object nvim_call_function(String fn, Array args, Error *err) /// @param args Function arguments packed in an Array /// @param[out] err Error details, if any /// @return Result of the function call -Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err) +Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena, Error *err) FUNC_API_SINCE(4) { Object rv = OBJECT_INIT; @@ -298,9 +298,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err) mustfree = true; break; case kObjectTypeDictionary: - if (!object_to_vim(dict, &rettv, err)) { - goto end; - } + object_to_vim(dict, &rettv, err); break; default: api_set_error(err, kErrorTypeValidation, @@ -339,7 +337,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err) goto end; } - rv = _call_function(fn, args, self_dict, err); + rv = _call_function(fn, args, self_dict, arena, err); end: if (mustfree) { tv_clear(&rettv); @@ -353,9 +351,7 @@ typedef struct { Object *ret_node_p; } ExprASTConvStackItem; -/// @cond DOXYGEN_NOT_A_FUNCTION typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; -/// @endcond /// Parse a Vimscript expression. /// @@ -369,8 +365,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "l" when needing to start parsing with lvalues for /// ":let" or ":for". /// Common flag sets: -/// - "m" to parse like for ":echo". -/// - "E" to parse like for "<C-r>=". +/// - "m" to parse like for `":echo"`. +/// - "E" to parse like for `"<C-r>="`. /// - empty string for ":call". /// - "lm" to parse for ":let". /// @param[in] highlight If true, return value will also include "highlight" @@ -389,12 +385,12 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "arg": String, error message argument. /// - "len": Amount of bytes successfully parsed. With flags equal to "" /// that should be equal to the length of expr string. -/// (“Successfully parsed” here means “participated in AST -/// creation”, not “till the first error”.) +/// ("Successfully parsed" here means "participated in AST +/// creation", not "till the first error".) /// - "ast": AST, either nil or a dictionary with these keys: /// - "type": node type, one of the value names from ExprASTNodeType /// stringified without "kExprNode" prefix. -/// - "start": a pair [line, column] describing where node is "started" +/// - "start": a pair `[line, column]` describing where node is "started" /// where "line" is always 0 (will not be 0 if you will be /// using this API on e.g. ":let", but that is not /// present yet). Both elements are Integers. @@ -431,7 +427,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "svalue": String, value for "SingleQuotedString" and /// "DoubleQuotedString" nodes. /// @param[out] err Error details, if any -Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, Error *err) +Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, Arena *arena, + Error *err) FUNC_API_SINCE(4) FUNC_API_FAST { int pflags = 0; @@ -473,82 +470,40 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E + (size_t)(east.err.msg != NULL) // "error" + (size_t)highlight // "highlight" + 0); - Dictionary ret = { - .items = xmalloc(ret_size * sizeof(ret.items[0])), - .size = 0, - .capacity = ret_size, - }; - ret.items[ret.size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ast"), - .value = NIL, - }; - ret.items[ret.size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("len"), - .value = INTEGER_OBJ((Integer)(pstate.pos.line == 1 - ? parser_lines[0].size - : pstate.pos.col)), - }; + + Dictionary ret = arena_dict(arena, ret_size); + PUT_C(ret, "len", INTEGER_OBJ((Integer)(pstate.pos.line == 1 + ? parser_lines[0].size + : pstate.pos.col))); if (east.err.msg != NULL) { - Dictionary err_dict = { - .items = xmalloc(2 * sizeof(err_dict.items[0])), - .size = 2, - .capacity = 2, - }; - err_dict.items[0] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("message"), - .value = CSTR_TO_OBJ(east.err.msg), - }; - if (east.err.arg == NULL) { - err_dict.items[1] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("arg"), - .value = STRING_OBJ(STRING_INIT), - }; - } else { - err_dict.items[1] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("arg"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(east.err.arg, (size_t)east.err.arg_len), - .size = (size_t)east.err.arg_len, - })), - }; - } - ret.items[ret.size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("error"), - .value = DICTIONARY_OBJ(err_dict), - }; + Dictionary err_dict = arena_dict(arena, 2); + PUT_C(err_dict, "message", CSTR_TO_ARENA_OBJ(arena, east.err.msg)); + PUT_C(err_dict, "arg", CBUF_TO_ARENA_OBJ(arena, east.err.arg, (size_t)east.err.arg_len)); + PUT_C(ret, "error", DICTIONARY_OBJ(err_dict)); } if (highlight) { - Array hl = (Array) { - .items = xmalloc(kv_size(colors) * sizeof(hl.items[0])), - .capacity = kv_size(colors), - .size = kv_size(colors), - }; + Array hl = arena_array(arena, kv_size(colors)); for (size_t i = 0; i < kv_size(colors); i++) { const ParserHighlightChunk chunk = kv_A(colors, i); - Array chunk_arr = (Array) { - .items = xmalloc(4 * sizeof(chunk_arr.items[0])), - .capacity = 4, - .size = 4, - }; - chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line); - chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col); - chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col); - chunk_arr.items[3] = CSTR_TO_OBJ(chunk.group); - hl.items[i] = ARRAY_OBJ(chunk_arr); + Array chunk_arr = arena_array(arena, 4); + ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.start.line)); + ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.start.col)); + ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.end_col)); + ADD_C(chunk_arr, CSTR_AS_OBJ(chunk.group)); + + ADD_C(hl, ARRAY_OBJ(chunk_arr)); } - ret.items[ret.size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("highlight"), - .value = ARRAY_OBJ(hl), - }; + PUT_C(ret, "highlight", ARRAY_OBJ(hl)); } kvi_destroy(colors); // Walk over the AST, freeing nodes in process. ExprASTConvStack ast_conv_stack; kvi_init(ast_conv_stack); + Object ast = NIL; kvi_push(ast_conv_stack, ((ExprASTConvStackItem) { .node_p = &east.root, - .ret_node_p = &ret.items[0].value, + .ret_node_p = &ast, })); while (kv_size(ast_conv_stack)) { ExprASTConvStackItem cur_item = kv_last(ast_conv_stack); @@ -575,28 +530,17 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E || node->type == kExprNodeSingleQuotedString) // "svalue" + (node->type == kExprNodeAssignment) // "augmentation" + 0); - Dictionary ret_node = { - .items = xmalloc(items_size * sizeof(ret_node.items[0])), - .capacity = items_size, - .size = 0, - }; + Dictionary ret_node = arena_dict(arena, items_size); *cur_item.ret_node_p = DICTIONARY_OBJ(ret_node); } Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary; if (node->children != NULL) { const size_t num_children = 1 + (node->children->next != NULL); - Array children_array = { - .items = xmalloc(num_children * sizeof(children_array.items[0])), - .capacity = num_children, - .size = num_children, - }; + Array children_array = arena_array(arena, num_children); for (size_t i = 0; i < num_children; i++) { - children_array.items[i] = NIL; + ADD_C(children_array, NIL); } - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("children"), - .value = ARRAY_OBJ(children_array), - }; + PUT_C(*ret_node, "children", ARRAY_OBJ(children_array)); kvi_push(ast_conv_stack, ((ExprASTConvStackItem) { .node_p = &node->children, .ret_node_p = &children_array.items[0], @@ -608,126 +552,60 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E })); } else { kv_drop(ast_conv_stack, 1); - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("type"), - .value = CSTR_TO_OBJ(east_node_type_tab[node->type]), - }; - Array start_array = { - .items = xmalloc(2 * sizeof(start_array.items[0])), - .capacity = 2, - .size = 2, - }; - start_array.items[0] = INTEGER_OBJ((Integer)node->start.line); - start_array.items[1] = INTEGER_OBJ((Integer)node->start.col); - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("start"), - .value = ARRAY_OBJ(start_array), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("len"), - .value = INTEGER_OBJ((Integer)node->len), - }; + PUT_C(*ret_node, "type", CSTR_AS_OBJ(east_node_type_tab[node->type])); + Array start_array = arena_array(arena, 2); + ADD_C(start_array, INTEGER_OBJ((Integer)node->start.line)); + ADD_C(start_array, INTEGER_OBJ((Integer)node->start.col)); + PUT_C(*ret_node, "start", ARRAY_OBJ(start_array)); + + PUT_C(*ret_node, "len", INTEGER_OBJ((Integer)node->len)); switch (node->type) { case kExprNodeDoubleQuotedString: - case kExprNodeSingleQuotedString: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("svalue"), - .value = STRING_OBJ(((String) { - .data = node->data.str.value, - .size = node->data.str.size, - })), - }; + case kExprNodeSingleQuotedString: { + Object str = CBUF_TO_ARENA_OBJ(arena, node->data.str.value, node->data.str.size); + PUT_C(*ret_node, "svalue", str); + xfree(node->data.str.value); break; + } case kExprNodeOption: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("scope"), - .value = INTEGER_OBJ(node->data.opt.scope), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ident"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(node->data.opt.ident, - node->data.opt.ident_len), - .size = node->data.opt.ident_len, - })), - }; + PUT_C(*ret_node, "scope", INTEGER_OBJ(node->data.opt.scope)); + PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.opt.ident, + node->data.opt.ident_len)); break; case kExprNodePlainIdentifier: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("scope"), - .value = INTEGER_OBJ(node->data.var.scope), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ident"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(node->data.var.ident, - node->data.var.ident_len), - .size = node->data.var.ident_len, - })), - }; + PUT_C(*ret_node, "scope", INTEGER_OBJ(node->data.var.scope)); + PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.var.ident, + node->data.var.ident_len)); break; case kExprNodePlainKey: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ident"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(node->data.var.ident, - node->data.var.ident_len), - .size = node->data.var.ident_len, - })), - }; + PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.var.ident, + node->data.var.ident_len)); break; case kExprNodeEnvironment: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ident"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(node->data.env.ident, - node->data.env.ident_len), - .size = node->data.env.ident_len, - })), - }; + PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.env.ident, + node->data.env.ident_len)); break; case kExprNodeRegister: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("name"), - .value = INTEGER_OBJ(node->data.reg.name), - }; + PUT_C(*ret_node, "name", INTEGER_OBJ(node->data.reg.name)); break; case kExprNodeComparison: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("cmp_type"), - .value = CSTR_TO_OBJ(eltkn_cmp_type_tab[node->data.cmp.type]), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ccs_strategy"), - .value = CSTR_TO_OBJ(ccs_tab[node->data.cmp.ccs]), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("invert"), - .value = BOOLEAN_OBJ(node->data.cmp.inv), - }; + PUT_C(*ret_node, "cmp_type", CSTR_AS_OBJ(eltkn_cmp_type_tab[node->data.cmp.type])); + PUT_C(*ret_node, "ccs_strategy", CSTR_AS_OBJ(ccs_tab[node->data.cmp.ccs])); + PUT_C(*ret_node, "invert", BOOLEAN_OBJ(node->data.cmp.inv)); break; case kExprNodeFloat: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("fvalue"), - .value = FLOAT_OBJ(node->data.flt.value), - }; + PUT_C(*ret_node, "fvalue", FLOAT_OBJ(node->data.flt.value)); break; case kExprNodeInteger: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ivalue"), - .value = INTEGER_OBJ((Integer)(node->data.num.value > API_INTEGER_MAX - ? API_INTEGER_MAX - : (Integer)node->data.num.value)), - }; + PUT_C(*ret_node, "ivalue", INTEGER_OBJ((Integer)(node->data.num.value > API_INTEGER_MAX + ? API_INTEGER_MAX + : (Integer)node->data.num.value))); break; case kExprNodeAssignment: { const ExprAssignmentType asgn_type = node->data.ass.type; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("augmentation"), - .value = STRING_OBJ(asgn_type == kExprAsgnPlain - ? (String)STRING_INIT - : cstr_to_string(expr_asgn_type_tab[asgn_type])), - }; + String str = (asgn_type == kExprAsgnPlain + ? (String)STRING_INIT : cstr_as_string(expr_asgn_type_tab[asgn_type])); + PUT_C(*ret_node, "augmentation", STRING_OBJ(str)); break; } case kExprNodeMissing: @@ -768,6 +646,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E } } kvi_destroy(ast_conv_stack); + PUT_C(ret, "ast", ast); assert(ret.size == ret.capacity); // Should be a no-op actually, leaving it in case non-nodes will need to be diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 4e23717dc6..543c7b8113 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -7,24 +7,33 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/tabpage.h" #include "nvim/api/win_config.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer_defs.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/drawscreen.h" -#include "nvim/func_attr.h" +#include "nvim/eval/window.h" +#include "nvim/extmark_defs.h" #include "nvim/globals.h" -#include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight_group.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/pos_defs.h" #include "nvim/strings.h" #include "nvim/syntax.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_compositor.h" +#include "nvim/ui_defs.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" #include "nvim/winfloat.h" @@ -32,9 +41,9 @@ # include "api/win_config.c.generated.h" #endif -/// Open a new window. +/// Opens a new split window, or a floating window if `relative` is specified, +/// or an external window (managed by the UI) if `external` is specified. /// -/// Currently this is used to open floating and external windows. /// Floats are windows that are drawn above the split layout, at some anchor /// position in some other window. Floats can be drawn internally or by external /// GUI with the |ui-multigrid| extension. External windows are only supported @@ -42,8 +51,17 @@ /// /// For a general overview of floats, see |api-floatwin|. /// -/// Exactly one of `external` and `relative` must be specified. The `width` and -/// `height` of the new window must be specified. +/// The `width` and `height` of the new window must be specified when opening +/// a floating window, but are optional for normal windows. +/// +/// If `relative` and `external` are omitted, a normal "split" window is created. +/// The `win` property determines which window will be split. If no `win` is +/// provided or `win == 0`, a window will be created adjacent to the current window. +/// If -1 is provided, a top-level split will be created. `vertical` and `split` are +/// only valid for normal windows, and are used to control split direction. For `vertical`, +/// the exact direction is determined by |'splitright'| and |'splitbelow'|. +/// Split windows cannot have `bufpos`/`row`/`col`/`border`/`title`/`footer` +/// properties. /// /// With relative=editor (row=0,col=0) refers to the top-left corner of the /// screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right @@ -68,6 +86,14 @@ /// ```lua /// vim.api.nvim_open_win(0, false, /// {relative='win', width=12, height=3, bufpos={100,10}}) +/// ``` +/// +/// Example (Lua): vertical split left of the current window +/// +/// ```lua +/// vim.api.nvim_open_win(0, false, { +/// split = 'left', +/// win = 0 /// }) /// ``` /// @@ -80,7 +106,8 @@ /// - "win" Window given by the `win` field, or current window. /// - "cursor" Cursor position in current window. /// - "mouse" Mouse position -/// - win: |window-ID| for relative="win". +/// - win: |window-ID| window to split, or relative window when creating a +/// float (relative="win"). /// - anchor: Decides which corner of the float to place at (row,col): /// - "NW" northwest (default) /// - "NE" northeast @@ -89,12 +116,12 @@ /// - width: Window width (in character cells). Minimum of 1. /// - height: Window height (in character cells). Minimum of 1. /// - bufpos: Places float relative to buffer text (only when -/// relative="win"). Takes a tuple of zero-indexed [line, column]. -/// `row` and `col` if given are applied relative to this -/// position, else they default to: -/// - `row=1` and `col=0` if `anchor` is "NW" or "NE" -/// - `row=0` and `col=0` if `anchor` is "SW" or "SE" -/// (thus like a tooltip near the buffer text). +/// relative="win"). Takes a tuple of zero-indexed `[line, column]`. +/// `row` and `col` if given are applied relative to this +/// position, else they default to: +/// - `row=1` and `col=0` if `anchor` is "NW" or "NE" +/// - `row=0` and `col=0` if `anchor` is "SW" or "SE" +/// (thus like a tooltip near the buffer text). /// - row: Row position in units of "screen cell height", may be fractional. /// - col: Column position in units of "screen cell width", may be /// fractional. @@ -126,7 +153,7 @@ /// 'fillchars' to a space char, and clearing the /// |hl-EndOfBuffer| region in 'winhighlight'. /// - border: Style of (optional) window border. This can either be a string -/// or an array. The string values are +/// or an array. The string values are /// - "none": No border (default). /// - "single": A single line box. /// - "double": A double line box. @@ -134,21 +161,31 @@ /// - "solid": Adds padding by a single whitespace cell. /// - "shadow": A drop shadow effect by blending with the background. /// - If it is an array, it should have a length of eight or any divisor of -/// eight. The array will specify the eight chars building up the border -/// in a clockwise fashion starting with the top-left corner. As an -/// example, the double box style could be specified as +/// eight. The array will specify the eight chars building up the border +/// in a clockwise fashion starting with the top-left corner. As an +/// example, the double box style could be specified as: +/// ``` /// [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]. -/// If the number of chars are less than eight, they will be repeated. Thus -/// an ASCII border could be specified as +/// ``` +/// If the number of chars are less than eight, they will be repeated. Thus +/// an ASCII border could be specified as +/// ``` /// [ "/", "-", \"\\\\\", "|" ], -/// or all chars the same as +/// ``` +/// or all chars the same as +/// ``` /// [ "x" ]. +/// ``` /// An empty string can be used to turn off a specific border, for instance, +/// ``` /// [ "", "", "", ">", "", "", "", "<" ] +/// ``` /// will only make vertical borders but not horizontal ones. /// By default, `FloatBorder` highlight is used, which links to `WinSeparator` /// when not defined. It could also be specified by character: +/// ``` /// [ ["+", "MyCorner"], ["x", "MyBorder"] ]. +/// ``` /// - title: Title (optional) in window border, string or list. /// List should consist of `[text, highlight]` tuples. /// If string, the default highlight group is `FloatTitle`. @@ -167,43 +204,90 @@ /// - fixed: If true when anchor is NW or SW, the float window /// would be kept fixed even if the window would be truncated. /// - hide: If true the floating window will be hidden. +/// - vertical: Split vertically |:vertical|. +/// - split: Split direction: "left", "right", "above", "below". /// /// @param[out] err Error details, if any /// /// @return Window handle, or 0 on error -Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, Error *err) - FUNC_API_SINCE(6) - FUNC_API_TEXTLOCK_ALLOW_CMDWIN +Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Error *err) + FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { +#define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key) buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { return 0; } - if (cmdwin_type != 0 && (enter || buf == curbuf)) { + if ((cmdwin_type != 0 && enter) || buf == cmdwin_buf) { api_set_error(err, kErrorTypeException, "%s", e_cmdwin); return 0; } - FloatConfig fconfig = FLOAT_CONFIG_INIT; + WinConfig fconfig = WIN_CONFIG_INIT; if (!parse_float_config(config, &fconfig, false, true, err)) { return 0; } - win_T *wp = win_new_float(NULL, false, fconfig, err); + + bool is_split = HAS_KEY_X(config, split) || HAS_KEY_X(config, vertical); + + win_T *wp = NULL; + tabpage_T *tp = curtab; + if (is_split) { + win_T *parent = NULL; + if (config->win != -1) { + parent = find_window_by_handle(fconfig.window, err); + if (!parent) { + // find_window_by_handle has already set the error + return 0; + } else if (parent->w_floating) { + api_set_error(err, kErrorTypeException, "Cannot split a floating window"); + return 0; + } + } + + if (HAS_KEY_X(config, vertical) && !HAS_KEY_X(config, split)) { + if (config->vertical) { + fconfig.split = p_spr ? kWinSplitRight : kWinSplitLeft; + } else { + fconfig.split = p_sb ? kWinSplitBelow : kWinSplitAbove; + } + } + int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER; + + if (parent == NULL) { + wp = win_split_ins(0, flags, NULL, 0); + } else { + tp = win_find_tabpage(parent); + switchwin_T switchwin; + // `parent` is valid in `tp`, so switch_win should not fail. + const int result = switch_win(&switchwin, parent, tp, true); + (void)result; + assert(result == OK); + wp = win_split_ins(0, flags, NULL, 0); + restore_win(&switchwin, true); + } + if (wp) { + wp->w_config = fconfig; + } + } else { + wp = win_new_float(NULL, false, fconfig, err); + } if (!wp) { + api_set_error(err, kErrorTypeException, "Failed to create window"); return 0; } + switchwin_T switchwin; + if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { + apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); + } + restore_win_noblock(&switchwin, true); if (enter) { - win_enter(wp, false); + goto_tabpage_win(tp, wp); } - // autocmds in win_enter or win_set_buf below may close the window - if (win_valid(wp) && buffer > 0) { - Boolean noautocmd = !enter || fconfig.noautocmd; - win_set_buf(wp, buf, noautocmd, err); - if (!fconfig.noautocmd) { - apply_autocmds(EVENT_WINNEW, NULL, NULL, false, buf); - } + if (win_valid_any_tab(wp) && buf != wp->w_buffer) { + win_set_buf(wp, buf, !enter || fconfig.noautocmd, err); } - if (!win_valid(wp)) { + if (!win_valid_any_tab(wp)) { api_set_error(err, kErrorTypeException, "Window was closed immediately"); return 0; } @@ -213,6 +297,37 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E didset_window_options(wp, true); } return wp->handle; +#undef HAS_KEY_X +} + +static WinSplit win_split_dir(win_T *win) +{ + if (win->w_frame == NULL || win->w_frame->fr_parent == NULL) { + return kWinSplitLeft; + } + + char layout = win->w_frame->fr_parent->fr_layout; + if (layout == FR_COL) { + return win->w_frame->fr_next ? kWinSplitAbove : kWinSplitBelow; + } else { + return win->w_frame->fr_next ? kWinSplitLeft : kWinSplitRight; + } +} + +static int win_split_flags(WinSplit split, bool toplevel) +{ + int flags = 0; + if (split == kWinSplitAbove || split == kWinSplitBelow) { + flags |= WSP_HOR; + } else { + flags |= WSP_VERT; + } + if (split == kWinSplitAbove || split == kWinSplitLeft) { + flags |= toplevel ? WSP_TOP : WSP_ABOVE; + } else { + flags |= toplevel ? WSP_BOT : WSP_BELOW; + } + return flags; } /// Configures window layout. Currently only for floating and external windows @@ -227,61 +342,231 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E /// @param config Map defining the window configuration, /// see |nvim_open_win()| /// @param[out] err Error details, if any -void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) +void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) FUNC_API_SINCE(6) { +#define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key) win_T *win = find_window_by_handle(window, err); if (!win) { return; } - bool new_float = !win->w_floating; + tabpage_T *win_tp = win_find_tabpage(win); + bool was_split = !win->w_floating; + bool has_split = HAS_KEY_X(config, split); + bool has_vertical = HAS_KEY_X(config, vertical); // reuse old values, if not overridden - FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config; + WinConfig fconfig = win->w_config; - if (!parse_float_config(config, &fconfig, !new_float, false, err)) { + bool to_split = config->relative.size == 0 + && !(HAS_KEY_X(config, external) ? config->external : fconfig.external) + && (has_split || has_vertical || was_split); + + if (!parse_float_config(config, &fconfig, !was_split || to_split, false, err)) { return; } - if (new_float) { + if (was_split && !to_split) { if (!win_new_float(win, false, fconfig, err)) { return; } redraw_later(win, UPD_NOT_VALID); + } else if (to_split) { + win_T *parent = NULL; + if (config->win != -1) { + parent = find_window_by_handle(fconfig.window, err); + if (!parent) { + return; + } else if (parent->w_floating) { + api_set_error(err, kErrorTypeException, "Cannot split a floating window"); + return; + } + } + + WinSplit old_split = win_split_dir(win); + if (has_vertical && !has_split) { + if (config->vertical) { + if (old_split == kWinSplitRight || p_spr) { + fconfig.split = kWinSplitRight; + } else { + fconfig.split = kWinSplitLeft; + } + } else { + if (old_split == kWinSplitBelow || p_sb) { + fconfig.split = kWinSplitBelow; + } else { + fconfig.split = kWinSplitAbove; + } + } + } + win->w_config = fconfig; + + // If there's no "vertical" or "split" set, or if "split" is unchanged, + // then we can just change the size of the window. + if ((!has_vertical && !has_split) + || (was_split && !HAS_KEY_X(config, win) && old_split == fconfig.split)) { + if (HAS_KEY_X(config, width)) { + win_setwidth_win(fconfig.width, win); + } + if (HAS_KEY_X(config, height)) { + win_setheight_win(fconfig.height, win); + } + redraw_later(win, UPD_NOT_VALID); + return; + } + + if (was_split) { + win_T *new_curwin = NULL; + + // If the window is the last in the tabpage or `fconfig.win` is + // a handle to itself, we can't split it. + if (win->w_frame->fr_parent == NULL) { + // FIXME(willothy): if the window is the last in the tabpage but there is another tabpage + // and the target window is in that other tabpage, should we move the window to that + // tabpage and close the previous one, or just error? + api_set_error(err, kErrorTypeValidation, "Cannot move last window"); + return; + } else if (parent != NULL && parent->handle == win->handle) { + int n_frames = 0; + for (frame_T *fr = win->w_frame->fr_parent->fr_child; fr != NULL; fr = fr->fr_next) { + n_frames++; + } + + win_T *neighbor = NULL; + + if (n_frames > 2) { + // There are three or more windows in the frame, we need to split a neighboring window. + frame_T *frame = win->w_frame->fr_parent; + + if (frame->fr_parent) { + // ┌──────────────┐ + // │ A │ + // ├────┬────┬────┤ + // │ B │ C │ D │ + // └────┴────┴────┘ + // || + // \/ + // ┌───────────────────┐ + // │ A │ + // ├─────────┬─────────┤ + // │ │ C │ + // │ B ├─────────┤ + // │ │ D │ + // └─────────┴─────────┘ + if (fconfig.split == kWinSplitAbove || fconfig.split == kWinSplitLeft) { + neighbor = win->w_next; + } else { + neighbor = win->w_prev; + } + } + // If the frame doesn't have a parent, the old frame + // was the root frame and we need to create a top-level split. + int dir; + new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); + } else if (n_frames == 2) { + // There are two windows in the frame, we can just rotate it. + int dir; + neighbor = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); + new_curwin = neighbor; + } else { + // There is only one window in the frame, we can't split it. + api_set_error(err, kErrorTypeValidation, "Cannot split window into itself"); + return; + } + // Set the parent to whatever the correct + // neighbor window was determined to be. + parent = neighbor; + } else { + int dir; + new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); + } + // move to neighboring window if we're moving the current window to a new tabpage + if (curwin == win && parent != NULL && new_curwin != NULL + && win_tp != win_find_tabpage(parent)) { + win_enter(new_curwin, true); + } + win_remove(win, win_tp == curtab ? NULL : win_tp); + } else { + win_remove(win, win_tp == curtab ? NULL : win_tp); + ui_comp_remove_grid(&win->w_grid_alloc); + if (win->w_config.external) { + for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { + if (tp == curtab) { + continue; + } + if (tp->tp_curwin == win) { + tp->tp_curwin = tp->tp_firstwin; + } + } + } + win->w_pos_changed = true; + } + + int flags = win_split_flags(fconfig.split, parent == NULL); + + if (parent == NULL) { + if (!win_split_ins(0, flags, win, 0)) { + // TODO(willothy): What should this error message say? + api_set_error(err, kErrorTypeException, "Failed to split window"); + return; + } + } else { + win_execute_T args; + + tabpage_T *tp = win_find_tabpage(parent); + if (!win_execute_before(&args, parent, tp)) { + // TODO(willothy): how should we handle this / what should the message be? + api_set_error(err, kErrorTypeException, "Failed to switch to tabpage %d", tp->handle); + win_execute_after(&args); + return; + } + // This should return the same ptr to `win`, but we check for + // NULL to detect errors. + win_T *res = win_split_ins(0, flags, win, 0); + win_execute_after(&args); + if (!res) { + // TODO(willothy): What should this error message say? + api_set_error(err, kErrorTypeException, "Failed to split window"); + return; + } + } + if (HAS_KEY_X(config, width)) { + win_setwidth_win(fconfig.width, win); + } + if (HAS_KEY_X(config, height)) { + win_setheight_win(fconfig.height, win); + } + redraw_later(win, UPD_NOT_VALID); + return; } else { win_config_float(win, fconfig); win->w_pos_changed = true; } - if (HAS_KEY(config, float_config, style)) { + if (HAS_KEY_X(config, style)) { if (fconfig.style == kWinStyleMinimal) { win_set_minimal_style(win); didset_window_options(win, true); } } +#undef HAS_KEY_X } -static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, - BorderTextType bordertext_type) +#define PUT_KEY_X(d, key, value) PUT_KEY(d, win_config, key, value) +static void config_put_bordertext(Dict(win_config) *config, WinConfig *fconfig, + BorderTextType bordertext_type, Arena *arena) { VirtText vt; AlignTextPos align; - char *field_name; - char *field_pos_name; switch (bordertext_type) { case kBorderTextTitle: vt = fconfig->title_chunks; align = fconfig->title_pos; - field_name = "title"; - field_pos_name = "title_pos"; break; case kBorderTextFooter: vt = fconfig->footer_chunks; align = fconfig->footer_pos; - field_name = "footer"; - field_pos_name = "footer_pos"; break; } - Array bordertext = virt_text_to_array(vt, true); - PUT(config, field_name, ARRAY_OBJ(bordertext)); + Array bordertext = virt_text_to_array(vt, true, arena); char *pos; switch (align) { @@ -295,9 +580,16 @@ static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, pos = "right"; break; } - PUT(config, field_pos_name, CSTR_TO_OBJ(pos)); - return config; + switch (bordertext_type) { + case kBorderTextTitle: + PUT_KEY_X(*config, title, ARRAY_OBJ(bordertext)); + PUT_KEY_X(*config, title_pos, cstr_as_string(pos)); + break; + case kBorderTextFooter: + PUT_KEY_X(*config, footer, ARRAY_OBJ(bordertext)); + PUT_KEY_X(*config, footer_pos, cstr_as_string(pos)); + } } /// Gets window configuration. @@ -309,70 +601,80 @@ static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return Map defining the window configuration, see |nvim_open_win()| -Dictionary nvim_win_get_config(Window window, Error *err) +Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err) FUNC_API_SINCE(6) { - Dictionary rv = ARRAY_DICT_INIT; + /// Keep in sync with FloatRelative in buffer_defs.h + static const char *const float_relative_str[] = { "editor", "win", "cursor", "mouse" }; + + /// Keep in sync with WinSplit in buffer_defs.h + static const char *const win_split_str[] = { "left", "right", "above", "below" }; + + Dict(win_config) rv = KEYDICT_INIT; win_T *wp = find_window_by_handle(window, err); if (!wp) { return rv; } - FloatConfig *config = &wp->w_float_config; + WinConfig *config = &wp->w_config; - PUT(rv, "focusable", BOOLEAN_OBJ(config->focusable)); - PUT(rv, "external", BOOLEAN_OBJ(config->external)); - PUT(rv, "hide", BOOLEAN_OBJ(config->hide)); + PUT_KEY_X(rv, focusable, config->focusable); + PUT_KEY_X(rv, external, config->external); + PUT_KEY_X(rv, hide, config->hide); if (wp->w_floating) { - PUT(rv, "width", INTEGER_OBJ(config->width)); - PUT(rv, "height", INTEGER_OBJ(config->height)); + PUT_KEY_X(rv, width, config->width); + PUT_KEY_X(rv, height, config->height); if (!config->external) { if (config->relative == kFloatRelativeWindow) { - PUT(rv, "win", INTEGER_OBJ(config->window)); + PUT_KEY_X(rv, win, config->window); if (config->bufpos.lnum >= 0) { - Array pos = ARRAY_DICT_INIT; - ADD(pos, INTEGER_OBJ(config->bufpos.lnum)); - ADD(pos, INTEGER_OBJ(config->bufpos.col)); - PUT(rv, "bufpos", ARRAY_OBJ(pos)); + Array pos = arena_array(arena, 2); + ADD_C(pos, INTEGER_OBJ(config->bufpos.lnum)); + ADD_C(pos, INTEGER_OBJ(config->bufpos.col)); + PUT_KEY_X(rv, bufpos, pos); } } - PUT(rv, "anchor", CSTR_TO_OBJ(float_anchor_str[config->anchor])); - PUT(rv, "row", FLOAT_OBJ(config->row)); - PUT(rv, "col", FLOAT_OBJ(config->col)); - PUT(rv, "zindex", INTEGER_OBJ(config->zindex)); + PUT_KEY_X(rv, anchor, cstr_as_string(float_anchor_str[config->anchor])); + PUT_KEY_X(rv, row, config->row); + PUT_KEY_X(rv, col, config->col); + PUT_KEY_X(rv, zindex, config->zindex); } if (config->border) { - Array border = ARRAY_DICT_INIT; + Array border = arena_array(arena, 8); for (size_t i = 0; i < 8; i++) { - Array tuple = ARRAY_DICT_INIT; - - String s = cstrn_to_string(config->border_chars[i], MAX_SCHAR_SIZE); + String s = cstrn_as_string(config->border_chars[i], MAX_SCHAR_SIZE); int hi_id = config->border_hl_ids[i]; char *hi_name = syn_id2name(hi_id); if (hi_name[0]) { - ADD(tuple, STRING_OBJ(s)); - ADD(tuple, CSTR_TO_OBJ(hi_name)); - ADD(border, ARRAY_OBJ(tuple)); + Array tuple = arena_array(arena, 2); + ADD_C(tuple, STRING_OBJ(s)); + ADD_C(tuple, CSTR_AS_OBJ(hi_name)); + ADD_C(border, ARRAY_OBJ(tuple)); } else { - ADD(border, STRING_OBJ(s)); + ADD_C(border, STRING_OBJ(s)); } } - PUT(rv, "border", ARRAY_OBJ(border)); + PUT_KEY_X(rv, border, ARRAY_OBJ(border)); if (config->title) { - rv = config_put_bordertext(rv, config, kBorderTextTitle); + config_put_bordertext(&rv, config, kBorderTextTitle, arena); } if (config->footer) { - rv = config_put_bordertext(rv, config, kBorderTextFooter); + config_put_bordertext(&rv, config, kBorderTextFooter, arena); } } + } else if (!config->external) { + PUT_KEY_X(rv, width, wp->w_width); + PUT_KEY_X(rv, height, wp->w_height); + WinSplit split = win_split_dir(wp); + PUT_KEY_X(rv, split, cstr_as_string(win_split_str[split])); } const char *rel = (wp->w_floating && !config->external ? float_relative_str[config->relative] : ""); - PUT(rv, "relative", CSTR_TO_OBJ(rel)); + PUT_KEY_X(rv, relative, cstr_as_string(rel)); return rv; } @@ -414,10 +716,26 @@ static bool parse_float_relative(String relative, FloatRelative *out) return true; } +static bool parse_config_split(String split, WinSplit *out) +{ + char *str = split.data; + if (striequal(str, "left")) { + *out = kWinSplitLeft; + } else if (striequal(str, "right")) { + *out = kWinSplitRight; + } else if (striequal(str, "above")) { + *out = kWinSplitAbove; + } else if (striequal(str, "below")) { + *out = kWinSplitBelow; + } else { + return false; + } + return true; +} + static bool parse_float_bufpos(Array bufpos, lpos_T *out) { - if (bufpos.size != 2 - || bufpos.items[0].type != kObjectTypeInteger + if (bufpos.size != 2 || bufpos.items[0].type != kObjectTypeInteger || bufpos.items[1].type != kObjectTypeInteger) { return false; } @@ -426,21 +744,39 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out) return true; } -static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, - FloatConfig *fconfig, Error *err) +static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, WinConfig *fconfig, + Error *err) { + if (bordertext.type != kObjectTypeString && bordertext.type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, "title/footer must be string or array"); + return; + } + + if (bordertext.type == kObjectTypeArray && bordertext.data.array.size == 0) { + api_set_error(err, kErrorTypeValidation, "title/footer cannot be an empty array"); + return; + } + bool *is_present; VirtText *chunks; int *width; int default_hl_id; switch (bordertext_type) { case kBorderTextTitle: + if (fconfig->title) { + clear_virttext(&fconfig->title_chunks); + } + is_present = &fconfig->title; chunks = &fconfig->title_chunks; width = &fconfig->title_width; default_hl_id = syn_check_group(S_LEN("FloatTitle")); break; case kBorderTextFooter: + if (fconfig->footer) { + clear_virttext(&fconfig->footer_chunks); + } + is_present = &fconfig->footer; chunks = &fconfig->footer_chunks; width = &fconfig->footer_width; @@ -460,16 +796,6 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, return; } - if (bordertext.type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, "title must be string or array"); - return; - } - - if (bordertext.data.array.size == 0) { - api_set_error(err, kErrorTypeValidation, "title cannot be an empty array"); - return; - } - *width = 0; *chunks = parse_virt_text(bordertext.data.array, err, width); @@ -477,7 +803,7 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, } static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type, - FloatConfig *fconfig, Error *err) + WinConfig *fconfig, Error *err) { AlignTextPos *align; switch (bordertext_type) { @@ -516,7 +842,7 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex return true; } -static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) +static void parse_border_style(Object style, WinConfig *fconfig, Error *err) { struct { const char *name; @@ -531,7 +857,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) { NULL, { { NUL } }, false }, }; - char (*chars)[MAX_SCHAR_SIZE] = fconfig->border_chars; + char(*chars)[MAX_SCHAR_SIZE] = fconfig->border_chars; int *hl_ids = fconfig->border_hl_ids; fconfig->border = true; @@ -540,8 +866,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) Array arr = style.data.array; size_t size = arr.size; if (!size || size > 8 || (size & (size - 1))) { - api_set_error(err, kErrorTypeValidation, - "invalid number of border chars"); + api_set_error(err, kErrorTypeValidation, "invalid number of border chars"); return; } for (size_t i = 0; i < size; i++) { @@ -571,10 +896,8 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) api_set_error(err, kErrorTypeValidation, "invalid border char"); return; } - if (string.size - && mb_string2cells_len(string.data, string.size) > 1) { - api_set_error(err, kErrorTypeValidation, - "border chars must be one cell"); + if (string.size && mb_string2cells_len(string.data, string.size) > 1) { + api_set_error(err, kErrorTypeValidation, "border chars must be one cell"); return; } size_t len = MIN(string.size, sizeof(*chars) - 1); @@ -593,8 +916,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) || (chars[1][0] && chars[3][0] && !chars[2][0]) || (chars[3][0] && chars[5][0] && !chars[4][0]) || (chars[5][0] && chars[7][0] && !chars[6][0])) { - api_set_error(err, kErrorTypeValidation, - "corner between used edges must be specified"); + api_set_error(err, kErrorTypeValidation, "corner between used edges must be specified"); } } else if (style.type == kObjectTypeString) { String str = style.data.string; @@ -621,26 +943,24 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) return; } } - api_set_error(err, kErrorTypeValidation, - "invalid border style \"%s\"", str.data); + api_set_error(err, kErrorTypeValidation, "invalid border style \"%s\"", str.data); } } -static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf, +static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, bool reconf, bool new_win, Error *err) { -#define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key) - bool has_relative = false, relative_is_win = false; - // ignore empty string, to match nvim_win_get_config - if (HAS_KEY_X(config, relative) && config->relative.size > 0) { +#define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key) + bool has_relative = false, relative_is_win = false, is_split = false; + if (config->relative.size > 0) { if (!parse_float_relative(config->relative, &fconfig->relative)) { api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key"); return false; } - if (!(HAS_KEY_X(config, row) && HAS_KEY_X(config, col)) && !HAS_KEY_X(config, bufpos)) { - api_set_error(err, kErrorTypeValidation, - "'relative' requires 'row'/'col' or 'bufpos'"); + if (config->relative.size > 0 && !(HAS_KEY_X(config, row) && HAS_KEY_X(config, col)) + && !HAS_KEY_X(config, bufpos)) { + api_set_error(err, kErrorTypeValidation, "'relative' requires 'row'/'col' or 'bufpos'"); return false; } @@ -650,6 +970,32 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, relative_is_win = true; fconfig->bufpos.lnum = -1; } + } else if (!config->external) { + if (HAS_KEY_X(config, vertical) || HAS_KEY_X(config, split)) { + is_split = true; + } else if (new_win) { + api_set_error(err, kErrorTypeValidation, + "Must specify 'relative' or 'external' when creating a float"); + return false; + } + } + + if (HAS_KEY_X(config, vertical)) { + if (!is_split) { + api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'vertical'"); + return false; + } + } + + if (HAS_KEY_X(config, split)) { + if (!is_split) { + api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'split'"); + return false; + } + if (!parse_config_split(config->split, &fconfig->split)) { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'split' key"); + return false; + } } if (HAS_KEY_X(config, anchor)) { @@ -660,7 +1006,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, row)) { - if (!has_relative) { + if (!has_relative || is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'"); return false; } @@ -668,7 +1014,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, col)) { - if (!has_relative) { + if (!has_relative || is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'"); return false; } @@ -676,7 +1022,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, bufpos)) { - if (!has_relative) { + if (!has_relative || is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'"); return false; } else { @@ -701,7 +1047,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer"); return false; } - } else if (!reconf) { + } else if (!reconf && !is_split) { api_set_error(err, kErrorTypeValidation, "Must specify 'width'"); return false; } @@ -713,21 +1059,22 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer"); return false; } - } else if (!reconf) { + } else if (!reconf && !is_split) { api_set_error(err, kErrorTypeValidation, "Must specify 'height'"); return false; } - if (relative_is_win) { + if (relative_is_win || is_split) { fconfig->window = curwin->handle; if (HAS_KEY_X(config, win)) { if (config->win > 0) { fconfig->window = config->win; } } - } else { + } else if (has_relative) { if (HAS_KEY_X(config, win)) { - api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'"); + api_set_error(err, kErrorTypeValidation, + "'win' key is only valid with relative='win' and relative=''"); return false; } } @@ -740,23 +1087,20 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, return false; } if (fconfig->external && !ui_has(kUIMultigrid)) { - api_set_error(err, kErrorTypeValidation, - "UI doesn't support external windows"); + api_set_error(err, kErrorTypeValidation, "UI doesn't support external windows"); return false; } } - if (!reconf && (!has_relative && !fconfig->external)) { - api_set_error(err, kErrorTypeValidation, - "One of 'relative' and 'external' must be used"); - return false; - } - if (HAS_KEY_X(config, focusable)) { fconfig->focusable = config->focusable; } if (HAS_KEY_X(config, zindex)) { + if (is_split) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'zindex'"); + return false; + } if (config->zindex > 0) { fconfig->zindex = (int)config->zindex; } else { @@ -766,16 +1110,16 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, title)) { + if (is_split) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'title'"); + return false; + } // title only work with border if (!HAS_KEY_X(config, border) && !fconfig->border) { api_set_error(err, kErrorTypeException, "title requires border to be set"); return false; } - if (fconfig->title) { - clear_virttext(&fconfig->title_chunks); - } - parse_bordertext(config->title, kBorderTextTitle, fconfig, err); if (ERROR_SET(err)) { return false; @@ -793,16 +1137,16 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, footer)) { + if (is_split) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'footer'"); + return false; + } // footer only work with border if (!HAS_KEY_X(config, border) && !fconfig->border) { api_set_error(err, kErrorTypeException, "footer requires border to be set"); return false; } - if (fconfig->footer) { - clear_virttext(&fconfig->footer_chunks); - } - parse_bordertext(config->footer, kBorderTextFooter, fconfig, err); if (ERROR_SET(err)) { return false; @@ -820,6 +1164,10 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, border)) { + if (is_split) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'border'"); + return false; + } parse_border_style(config->border, fconfig, err); if (ERROR_SET(err)) { return false; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index de5b40940f..ed51eedf1b 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -15,11 +15,10 @@ #include "nvim/drawscreen.h" #include "nvim/eval/window.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" -#include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/plines.h" @@ -27,6 +26,10 @@ #include "nvim/types_defs.h" #include "nvim/window.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/window.c.generated.h" +#endif + /// Gets the current buffer in a window /// /// @param window Window handle, or 0 for current window @@ -58,7 +61,7 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err) if (!win || !buf) { return; } - if (cmdwin_type != 0 && (win == curwin || win == cmdwin_old_curwin || buf == curbuf)) { + if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) { api_set_error(err, kErrorTypeException, "%s", e_cmdwin); return; } @@ -74,15 +77,16 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err) /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return (row, col) tuple -ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err) +ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); if (win) { - ADD(rv, INTEGER_OBJ(win->w_cursor.lnum)); - ADD(rv, INTEGER_OBJ(win->w_cursor.col)); + rv = arena_array(arena, 2); + ADD_C(rv, INTEGER_OBJ(win->w_cursor.lnum)); + ADD_C(rv, INTEGER_OBJ(win->w_cursor.col)); } return rv; @@ -234,7 +238,7 @@ void nvim_win_set_width(Window window, Integer width, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_win_get_var(Window window, String name, Error *err) +Object nvim_win_get_var(Window window, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -243,7 +247,7 @@ Object nvim_win_get_var(Window window, String name, Error *err) return (Object)OBJECT_INIT; } - return dict_get_value(win->w_vars, name, err); + return dict_get_value(win->w_vars, name, arena, err); } /// Sets a window-scoped (w:) variable @@ -261,7 +265,7 @@ void nvim_win_set_var(Window window, String name, Object value, Error *err) return; } - dict_set_var(win->w_vars, name, value, false, false, err); + dict_set_var(win->w_vars, name, value, false, false, NULL, err); } /// Removes a window-scoped (w:) variable @@ -278,7 +282,7 @@ void nvim_win_del_var(Window window, String name, Error *err) return; } - dict_set_var(win->w_vars, name, NIL, true, false, err); + dict_set_var(win->w_vars, name, NIL, true, false, NULL, err); } /// Gets the window position in display cells. First position is zero. @@ -286,15 +290,16 @@ void nvim_win_del_var(Window window, String name, Error *err) /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return (row, col) tuple with the window position -ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err) +ArrayOf(Integer, 2) nvim_win_get_position(Window window, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); if (win) { - ADD(rv, INTEGER_OBJ(win->w_winrow)); - ADD(rv, INTEGER_OBJ(win->w_wincol)); + rv = arena_array(arena, 2); + ADD_C(rv, INTEGER_OBJ(win->w_winrow)); + ADD_C(rv, INTEGER_OBJ(win->w_wincol)); } return rv; @@ -418,8 +423,7 @@ void nvim_win_close(Window window, Boolean force, Error *err) /// @param fun Function to call inside the window (currently Lua callable /// only) /// @param[out] err Error details, if any -/// @return Return value of function. NB: will deepcopy Lua values -/// currently, use upvalues to send Lua references in and out. +/// @return Return value of function. Object nvim_win_call(Window window, LuaRef fun, Error *err) FUNC_API_SINCE(7) FUNC_API_LUA_ONLY @@ -432,10 +436,12 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err) try_start(); Object res = OBJECT_INIT; - WIN_EXECUTE(win, tabpage, { + win_execute_T win_execute_args; + if (win_execute_before(&win_execute_args, win, tabpage)) { Array args = ARRAY_DICT_INIT; - res = nlua_call_ref(fun, NULL, args, true, err); - }); + res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err); + } + win_execute_after(&win_execute_args); try_end(err); return res; } @@ -446,6 +452,7 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err) /// /// This takes precedence over the 'winhighlight' option. /// +/// @param window /// @param ns_id the namespace to use /// @param[out] err Error details, if any void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err) diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c index 84f4297c99..4587415c3b 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -22,7 +22,6 @@ #include "nvim/arabic.h" #include "nvim/ascii_defs.h" -#include "nvim/func_attr.h" #include "nvim/macros_defs.h" #include "nvim/option_vars.h" @@ -258,6 +257,7 @@ bool arabic_maycombine(int two) } /// Check whether we are dealing with Arabic combining characters. +/// Returns false for negative values. /// Note: these are NOT really composing characters! /// /// @param one First character. @@ -271,22 +271,22 @@ bool arabic_combine(int one, int two) return false; } -/// A_is_iso returns true if 'c' is an Arabic ISO-8859-6 character +/// @return true if 'c' is an Arabic ISO-8859-6 character /// (alphabet/number/punctuation) -static int A_is_iso(int c) +static bool A_is_iso(int c) { return find_achar(c) != NULL; } -/// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B) -static int A_is_ok(int c) +/// @return true if 'c' is an Arabic 10646 (8859-6 or Form-B) +static bool A_is_ok(int c) { return (A_is_iso(c) || c == a_BYTE_ORDER_MARK); } -/// A_is_valid returns true if 'c' is an Arabic 10646 (8859-6 or Form-B) -/// with some exceptions/exclusions -static int A_is_valid(int c) +/// @return true if 'c' is an Arabic 10646 (8859-6 or Form-B) +/// with some exceptions/exclusions +static bool A_is_valid(int c) { return (A_is_ok(c) && c != a_HAMZA); } @@ -305,8 +305,8 @@ int arabic_shape(int c, int *c1p, int prev_c, int prev_c1, int next_c) } int curr_c; - int curr_laa = arabic_combine(c, *c1p); - int prev_laa = arabic_combine(prev_c, prev_c1); + bool curr_laa = arabic_combine(c, *c1p); + bool prev_laa = arabic_combine(prev_c, prev_c1); if (curr_laa) { if (A_is_valid(prev_c) && can_join(prev_c, a_LAM) && !prev_laa) { diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c index d2734e6c5a..a02c22deae 100644 --- a/src/nvim/arglist.c +++ b/src/nvim/arglist.c @@ -10,6 +10,7 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval.h" @@ -20,9 +21,9 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" @@ -35,6 +36,7 @@ #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/types_defs.h" #include "nvim/undo.h" #include "nvim/version.h" @@ -282,7 +284,7 @@ static char *do_one_arg(char *str) /// Separate the arguments in "str" and return a list of pointers in the /// growarray "gap". -static void get_arglist(garray_T *gap, char *str, int escaped) +static void get_arglist(garray_T *gap, char *str, bool escaped) { ga_init(gap, (int)sizeof(char *), 20); while (*str != NUL) { @@ -428,7 +430,7 @@ static int do_arglist(char *str, int what, int after, bool will_edit) garray_T new_ga; int exp_count; char **exp_files; - int arg_escaped = true; + bool arg_escaped = true; if (check_arglist_locked() == FAIL) { return FAIL; @@ -475,7 +477,7 @@ static int do_arglist(char *str, int what, int after, bool will_edit) /// Redefine the argument list. void set_arglist(char *str) { - do_arglist(str, AL_SET, 0, false); + do_arglist(str, AL_SET, 0, true); } /// @return true if window "win" is editing the file at the current argument @@ -852,6 +854,9 @@ static void arg_all_close_unused_windows(arg_all_state_T *aall) if (aall->had_tab > 0) { goto_tabpage_tp(first_tabpage, true, true); } + + // moving tabpages around in an autocommand may cause an endless loop + tabpage_move_disallowed++; while (true) { win_T *wpnext = NULL; tabpage_T *tpnext = curtab->tp_next; @@ -917,7 +922,7 @@ static void arg_all_close_unused_windows(arg_all_state_T *aall) if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) { bufref_T bufref; set_bufref(&bufref, buf); - (void)autowrite(buf, false); + autowrite(buf, false); // Check if autocommands removed the window. if (!win_valid(wp) || !bufref_valid(&bufref)) { wpnext = lastwin->w_floating ? lastwin : firstwin; // Start all over... @@ -951,6 +956,7 @@ static void arg_all_close_unused_windows(arg_all_state_T *aall) } goto_tabpage_tp(tpnext, true, true); } + tabpage_move_disallowed--; } /// Open up to "count" windows for the files in the argument list "aall->alist". @@ -1016,10 +1022,10 @@ static void arg_all_open_windows(arg_all_state_T *aall, int count) aall->new_curwin = curwin; aall->new_curtab = curtab; } - (void)do_ecmd(0, alist_name(&AARGLIST(aall->alist)[i]), NULL, NULL, ECMD_ONE, - ((buf_hide(curwin->w_buffer) - || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0) + ECMD_OLDBUF, - curwin); + do_ecmd(0, alist_name(&AARGLIST(aall->alist)[i]), NULL, NULL, ECMD_ONE, + ((buf_hide(curwin->w_buffer) + || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0) + ECMD_OLDBUF, + curwin); if (tab_drop_empty_window && i == count - 1) { autocmd_no_enter++; } @@ -1087,11 +1093,6 @@ static void do_arg_all(int count, int forceit, int keep_tabs) // When the ":tab" modifier was used do this for all tab pages. arg_all_close_unused_windows(&aall); - // Now set the last used tabpage to where we started. - if (valid_tabpage(new_lu_tp)) { - lastused_tabpage = new_lu_tp; - } - // Open a window for files in the argument list that don't have one. // ARGCOUNT may change while doing this, because of autocommands. if (count > aall.opened_len || count <= 0) { @@ -1128,6 +1129,12 @@ static void do_arg_all(int count, int forceit, int keep_tabs) if (valid_tabpage(aall.new_curtab)) { goto_tabpage_tp(aall.new_curtab, true, true); } + + // Now set the last used tabpage to where we started. + if (valid_tabpage(new_lu_tp)) { + lastused_tabpage = new_lu_tp; + } + if (win_valid(aall.new_curwin)) { win_enter(aall.new_curwin, false); } diff --git a/src/nvim/arglist.h b/src/nvim/arglist.h index 97729f466c..5b49423c18 100644 --- a/src/nvim/arglist.h +++ b/src/nvim/arglist.h @@ -1,10 +1,8 @@ #pragma once -#include "nvim/arglist_defs.h" // IWYU pragma: export +#include "nvim/arglist_defs.h" // IWYU pragma: keep #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep -#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep -#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "arglist.h.generated.h" diff --git a/src/nvim/arglist_defs.h b/src/nvim/arglist_defs.h index a79d540a6e..51f184e3b3 100644 --- a/src/nvim/arglist_defs.h +++ b/src/nvim/arglist_defs.h @@ -4,7 +4,7 @@ /// Argument list: Array of file names. /// Used for the global argument list and the argument lists local to a window. -typedef struct arglist { +typedef struct { garray_T al_ga; ///< growarray with the array of file names int al_refcount; ///< number of windows using this arglist int id; ///< id of this arglist @@ -13,7 +13,7 @@ typedef struct arglist { /// For each argument remember the file name as it was given, and the buffer /// number that contains the expanded file name (required for when ":cd" is /// used). -typedef struct argentry { +typedef struct { char *ae_fname; ///< file name as specified int ae_fnum; ///< buffer number with expanded file name } aentry_T; diff --git a/src/nvim/ascii_defs.h b/src/nvim/ascii_defs.h index 4125336796..0cd7ccfec4 100644 --- a/src/nvim/ascii_defs.h +++ b/src/nvim/ascii_defs.h @@ -3,7 +3,6 @@ #include <stdbool.h> #include "nvim/func_attr.h" -#include "nvim/macros_defs.h" #include "nvim/os/os_defs.h" // Definitions of various common control characters. @@ -86,31 +85,6 @@ static inline bool ascii_iswhite(int c) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; - -static inline bool ascii_iswhite_or_nul(int c) - REAL_FATTR_CONST - REAL_FATTR_ALWAYS_INLINE; - -static inline bool ascii_isdigit(int c) - REAL_FATTR_CONST - REAL_FATTR_ALWAYS_INLINE; - -static inline bool ascii_isxdigit(int c) - REAL_FATTR_CONST - REAL_FATTR_ALWAYS_INLINE; - -static inline bool ascii_isident(int c) - REAL_FATTR_CONST - REAL_FATTR_ALWAYS_INLINE; - -static inline bool ascii_isbdigit(int c) - REAL_FATTR_CONST - REAL_FATTR_ALWAYS_INLINE; - -static inline bool ascii_isspace(int c) - REAL_FATTR_CONST - REAL_FATTR_ALWAYS_INLINE; - /// Checks if `c` is a space or tab character. /// /// @see {ascii_isdigit} @@ -119,6 +93,9 @@ static inline bool ascii_iswhite(int c) return c == ' ' || c == '\t'; } +static inline bool ascii_iswhite_or_nul(int c) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; /// Checks if `c` is a space or tab character or NUL. /// /// @see {ascii_isdigit} @@ -127,6 +104,9 @@ static inline bool ascii_iswhite_or_nul(int c) return ascii_iswhite(c) || c == NUL; } +static inline bool ascii_isdigit(int c) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; /// Check whether character is a decimal digit. /// /// Library isdigit() function is officially locale-dependent and, for @@ -141,6 +121,9 @@ static inline bool ascii_isdigit(int c) return c >= '0' && c <= '9'; } +static inline bool ascii_isxdigit(int c) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; /// Checks if `c` is a hexadecimal digit, that is, one of 0-9, a-f, A-F. /// /// @see {ascii_isdigit} @@ -151,6 +134,9 @@ static inline bool ascii_isxdigit(int c) || (c >= 'A' && c <= 'F'); } +static inline bool ascii_isident(int c) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; /// Checks if `c` is an “identifier” character /// /// That is, whether it is alphanumeric character or underscore. @@ -159,6 +145,9 @@ static inline bool ascii_isident(int c) return ASCII_ISALNUM(c) || c == '_'; } +static inline bool ascii_isbdigit(int c) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; /// Checks if `c` is a binary digit, that is, 0-1. /// /// @see {ascii_isdigit} @@ -167,6 +156,9 @@ static inline bool ascii_isbdigit(int c) return (c == '0' || c == '1'); } +static inline bool ascii_isodigit(int c) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; /// Checks if `c` is an octal digit, that is, 0-7. /// /// @see {ascii_isdigit} @@ -175,6 +167,9 @@ static inline bool ascii_isodigit(int c) return (c >= '0' && c <= '7'); } +static inline bool ascii_isspace(int c) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; /// Checks if `c` is a white-space character, that is, /// one of \f, \n, \r, \t, \v. /// diff --git a/src/nvim/assert_defs.h b/src/nvim/assert_defs.h index cfc27ee994..5a6474862c 100644 --- a/src/nvim/assert_defs.h +++ b/src/nvim/assert_defs.h @@ -61,7 +61,7 @@ # define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg) // if we're dealing with gcc >= 4.6 in C99 mode, we can still use // _Static_assert but we need to suppress warnings, this is pretty ugly. -#elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) && /* NOLINT(whitespace/parens)*/ \ +#elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) && \ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) # define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg) diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 696df7c534..ca438e87b4 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -1,172 +1,175 @@ return { events = { - 'BufAdd', -- after adding a buffer to the buffer list - 'BufDelete', -- deleting a buffer from the buffer list - 'BufEnter', -- after entering a buffer - 'BufFilePost', -- after renaming a buffer - 'BufFilePre', -- before renaming a buffer - 'BufHidden', -- just after buffer becomes hidden - 'BufLeave', -- before leaving a buffer - 'BufModifiedSet', -- after the 'modified' state of a buffer changes - 'BufNew', -- after creating any buffer - 'BufNewFile', -- when creating a buffer for a new file - 'BufReadCmd', -- read buffer using command - 'BufReadPost', -- after reading a buffer - 'BufReadPre', -- before reading a buffer - 'BufUnload', -- just before unloading a buffer - 'BufWinEnter', -- after showing a buffer in a window - 'BufWinLeave', -- just after buffer removed from window - 'BufWipeout', -- just before really deleting a buffer - 'BufWriteCmd', -- write buffer using command - 'BufWritePost', -- after writing a buffer - 'BufWritePre', -- before writing a buffer - 'ChanInfo', -- info was received about channel - 'ChanOpen', -- channel was opened - 'CmdUndefined', -- command undefined - 'CmdWinEnter', -- after entering the cmdline window - 'CmdWinLeave', -- before leaving the cmdline window - 'CmdlineChanged', -- command line was modified - 'CmdlineEnter', -- after entering cmdline mode - 'CmdlineLeave', -- before leaving cmdline mode - 'ColorScheme', -- after loading a colorscheme - 'ColorSchemePre', -- before loading a colorscheme - 'CompleteChanged', -- after popup menu changed - 'CompleteDone', -- after finishing insert complete - 'CompleteDonePre', -- idem, before clearing info - 'CursorHold', -- cursor in same position for a while - 'CursorHoldI', -- idem, in Insert mode - 'CursorMoved', -- cursor was moved - 'CursorMovedI', -- cursor was moved in Insert mode - 'DiagnosticChanged', -- diagnostics in a buffer were modified - 'DiffUpdated', -- diffs have been updated - 'DirChanged', -- directory changed - 'DirChangedPre', -- directory is going to change - 'EncodingChanged', -- after changing the 'encoding' option - 'ExitPre', -- before exiting - 'FileAppendCmd', -- append to a file using command - 'FileAppendPost', -- after appending to a file - 'FileAppendPre', -- before appending to a file - 'FileChangedRO', -- before first change to read-only file - 'FileChangedShell', -- after shell command that changed file - 'FileChangedShellPost', -- after (not) reloading changed file - 'FileReadCmd', -- read from a file using command - 'FileReadPost', -- after reading a file - 'FileReadPre', -- before reading a file - 'FileType', -- new file type detected (user defined) - 'FileWriteCmd', -- write to a file using command - 'FileWritePost', -- after writing a file - 'FileWritePre', -- before writing a file - 'FilterReadPost', -- after reading from a filter - 'FilterReadPre', -- before reading from a filter - 'FilterWritePost', -- after writing to a filter - 'FilterWritePre', -- before writing to a filter - 'FocusGained', -- got the focus - 'FocusLost', -- lost the focus to another app - 'FuncUndefined', -- if calling a function which doesn't exist - 'GUIEnter', -- after starting the GUI - 'GUIFailed', -- after starting the GUI failed - 'InsertChange', -- when changing Insert/Replace mode - 'InsertCharPre', -- before inserting a char - 'InsertEnter', -- when entering Insert mode - 'InsertLeave', -- just after leaving Insert mode - 'InsertLeavePre', -- just before leaving Insert mode - 'LspAttach', -- after an LSP client attaches to a buffer - 'LspDetach', -- after an LSP client detaches from a buffer - 'LspRequest', -- after an LSP request is started, canceled, or completed - 'LspNotify', -- after an LSP notice has been sent to the server - 'LspTokenUpdate', -- after a visible LSP token is updated - 'LspProgress', -- after a LSP progress update - 'MenuPopup', -- just before popup menu is displayed - 'ModeChanged', -- after changing the mode - 'OptionSet', -- after setting any option - 'QuickFixCmdPost', -- after :make, :grep etc. - 'QuickFixCmdPre', -- before :make, :grep etc. - 'QuitPre', -- before :quit - 'RecordingEnter', -- when starting to record a macro - 'RecordingLeave', -- just before a macro stops recording - 'RemoteReply', -- upon string reception from a remote vim - 'SafeState', -- going to wait for a character - 'SearchWrapped', -- after the search wrapped around - 'SessionLoadPost', -- after loading a session file - 'ShellCmdPost', -- after ":!cmd" - 'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd". - 'Signal', -- after nvim process received a signal - 'SourceCmd', -- sourcing a Vim script using command - 'SourcePost', -- after sourcing a Vim script - 'SourcePre', -- before sourcing a Vim script - 'SpellFileMissing', -- spell file missing - 'StdinReadPost', -- after reading from stdin - 'StdinReadPre', -- before reading from stdin - 'SwapExists', -- found existing swap file - 'Syntax', -- syntax selected - 'TabClosed', -- a tab has closed - 'TabEnter', -- after entering a tab page - 'TabLeave', -- before leaving a tab page - 'TabNew', -- when creating a new tab - 'TabNewEntered', -- after entering a new tab - 'TermChanged', -- after changing 'term' - 'TermClose', -- after the process exits - 'TermEnter', -- after entering Terminal mode - 'TermLeave', -- after leaving Terminal mode - 'TermOpen', -- after opening a terminal buffer - 'TermResponse', -- after setting "v:termresponse" - 'TextChanged', -- text was modified - 'TextChangedI', -- text was modified in Insert mode(no popup) - 'TextChangedP', -- text was modified in Insert mode(popup) - 'TextChangedT', -- text was modified in Terminal mode - 'TextYankPost', -- after a yank or delete was done (y, d, c) - 'UIEnter', -- after UI attaches - 'UILeave', -- after UI detaches - 'User', -- user defined autocommand - 'VimEnter', -- after starting Vim - 'VimLeave', -- before exiting Vim - 'VimLeavePre', -- before exiting Vim and writing ShaDa file - 'VimResized', -- after Vim window was resized - 'VimResume', -- after Nvim is resumed - 'VimSuspend', -- before Nvim is suspended - 'WinClosed', -- after closing a window - 'WinEnter', -- after entering a window - 'WinLeave', -- before leaving a window - 'WinNew', -- when entering a new window - 'WinResized', -- after a window was resized - 'WinScrolled', -- after a window was scrolled or resized + 'BufAdd', -- after adding a buffer to the buffer list + 'BufDelete', -- deleting a buffer from the buffer list + 'BufEnter', -- after entering a buffer + 'BufFilePost', -- after renaming a buffer + 'BufFilePre', -- before renaming a buffer + 'BufHidden', -- just after buffer becomes hidden + 'BufLeave', -- before leaving a buffer + 'BufModifiedSet', -- after the 'modified' state of a buffer changes + 'BufNew', -- after creating any buffer + 'BufNewFile', -- when creating a buffer for a new file + 'BufReadCmd', -- read buffer using command + 'BufReadPost', -- after reading a buffer + 'BufReadPre', -- before reading a buffer + 'BufUnload', -- just before unloading a buffer + 'BufWinEnter', -- after showing a buffer in a window + 'BufWinLeave', -- just after buffer removed from window + 'BufWipeout', -- just before really deleting a buffer + 'BufWriteCmd', -- write buffer using command + 'BufWritePost', -- after writing a buffer + 'BufWritePre', -- before writing a buffer + 'ChanInfo', -- info was received about channel + 'ChanOpen', -- channel was opened + 'CmdUndefined', -- command undefined + 'CmdWinEnter', -- after entering the cmdline window + 'CmdWinLeave', -- before leaving the cmdline window + 'CmdlineChanged', -- command line was modified + 'CmdlineEnter', -- after entering cmdline mode + 'CmdlineLeave', -- before leaving cmdline mode + 'ColorScheme', -- after loading a colorscheme + 'ColorSchemePre', -- before loading a colorscheme + 'CompleteChanged', -- after popup menu changed + 'CompleteDone', -- after finishing insert complete + 'CompleteDonePre', -- idem, before clearing info + 'CursorHold', -- cursor in same position for a while + 'CursorHoldI', -- idem, in Insert mode + 'CursorMoved', -- cursor was moved + 'CursorMovedI', -- cursor was moved in Insert mode + 'DiagnosticChanged', -- diagnostics in a buffer were modified + 'DiffUpdated', -- diffs have been updated + 'DirChanged', -- directory changed + 'DirChangedPre', -- directory is going to change + 'EncodingChanged', -- after changing the 'encoding' option + 'ExitPre', -- before exiting + 'FileAppendCmd', -- append to a file using command + 'FileAppendPost', -- after appending to a file + 'FileAppendPre', -- before appending to a file + 'FileChangedRO', -- before first change to read-only file + 'FileChangedShell', -- after shell command that changed file + 'FileChangedShellPost', -- after (not) reloading changed file + 'FileReadCmd', -- read from a file using command + 'FileReadPost', -- after reading a file + 'FileReadPre', -- before reading a file + 'FileType', -- new file type detected (user defined) + 'FileWriteCmd', -- write to a file using command + 'FileWritePost', -- after writing a file + 'FileWritePre', -- before writing a file + 'FilterReadPost', -- after reading from a filter + 'FilterReadPre', -- before reading from a filter + 'FilterWritePost', -- after writing to a filter + 'FilterWritePre', -- before writing to a filter + 'FocusGained', -- got the focus + 'FocusLost', -- lost the focus to another app + 'FuncUndefined', -- if calling a function which doesn't exist + 'GUIEnter', -- after starting the GUI + 'GUIFailed', -- after starting the GUI failed + 'InsertChange', -- when changing Insert/Replace mode + 'InsertCharPre', -- before inserting a char + 'InsertEnter', -- when entering Insert mode + 'InsertLeave', -- just after leaving Insert mode + 'InsertLeavePre', -- just before leaving Insert mode + 'LspAttach', -- after an LSP client attaches to a buffer + 'LspDetach', -- after an LSP client detaches from a buffer + 'LspRequest', -- after an LSP request is started, canceled, or completed + 'LspNotify', -- after an LSP notice has been sent to the server + 'LspTokenUpdate', -- after a visible LSP token is updated + 'LspProgress', -- after a LSP progress update + 'MenuPopup', -- just before popup menu is displayed + 'ModeChanged', -- after changing the mode + 'OptionSet', -- after setting any option + 'QuickFixCmdPost', -- after :make, :grep etc. + 'QuickFixCmdPre', -- before :make, :grep etc. + 'QuitPre', -- before :quit + 'RecordingEnter', -- when starting to record a macro + 'RecordingLeave', -- just before a macro stops recording + 'RemoteReply', -- upon string reception from a remote vim + 'SafeState', -- going to wait for a character + 'SearchWrapped', -- after the search wrapped around + 'SessionLoadPost', -- after loading a session file + 'ShellCmdPost', -- after ":!cmd" + 'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd". + 'Signal', -- after nvim process received a signal + 'SourceCmd', -- sourcing a Vim script using command + 'SourcePost', -- after sourcing a Vim script + 'SourcePre', -- before sourcing a Vim script + 'SpellFileMissing', -- spell file missing + 'StdinReadPost', -- after reading from stdin + 'StdinReadPre', -- before reading from stdin + 'SwapExists', -- found existing swap file + 'Syntax', -- syntax selected + 'TabClosed', -- a tab has closed + 'TabEnter', -- after entering a tab page + 'TabLeave', -- before leaving a tab page + 'TabNew', -- when creating a new tab + 'TabNewEntered', -- after entering a new tab + 'TermChanged', -- after changing 'term' + 'TermClose', -- after the process exits + 'TermEnter', -- after entering Terminal mode + 'TermLeave', -- after leaving Terminal mode + 'TermOpen', -- after opening a terminal buffer + 'TermRequest', -- after an unhandled OSC sequence is emitted + 'TermResponse', -- after setting "v:termresponse" + 'TextChanged', -- text was modified + 'TextChangedI', -- text was modified in Insert mode(no popup) + 'TextChangedP', -- text was modified in Insert mode(popup) + 'TextChangedT', -- text was modified in Terminal mode + 'TextYankPost', -- after a yank or delete was done (y, d, c) + 'TextPutPost', -- after a put was done (p, P) + 'UIEnter', -- after UI attaches + 'UILeave', -- after UI detaches + 'User', -- user defined autocommand + 'VimEnter', -- after starting Vim + 'VimLeave', -- before exiting Vim + 'VimLeavePre', -- before exiting Vim and writing ShaDa file + 'VimResized', -- after Vim window was resized + 'VimResume', -- after Nvim is resumed + 'VimSuspend', -- before Nvim is suspended + 'WinClosed', -- after closing a window + 'WinEnter', -- after entering a window + 'WinLeave', -- before leaving a window + 'WinNew', -- when entering a new window + 'WinResized', -- after a window was resized + 'WinScrolled', -- after a window was scrolled or resized }, aliases = { { 'BufCreate', - 'BufAdd' + 'BufAdd', }, { 'BufRead', - 'BufReadPost' + 'BufReadPost', }, { 'BufWrite', - 'BufWritePre' + 'BufWritePre', }, { 'FileEncoding', - 'EncodingChanged' + 'EncodingChanged', }, }, -- List of nvim-specific events or aliases for the purpose of generating -- syntax file nvim_specific = { - BufModifiedSet=true, - DiagnosticChanged=true, - LspAttach=true, - LspDetach=true, - LspNotify=true, - LspRequest=true, - LspProgress=true, - LspTokenUpdate=true, - RecordingEnter=true, - RecordingLeave=true, - Signal=true, - TabNewEntered=true, - TermClose=true, - TermOpen=true, - UIEnter=true, - UILeave=true, + BufModifiedSet = true, + DiagnosticChanged = true, + LspAttach = true, + LspDetach = true, + LspNotify = true, + LspRequest = true, + LspProgress = true, + LspTokenUpdate = true, + RecordingEnter = true, + RecordingLeave = true, + Signal = true, + TabNewEntered = true, + TermClose = true, + TermOpen = true, + TermRequest = true, + UIEnter = true, + UILeave = true, }, } diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 74a1dbdbc3..3f93906942 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -19,17 +19,19 @@ #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" +#include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/hashtab.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/main.h" @@ -37,17 +39,22 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/time.h" +#include "nvim/os/time_defs.h" #include "nvim/path.h" #include "nvim/profile.h" #include "nvim/regexp.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/search.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" @@ -114,7 +121,7 @@ static void augroup_map_del(int id, const char *name) { if (name != NULL) { String key; - map_del(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name), &key); + map_del(String, int)(&map_augroup_name_to_id, cstr_as_string(name), &key); api_free_string(key); } if (id > 0) { @@ -469,7 +476,7 @@ void augroup_del(char *name, bool stupid_legacy_mode) int augroup_find(const char *name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int existing_id = map_get(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name)); + int existing_id = map_get(String, int)(&map_augroup_name_to_id, cstr_as_string(name)); if (existing_id == AUGROUP_DELETED) { return existing_id; } @@ -530,7 +537,7 @@ bool augroup_exists(const char *name) } /// ":augroup {name}". -void do_augroup(char *arg, int del_group) +void do_augroup(char *arg, bool del_group) { if (del_group) { if (*arg == NUL) { @@ -705,7 +712,7 @@ char *au_event_disable(char *what) } else { STRCAT(new_ei, what); } - set_string_option_direct("ei", -1, new_ei, OPT_FREE, SID_NONE); + set_string_option_direct(kOptEventignore, new_ei, 0, SID_NONE); xfree(new_ei); return save_ei; } @@ -713,7 +720,7 @@ char *au_event_disable(char *what) void au_event_restore(char *old_ei) { if (old_ei != NULL) { - set_string_option_direct("ei", -1, old_ei, OPT_FREE, SID_NONE); + set_string_option_direct(kOptEventignore, old_ei, 0, SID_NONE); xfree(old_ei); } } @@ -755,7 +762,7 @@ void do_autocmd(exarg_T *eap, char *arg_in, int forceit) char *arg = arg_in; char *envpat = NULL; char *cmd; - int need_free = false; + bool need_free = false; bool nested = false; bool once = false; int group; @@ -914,7 +921,7 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char int patlen = (int)aucmd_pattern_length(pat); while (patlen) { // detect special <buffer[=X]> buffer-local patterns - int is_buflocal = aupat_is_buflocal(pat, patlen); + bool is_buflocal = aupat_is_buflocal(pat, patlen); if (is_buflocal) { const int buflocal_nr = aupat_get_buflocal_nr(pat, patlen); @@ -978,7 +985,7 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int const int findgroup = group == AUGROUP_ALL ? current_augroup : group; // detect special <buffer[=X]> buffer-local patterns - const int is_buflocal = aupat_is_buflocal(pat, patlen); + const bool is_buflocal = aupat_is_buflocal(pat, patlen); int buflocal_nr = 0; char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>" @@ -1222,7 +1229,7 @@ void ex_doautoall(exarg_T *eap) // Execute autocommands for the current buffer last. if (retval == OK) { - (void)do_doautocmd(arg, false, &did_aucmd); + do_doautocmd(arg, false, &did_aucmd); if (call_do_modelines && did_aucmd) { do_modelines(0); } @@ -1295,9 +1302,11 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) } aco->save_curwin_handle = curwin->handle; - aco->save_curbuf = curbuf; aco->save_prevwin_handle = prevwin == NULL ? 0 : prevwin->handle; aco->save_State = State; + if (bt_prompt(curbuf)) { + aco->save_prompt_insert = curbuf->b_prompt_insert; + } if (win != NULL) { // There is a window for "buf" in the current tab page, make it the @@ -1326,7 +1335,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) if (need_append) { win_append(lastwin, auc_win); pmap_put(int)(&window_handles, auc_win->handle, auc_win); - win_config_float(auc_win, auc_win->w_float_config); + win_config_float(auc_win, auc_win->w_config); } // Prevent chdir() call in win_enter_ext(), through do_autochdir() int save_acd = p_acd; @@ -1410,6 +1419,9 @@ win_found: curbuf = curwin->w_buffer; // May need to restore insert mode for a prompt buffer. entering_window(curwin); + if (bt_prompt(curbuf)) { + curbuf->b_prompt_insert = aco->save_prompt_insert; + } prevwin = win_find_by_handle(aco->save_prevwin_handle); vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables @@ -1568,12 +1580,11 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force bool retval = false; static int nesting = 0; char *save_cmdarg; - varnumber_T save_cmdbang; - static int filechangeshell_busy = false; + static bool filechangeshell_busy = false; proftime_T wait_time; bool did_save_redobuff = false; save_redo_T save_redo; - const bool save_KeyTyped = KeyTyped; // NOLINT + const bool save_KeyTyped = KeyTyped; // Quickly return if there are no autocommands for this event or // autocommands are blocked. @@ -1782,7 +1793,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force patcmd.data = data; // set v:cmdarg (only when there is a matching pattern) - save_cmdbang = get_vim_var_nr(VV_CMDBANG); + varnumber_T save_cmdbang = get_vim_var_nr(VV_CMDBANG); if (eap != NULL) { save_cmdarg = set_cmdarg(eap, NULL); set_vim_var_nr(VV_CMDBANG, eap->forceit); @@ -1815,7 +1826,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force } if (eap != NULL) { - (void)set_cmdarg(NULL, save_cmdarg); + set_cmdarg(NULL, save_cmdarg); set_vim_var_nr(VV_CMDBANG, save_cmdbang); } // delete from active_apc_list @@ -1991,15 +2002,15 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc) { Callback callback = ac->exec.callable.cb; if (callback.type == kCallbackLua) { - Dictionary data = ARRAY_DICT_INIT; - PUT(data, "id", INTEGER_OBJ(ac->id)); - PUT(data, "event", CSTR_TO_OBJ(event_nr2name(apc->event))); - PUT(data, "match", CSTR_TO_OBJ(autocmd_match)); - PUT(data, "file", CSTR_TO_OBJ(autocmd_fname)); - PUT(data, "buf", INTEGER_OBJ(autocmd_bufnr)); + MAXSIZE_TEMP_DICT(data, 7); + PUT_C(data, "id", INTEGER_OBJ(ac->id)); + PUT_C(data, "event", CSTR_AS_OBJ(event_nr2name(apc->event))); + PUT_C(data, "match", CSTR_AS_OBJ(autocmd_match)); + PUT_C(data, "file", CSTR_AS_OBJ(autocmd_fname)); + PUT_C(data, "buf", INTEGER_OBJ(autocmd_bufnr)); if (apc->data) { - PUT(data, "data", copy_object(*apc->data, NULL)); + PUT_C(data, "data", *apc->data); } int group = ac->pat->group; @@ -2012,21 +2023,15 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc) // omit group in these cases break; default: - PUT(data, "group", INTEGER_OBJ(group)); + PUT_C(data, "group", INTEGER_OBJ(group)); break; } MAXSIZE_TEMP_ARRAY(args, 1); ADD_C(args, DICTIONARY_OBJ(data)); - Object result = nlua_call_ref(callback.data.luaref, NULL, args, true, NULL); - bool ret = false; - if (result.type == kObjectTypeBoolean) { - ret = result.data.boolean; - } - api_free_dictionary(data); - api_free_object(result); - return ret; + Object result = nlua_call_ref(callback.data.luaref, NULL, args, kRetNilBool, NULL, NULL); + return LUARET_TRUTHY(result); } else { typval_T argsin = TV_INITIAL_VALUE; typval_T rettv = TV_INITIAL_VALUE; @@ -2164,7 +2169,7 @@ char *expand_get_augroup_name(expand_T *xp, int idx) } /// @param doautocmd true for :doauto*, false for :autocmd -char *set_context_in_autocmd(expand_T *xp, char *arg, int doautocmd) +char *set_context_in_autocmd(expand_T *xp, char *arg, bool doautocmd) { // check for a group name, skip it if present autocmd_include_groups = false; @@ -2424,7 +2429,7 @@ char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc) case CALLABLE_EX: return xstrdup(acc.callable.cmd); case CALLABLE_CB: - return callback_to_string(&acc.callable.cb); + return callback_to_string(&acc.callable.cb, NULL); case CALLABLE_NONE: return "This is not possible"; } @@ -2478,7 +2483,7 @@ bool au_event_is_empty(event_T event) /// Scan over the events. "*" stands for all events. /// true when group name was found -static char *arg_event_skip(char *arg, int have_group) +static char *arg_event_skip(char *arg, bool have_group) { char *pat; char *p; @@ -2565,7 +2570,7 @@ void may_trigger_vim_suspend_resume(bool suspend) pending_vimresume = kTrue; } else if (!suspend && pending_vimresume == kTrue) { pending_vimresume = kNone; - multiqueue_put(main_loop.events, vimresume_event, 0); + multiqueue_put(main_loop.events, vimresume_event, NULL); } } @@ -2574,6 +2579,11 @@ void do_autocmd_uienter(uint64_t chanid, bool attached) { static bool recursive = false; +#ifdef EXITFREE + if (entered_free_all_mem) { + return; + } +#endif if (starting == NO_SCREEN) { return; // user config hasn't been sourced yet } diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index 259a56cf5c..8019cb7145 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -1,11 +1,12 @@ #pragma once #include <stdbool.h> -#include <stddef.h> -#include <stdint.h> +#include <stddef.h> // IWYU pragma: keep +#include <stdint.h> // IWYU pragma: keep +#include "klib/kvec.h" #include "nvim/api/private/defs.h" // IWYU pragma: keep -#include "nvim/autocmd_defs.h" // IWYU pragma: export +#include "nvim/autocmd_defs.h" // IWYU pragma: keep #include "nvim/buffer_defs.h" #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep @@ -27,18 +28,57 @@ EXTERN win_T *last_cursormoved_win INIT( = NULL); /// For CursorMoved event, only used when last_cursormoved_win == curwin EXTERN pos_T last_cursormoved INIT( = { 0, 0, 0 }); -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "autocmd.h.generated.h" -#endif +EXTERN bool autocmd_busy INIT( = false); ///< Is apply_autocmds() busy? +EXTERN int autocmd_no_enter INIT( = false); ///< Buf/WinEnter autocmds disabled +EXTERN int autocmd_no_leave INIT( = false); ///< Buf/WinLeave autocmds disabled +EXTERN bool did_filetype INIT( = false); ///< FileType event found +/// value for did_filetype when starting to execute autocommands +EXTERN bool keep_filetype INIT( = false); + +/// When deleting the current buffer, another one must be loaded. +/// If we know which one is preferred, au_new_curbuf is set to it. +EXTERN bufref_T au_new_curbuf INIT( = { NULL, 0, 0 }); + +// When deleting a buffer/window and autocmd_busy is true, do not free the +// buffer/window. but link it in the list starting with +// au_pending_free_buf/ap_pending_free_win, using b_next/w_next. +// Free the buffer/window when autocmd_busy is being set to false. +EXTERN buf_T *au_pending_free_buf INIT( = NULL); +EXTERN win_T *au_pending_free_win INIT( = NULL); + +EXTERN char *autocmd_fname INIT( = NULL); ///< fname for <afile> on cmdline +EXTERN bool autocmd_fname_full INIT( = false); ///< autocmd_fname is full path +EXTERN int autocmd_bufnr INIT( = 0); ///< fnum for <abuf> on cmdline +EXTERN char *autocmd_match INIT( = NULL); ///< name for <amatch> on cmdline +EXTERN bool did_cursorhold INIT( = false); ///< set when CursorHold t'gerd -#define AUGROUP_DEFAULT (-1) // default autocmd group -#define AUGROUP_ERROR (-2) // erroneous autocmd group -#define AUGROUP_ALL (-3) // all autocmd groups -#define AUGROUP_DELETED (-4) // all autocmd groups -// #define AUGROUP_NS -5 // TODO(tjdevries): Support namespaced based augroups +typedef struct { + win_T *auc_win; ///< Window used in aucmd_prepbuf(). When not NULL the + ///< window has been allocated. + bool auc_win_used; ///< This auc_win is being used. +} aucmdwin_T; -#define BUFLOCAL_PAT_LEN 25 +/// When executing autocommands for a buffer that is not in any window, a +/// special window is created to handle the side effects. When autocommands +/// nest we may need more than one. +EXTERN kvec_t(aucmdwin_T) aucmd_win_vec INIT( = KV_INITIAL_VALUE); +#define aucmd_win (aucmd_win_vec.items) +#define AUCMD_WIN_COUNT ((int)aucmd_win_vec.size) + +enum { + AUGROUP_DEFAULT = -1, ///< default autocmd group + AUGROUP_ERROR = -2, ///< erroneous autocmd group + AUGROUP_ALL = -3, ///< all autocmd groups + AUGROUP_DELETED = -4, ///< all autocmd groups + // AUGROUP_NS = -5, // TODO(tjdevries): Support namespaced based augroups +}; + +enum { BUFLOCAL_PAT_LEN = 25, }; /// Iterates over all the events for auto commands #define FOR_ALL_AUEVENTS(event) \ - for (event_T event = (event_T)0; (int)event < (int)NUM_EVENTS; event = (event_T)((int)event + 1)) // NOLINT + for (event_T event = (event_T)0; (int)event < (int)NUM_EVENTS; event = (event_T)((int)event + 1)) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "autocmd.h.generated.h" +#endif diff --git a/src/nvim/autocmd_defs.h b/src/nvim/autocmd_defs.h index 4639ec2731..6535f8a7ea 100644 --- a/src/nvim/autocmd_defs.h +++ b/src/nvim/autocmd_defs.h @@ -4,15 +4,9 @@ #include <stddef.h> #include <stdint.h> -#include "klib/kvec.h" -#include "nvim/api/private/defs.h" #include "nvim/buffer_defs.h" -#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" -#include "nvim/regexp_defs.h" -#include "nvim/types_defs.h" -// event_T definition #ifdef INCLUDE_GENERATED_DECLARATIONS # include "auevents_enum.generated.h" #endif @@ -20,7 +14,6 @@ /// Struct to save values in before executing autocommands for a buffer that is /// not the current buffer. typedef struct { - buf_T *save_curbuf; ///< saved curbuf int use_aucmd_win_idx; ///< index in aucmd_win[] if >= 0 handle_T save_curwin_handle; ///< ID of saved curwin handle_T new_curwin_handle; ///< ID of new curwin @@ -29,6 +22,7 @@ typedef struct { char *globaldir; ///< saved value of globaldir bool save_VIsual_active; ///< saved VIsual_active int save_State; ///< saved State + int save_prompt_insert; ///< saved b_prompt_insert } aco_save_T; typedef struct { diff --git a/src/nvim/base64.c b/src/nvim/base64.c index 295dedd8d3..d461b7e3ff 100644 --- a/src/nvim/base64.c +++ b/src/nvim/base64.c @@ -12,7 +12,7 @@ #endif #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "base64.c.generated.h" // IWYU pragma: export +# include "base64.c.generated.h" #endif static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -65,6 +65,7 @@ static inline uint32_t htobe32(uint32_t host_32bits) /// @param src_len Length of the string /// @return Base64 encoded string char *base64_encode(const char *src, size_t src_len) + FUNC_ATTR_NONNULL_ALL { assert(src != NULL); diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 8a594dea92..f6c7229485 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -32,6 +32,7 @@ #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_updates.h" #include "nvim/change.h" @@ -39,28 +40,30 @@ #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cursor.h" -#include "nvim/decoration.h" #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" -#include "nvim/eval/typval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/ex_eval_defs.h" #include "nvim/ex_getln.h" #include "nvim/extmark.h" #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/help.h" #include "nvim/indent.h" #include "nvim/indent_c.h" @@ -68,26 +71,33 @@ #include "nvim/map_defs.h" #include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memfile_defs.h" +#include "nvim/memline.h" #include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/time.h" #include "nvim/path.h" #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/state_defs.h" @@ -95,7 +105,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/terminal.h" -#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/usercmd.h" @@ -133,7 +142,7 @@ int get_highest_fnum(void) /// @param read_stdin read file from stdin, otherwise fifo /// @param eap for forced 'ff' and 'fenc' or NULL /// @param flags extra flags for readfile() -static int read_buffer(int read_stdin, exarg_T *eap, int flags) +static int read_buffer(bool read_stdin, exarg_T *eap, int flags) { int retval = OK; bool silent = shortmess(SHM_FILEINFO); @@ -202,13 +211,13 @@ bool buf_ensure_loaded(buf_T *buf) /// @param flags_arg extra flags for readfile() /// /// @return FAIL for failure, OK otherwise. -int open_buffer(int read_stdin, exarg_T *eap, int flags_arg) +int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) { int flags = flags_arg; int retval = OK; bufref_T old_curbuf; OptInt old_tw = curbuf->b_p_tw; - int read_fifo = false; + bool read_fifo = false; bool silent = shortmess(SHM_FILEINFO); // The 'readonly' flag is only set when BF_NEVERLOADED is being reset. @@ -274,16 +283,14 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg) if (curbuf->b_ffname != NULL) { #ifdef UNIX int save_bin = curbuf->b_p_bin; - int perm; - - perm = os_getperm(curbuf->b_ffname); + int perm = os_getperm(curbuf->b_ffname); if (perm >= 0 && (0 || S_ISFIFO(perm) || S_ISSOCK(perm) # ifdef OPEN_CHR_FILES || (S_ISCHR(perm) && is_dev_fd_file(curbuf->b_ffname)) # endif - )) { // NOLINT(whitespace/parens) + )) { read_fifo = true; } if (read_fifo) { @@ -303,9 +310,9 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg) } #endif - // Help buffer is filtered. + // Help buffer: populate *local-additions* in help.txt if (bt_help(curbuf)) { - fix_help_buffer(); + get_local_additions(); } } else if (read_stdin) { int save_bin = curbuf->b_p_bin; @@ -332,7 +339,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg) // if first time loading this buffer, init b_chartab[] if (curbuf->b_flags & BF_NEVERLOADED) { - (void)buf_init_chartab(curbuf, false); + buf_init_chartab(curbuf, false); parse_cino(curbuf); } @@ -836,8 +843,9 @@ void buf_freeall(buf_T *buf, int flags) ml_close(buf, true); // close and delete the memline/memfile buf->b_ml.ml_line_count = 0; // no lines in buffer if ((flags & BFA_KEEP_UNDO) == 0) { - u_blockfree(buf); // free the memory allocated for undo - u_clearall(buf); // reset all undo information + // free the memory allocated for undo + // and reset all undo information + u_clearallandblockfree(buf); } syntax_clear(&buf->b_s); // reset syntax info buf->b_flags &= ~BF_READERR; // a read error is no longer relevant @@ -938,8 +946,8 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count) if (swap_exists_action == SEA_NONE) { swap_exists_action = SEA_DIALOG; } - (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, - start, dir, count, eap->forceit); + do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, + start, dir, count, eap->forceit); if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') { cleanup_T cs; @@ -1047,7 +1055,7 @@ char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_b int bnr; // buffer number if (addr_count == 0) { - (void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit); + do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit); } else { if (addr_count == 2) { if (*arg) { // both range and argument is not allowed @@ -1126,7 +1134,7 @@ char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_b /// Make the current buffer empty. /// Used when it is wiped out and it's the last buffer. -static int empty_curbuf(int close_others, int forceit, int action) +static int empty_curbuf(bool close_others, int forceit, int action) { buf_T *buf = curbuf; @@ -1176,6 +1184,32 @@ static int empty_curbuf(int close_others, int forceit, int action) return retval; } +/// Remove every jump list entry referring to a given buffer. +/// This function will also adjust the current jump list index. +void buf_remove_from_jumplist(buf_T *deleted_buf) +{ + // Remove all jump list entries that match the deleted buffer. + for (int i = curwin->w_jumplistlen - 1; i >= 0; i--) { + buf_T *buf = buflist_findnr(curwin->w_jumplist[i].fmark.fnum); + + if (buf == deleted_buf) { + // Found an entry that we want to delete. + curwin->w_jumplistlen -= 1; + + // If the current jump list index behind the entry we want to + // delete, move it back by one. + if (curwin->w_jumplistidx > i && curwin->w_jumplistidx > 0) { + curwin->w_jumplistidx -= 1; + } + + // Actually remove the entry from the jump list. + for (int d = i; d < curwin->w_jumplistlen; d++) { + curwin->w_jumplist[d] = curwin->w_jumplist[d + 1]; + } + } + } +} + /// Implementation of the commands for the buffer list. /// /// action == DOBUF_GOTO go to specified buffer @@ -1198,8 +1232,9 @@ int do_buffer(int action, int start, int dir, int count, int forceit) { buf_T *buf; buf_T *bp; - int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL - || action == DOBUF_WIPE); + bool update_jumplist = true; + bool unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL + || action == DOBUF_WIPE); switch (start) { case DOBUF_FIRST: @@ -1355,7 +1390,11 @@ int do_buffer(int action, int start, int dir, int count, int forceit) // If the buffer to be deleted is not the current one, delete it here. if (buf != curbuf) { + // Remove the buffer to be deleted from the jump list. + buf_remove_from_jumplist(buf); + close_windows(buf, false); + if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0) { close_buffer(NULL, buf, action, false, false); } @@ -1375,42 +1414,53 @@ int do_buffer(int action, int start, int dir, int count, int forceit) if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf)) { buf = au_new_curbuf.br_buf; } else if (curwin->w_jumplistlen > 0) { - int jumpidx; - - jumpidx = curwin->w_jumplistidx - 1; - if (jumpidx < 0) { - jumpidx = curwin->w_jumplistlen - 1; - } + // Remove the current buffer from the jump list. + buf_remove_from_jumplist(curbuf); + + // It's possible that we removed all jump list entries, in that case we need to try another + // approach + if (curwin->w_jumplistlen > 0) { + // If the index is the same as the length, the current position was not yet added to the jump + // list. So we can safely go back to the last entry and search from there. + if (curwin->w_jumplistidx == curwin->w_jumplistlen) { + curwin->w_jumplistidx = curwin->w_jumplistlen - 1; + } - forward = jumpidx; - while (jumpidx != curwin->w_jumplistidx) { - buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum); - if (buf != NULL) { - // Skip current and unlisted bufs. Also skip a quickfix - // buffer, it might be deleted soon. - if (buf == curbuf || !buf->b_p_bl || bt_quickfix(buf)) { - buf = NULL; - } else if (buf->b_ml.ml_mfp == NULL) { - // skip unloaded buf, but may keep it for later - if (bp == NULL) { - bp = buf; + int jumpidx = curwin->w_jumplistidx; + + forward = jumpidx; + do { + buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum); + + if (buf != NULL) { + // Skip unlisted bufs. Also skip a quickfix + // buffer, it might be deleted soon. + if (!buf->b_p_bl || bt_quickfix(buf)) { + buf = NULL; + } else if (buf->b_ml.ml_mfp == NULL) { + // skip unloaded buf, but may keep it for later + if (bp == NULL) { + bp = buf; + } + buf = NULL; } - buf = NULL; } - } - if (buf != NULL) { // found a valid buffer: stop searching - break; - } - // advance to older entry in jump list - if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) { - break; - } - if (--jumpidx < 0) { - jumpidx = curwin->w_jumplistlen - 1; - } - if (jumpidx == forward) { // List exhausted for sure - break; - } + if (buf != NULL) { // found a valid buffer: stop searching + curwin->w_jumplistidx = jumpidx; + update_jumplist = false; + break; + } + // advance to older entry in jump list + if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) { + break; + } + if (--jumpidx < 0) { + jumpidx = curwin->w_jumplistlen - 1; + } + if (jumpidx == forward) { // List exhausted for sure + break; + } + } while (jumpidx != curwin->w_jumplistidx); } } @@ -1506,7 +1556,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } // Go to the other buffer. - set_curbuf(buf, action); + set_curbuf(buf, action, update_jumplist); if (action == DOBUF_SPLIT) { RESET_BINDING(curwin); // reset 'scrollbind' and 'cursorbind' @@ -1528,14 +1578,18 @@ int do_buffer(int action, int start, int dir, int count, int forceit) /// DOBUF_UNLOAD unload it /// DOBUF_DEL delete it /// DOBUF_WIPE wipe it out -void set_curbuf(buf_T *buf, int action) +void set_curbuf(buf_T *buf, int action, bool update_jumplist) { buf_T *prevbuf; int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL || action == DOBUF_WIPE); OptInt old_tw = curbuf->b_p_tw; + const int last_winid = get_last_winid(); + + if (update_jumplist) { + setpcmark(); + } - setpcmark(); if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) { curwin->w_alt_fnum = curbuf->b_fnum; // remember alternate file } @@ -1559,7 +1613,11 @@ void set_curbuf(buf_T *buf, int action) if (prevbuf == curwin->w_buffer) { reset_synblock(curwin); } - if (unload) { + // autocommands may have opened a new window + // with prevbuf, grr + if (unload + || (last_winid != get_last_winid() + && strchr("wdu", prevbuf->b_p_bh[0]) != NULL)) { close_windows(prevbuf, false); } if (bufref_valid(&prevbufref) && !aborting()) { @@ -1589,6 +1647,11 @@ void set_curbuf(buf_T *buf, int action) // If curwin->w_buffer is null, enter_buffer() will make it valid again bool valid = buf_valid(buf); if ((valid && buf != curbuf && !aborting()) || curwin->w_buffer == NULL) { + // autocommands changed curbuf and we will move to another + // buffer soon, so decrement curbuf->b_nwindows + if (curbuf != NULL && prevbuf != curbuf) { + curbuf->b_nwindows--; + } // If the buffer is not valid but curwin->w_buffer is NULL we must // enter some buffer. Using the last one is hopefully OK. if (!valid) { @@ -1666,7 +1729,7 @@ void enter_buffer(buf_T *buf) need_fileinfo = true; // display file info after redraw } // check if file changed - (void)buf_check_timestamp(curbuf); + buf_check_timestamp(curbuf); curwin->w_topline = 1; curwin->w_topfill = 0; @@ -1691,12 +1754,12 @@ void enter_buffer(buf_T *buf) do_autochdir(); if (curbuf->b_kmap_state & KEYMAP_INIT) { - (void)keymap_init(); + keymap_init(); } // May need to set the spell language. Can only do this after the buffer // has been properly setup. if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { - (void)parse_spelllang(curwin); + parse_spelllang(curwin); } curbuf->b_last_used = time(NULL); @@ -1844,7 +1907,6 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags) buf = xcalloc(1, sizeof(buf_T)); // init b: variables buf->b_vars = tv_dict_alloc(); - buf->b_signcols.sentinel = 0; init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); buf_init_changedtick(buf); } @@ -1963,7 +2025,7 @@ bool curbuf_reusable(void) /// Free the memory for the options of a buffer. /// If "free_p_ff" is true also free 'fileformat', 'buftype' and /// 'fileencoding'. -void free_buf_options(buf_T *buf, int free_p_ff) +void free_buf_options(buf_T *buf, bool free_p_ff) { if (free_p_ff) { clear_string_option(&buf->b_p_fenc); @@ -2151,7 +2213,7 @@ buf_T *buflist_findname_exp(char *fname) #else false #endif - ); // NOLINT(whitespace/parens) + ); if (ffname != NULL) { buf = buflist_findname(ffname); xfree(ffname); @@ -2226,7 +2288,7 @@ int buflist_findpat(const char *pattern, const char *pattern_end, bool unlisted, return -1; } char *patend = pat + strlen(pat) - 1; - int toggledollar = (patend > pat && *patend == '$'); + bool toggledollar = (patend > pat && *patend == '$'); // First try finding a listed buffer. If not found and "unlisted" // is true, try finding an unlisted buffer. @@ -2325,10 +2387,8 @@ static int buf_time_compare(const void *s1, const void *s2) /// @return OK if matches found, FAIL otherwise. int ExpandBufnames(char *pat, int *num_file, char ***file, int options) { - int count = 0; - int round; - char *p; bufmatch_T *matches = NULL; + bool to_free = false; *num_file = 0; // return values in case of FAIL *file = NULL; @@ -2340,125 +2400,115 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options) const bool fuzzy = cmdline_fuzzy_complete(pat); char *patc = NULL; + fuzmatch_str_T *fuzmatch = NULL; + regmatch_T regmatch; + // Make a copy of "pat" and change "^" to "\(^\|[\/]\)" (if doing regular // expression matching) if (!fuzzy) { - if (*pat == '^') { - patc = xmalloc(strlen(pat) + 11); - STRCPY(patc, "\\(^\\|[\\/]\\)"); - STRCPY(patc + 11, pat + 1); + if (*pat == '^' && pat[1] != NUL) { + patc = xstrdup(pat + 1); + to_free = true; + } else if (*pat == '^') { + patc = ""; } else { patc = pat; } + regmatch.regprog = vim_regcomp(patc, RE_MAGIC); } - fuzmatch_str_T *fuzmatch = NULL; - // attempt == 0: try match with '\<', match at start of word - // attempt == 1: try match without '\<', match anywhere - for (int attempt = 0; attempt <= (fuzzy ? 0 : 1); attempt++) { - regmatch_T regmatch; - if (!fuzzy) { - if (attempt > 0 && patc == pat) { - break; // there was no anchor, no need to try again + int count = 0; + int score = 0; + // round == 1: Count the matches. + // round == 2: Build the array to keep the matches. + for (int round = 1; round <= 2; round++) { + count = 0; + FOR_ALL_BUFFERS(buf) { + if (!buf->b_p_bl) { // skip unlisted buffers + continue; } - regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC); - } - - int score = 0; - // round == 1: Count the matches. - // round == 2: Build the array to keep the matches. - for (round = 1; round <= 2; round++) { - count = 0; - FOR_ALL_BUFFERS(buf) { - if (!buf->b_p_bl) { // skip unlisted buffers + if (options & BUF_DIFF_FILTER) { + // Skip buffers not suitable for + // :diffget or :diffput completion. + if (buf == curbuf || !diff_mode_buf(buf)) { continue; } - if (options & BUF_DIFF_FILTER) { - // Skip buffers not suitable for - // :diffget or :diffput completion. - if (buf == curbuf || !diff_mode_buf(buf)) { - continue; - } - } + } - if (!fuzzy) { - if (regmatch.regprog == NULL) { - // invalid pattern, possibly after recompiling - if (patc != pat) { - xfree(patc); - } - return FAIL; - } - p = buflist_match(®match, buf, p_wic); - } else { - p = NULL; - // first try matching with the short file name - if ((score = fuzzy_match_str(buf->b_sfname, pat)) != 0) { - p = buf->b_sfname; - } - if (p == NULL) { - // next try matching with the full path file name - if ((score = fuzzy_match_str(buf->b_ffname, pat)) != 0) { - p = buf->b_ffname; - } + char *p = NULL; + if (!fuzzy) { + if (regmatch.regprog == NULL) { + // invalid pattern, possibly after recompiling + if (to_free) { + xfree(patc); } + return FAIL; } - - if (p == NULL) { - continue; - } - - if (round == 1) { - count++; - continue; - } - - if (options & WILD_HOME_REPLACE) { - p = home_replace_save(buf, p); - } else { - p = xstrdup(p); + p = buflist_match(®match, buf, p_wic); + } else { + p = NULL; + // first try matching with the short file name + if ((score = fuzzy_match_str(buf->b_sfname, pat)) != 0) { + p = buf->b_sfname; } - - if (!fuzzy) { - if (matches != NULL) { - matches[count].buf = buf; - matches[count].match = p; - count++; - } else { - (*file)[count++] = p; + if (p == NULL) { + // next try matching with the full path file name + if ((score = fuzzy_match_str(buf->b_ffname, pat)) != 0) { + p = buf->b_ffname; } - } else { - fuzmatch[count].idx = count; - fuzmatch[count].str = p; - fuzmatch[count].score = score; - count++; } } - if (count == 0) { // no match found, break here - break; + + if (p == NULL) { + continue; } + if (round == 1) { - if (!fuzzy) { - *file = xmalloc((size_t)count * sizeof(**file)); - if (options & WILD_BUFLASTUSED) { - matches = xmalloc((size_t)count * sizeof(*matches)); - } + count++; + continue; + } + + if (options & WILD_HOME_REPLACE) { + p = home_replace_save(buf, p); + } else { + p = xstrdup(p); + } + + if (!fuzzy) { + if (matches != NULL) { + matches[count].buf = buf; + matches[count].match = p; + count++; } else { - fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T)); + (*file)[count++] = p; } + } else { + fuzmatch[count].idx = count; + fuzmatch[count].str = p; + fuzmatch[count].score = score; + count++; } } - - if (!fuzzy) { - vim_regfree(regmatch.regprog); - if (count) { // match(es) found, break here - break; + if (count == 0) { // no match found, break here + break; + } + if (round == 1) { + if (!fuzzy) { + *file = xmalloc((size_t)count * sizeof(**file)); + if (options & WILD_BUFLASTUSED) { + matches = xmalloc((size_t)count * sizeof(*matches)); + } + } else { + fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T)); } } } - if (!fuzzy && patc != pat) { - xfree(patc); + if (!fuzzy) { + vim_regfree(regmatch.regprog); + if (to_free) { + xfree(patc); + } } if (!fuzzy) { @@ -2722,7 +2772,7 @@ void get_winopts(buf_T *buf) curwin->w_changelistidx = wip->wi_changelistidx; } - if (curwin->w_float_config.style == kWinStyleMinimal) { + if (curwin->w_config.style == kWinStyleMinimal) { didset_window_options(curwin, false); win_set_minimal_style(curwin); } @@ -2757,8 +2807,6 @@ linenr_T buflist_findlnum(buf_T *buf) void buflist_list(exarg_T *eap) { buf_T *buf = firstbuf; - int len; - int i; garray_T buflist; buf_T **buflist_data = NULL; @@ -2823,21 +2871,21 @@ void buflist_list(exarg_T *eap) } msg_putchar('\n'); - len = vim_snprintf(IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", - buf->b_fnum, - buf->b_p_bl ? ' ' : 'u', - buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), - buf->b_ml.ml_mfp == NULL ? ' ' : (buf->b_nwindows == 0 ? 'h' : 'a'), - ro_char, - changed_char, - NameBuff); + int len = vim_snprintf(IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", + buf->b_fnum, + buf->b_p_bl ? ' ' : 'u', + buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), + buf->b_ml.ml_mfp == NULL ? ' ' : (buf->b_nwindows == 0 ? 'h' : 'a'), + ro_char, + changed_char, + NameBuff); if (len > IOSIZE - 20) { len = IOSIZE - 20; } // put "line 999" in column 40 or after the file name - i = 40 - vim_strsize(IObuff); + int i = 40 - vim_strsize(IObuff); do { IObuff[len++] = ' '; } while (--i > 0 && len < IOSIZE - 18); @@ -3136,7 +3184,7 @@ static bool buf_same_file_id(buf_T *buf, FileID *file_id) /// Print info about the current buffer. /// /// @param fullname when non-zero print full path -void fileinfo(int fullname, int shorthelp, int dont_truncate) +void fileinfo(int fullname, int shorthelp, bool dont_truncate) { char *name; int n; @@ -3210,7 +3258,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate) (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); } - (void)append_arg_number(curwin, buffer, IOSIZE); + append_arg_number(curwin, buffer, IOSIZE); if (dont_truncate) { // Temporarily set msg_scroll to avoid the message being truncated. @@ -3253,7 +3301,6 @@ void maketitle(void) char *title_str = NULL; char *icon_str = NULL; int maxlen = 0; - int len; char buf[IOSIZE]; if (!redrawing()) { @@ -3278,7 +3325,7 @@ void maketitle(void) if (*p_titlestring != NUL) { if (stl_syntax & STL_IN_TITLE) { build_stl_str_hl(curwin, buf, sizeof(buf), p_titlestring, - "titlestring", 0, 0, maxlen, NULL, NULL, NULL); + kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL, NULL); title_str = buf; } else { title_str = p_titlestring; @@ -3376,14 +3423,14 @@ void maketitle(void) #undef SPACE_FOR_ARGNR } } - int mustset = value_change(title_str, &lasttitle); + bool mustset = value_change(title_str, &lasttitle); if (p_icon) { icon_str = buf; if (*p_iconstring != NUL) { if (stl_syntax & STL_IN_ICON) { build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring, - "iconstring", 0, 0, 0, NULL, NULL, NULL); + kOptIconstring, 0, 0, 0, NULL, NULL, NULL, NULL); } else { icon_str = p_iconstring; } @@ -3396,10 +3443,10 @@ void maketitle(void) } *icon_str = NUL; // Truncate name at 100 bytes. - len = (int)strlen(buf_p); + int len = (int)strlen(buf_p); if (len > 100) { len -= 100; - len += utf_cp_tail_off(buf_p, buf_p + len) + 1; + len += utf_cp_bounds(buf_p, buf_p + len).end_off; buf_p += len; } STRCPY(icon_str, buf_p); @@ -3558,16 +3605,12 @@ bool bt_prompt(buf_T *buf) /// Open a window for a number of buffers. void ex_buffer_all(exarg_T *eap) { - buf_T *buf; win_T *wp, *wpnext; int split_ret = OK; - bool p_ea_save; int open_wins = 0; - int r; linenr_T count; // Maximum number of windows to open. int all; // When true also load inactive buffers. int had_tab = cmdmod.cmod_tab; - tabpage_T *tpnext; if (eap->addr_count == 0) { // make as many windows as possible count = 9999; @@ -3592,7 +3635,7 @@ void ex_buffer_all(exarg_T *eap) goto_tabpage_tp(first_tabpage, true, true); } while (true) { - tpnext = curtab->tp_next; + tabpage_T *tpnext = curtab->tp_next; // Try to close floating windows first for (wp = lastwin->w_floating ? lastwin : firstwin; wp != NULL; wp = wpnext) { wpnext = wp->w_floating @@ -3637,7 +3680,7 @@ void ex_buffer_all(exarg_T *eap) // lastwin may be aucmd_win win_enter(lastwin_nofloating(), false); autocmd_no_leave++; - for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) { + for (buf_T *buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) { // Check if this buffer needs a window if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl) { continue; @@ -3667,7 +3710,7 @@ void ex_buffer_all(exarg_T *eap) bufref_T bufref; set_bufref(&bufref, buf); // Split the window and put the buffer in it. - p_ea_save = p_ea; + bool p_ea_save = p_ea; p_ea = true; // use space from all windows split_ret = win_split(0, WSP_ROOM | WSP_BELOW); open_wins++; @@ -3678,7 +3721,7 @@ void ex_buffer_all(exarg_T *eap) // Open the buffer in this window. swap_exists_action = SEA_DIALOG; - set_curbuf(buf, DOBUF_GOTO); + set_curbuf(buf, DOBUF_GOTO, false); if (!bufref_valid(&bufref)) { // Autocommands deleted the buffer. swap_exists_action = SEA_NONE; @@ -3708,7 +3751,7 @@ void ex_buffer_all(exarg_T *eap) os_breakcheck(); if (got_int) { - (void)vgetc(); // only break the file loading, not the rest + vgetc(); // only break the file loading, not the rest break; } // Autocommands deleted the buffer or aborted script processing!!! @@ -3726,8 +3769,8 @@ void ex_buffer_all(exarg_T *eap) // Close superfluous windows. for (wp = lastwin; open_wins > count;) { - r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer) - || autowrite(wp->w_buffer, false) == OK) && !is_aucmd_win(wp); + bool r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer) + || autowrite(wp->w_buffer, false) == OK) && !is_aucmd_win(wp); if (!win_valid(wp)) { // BufWrite Autocommands made the window invalid, start over wp = lastwin; @@ -3800,8 +3843,8 @@ static int chk_modeline(linenr_T lnum, int flags) int prev = -1; for (s = ml_get(lnum); *s != NUL; s++) { if (prev == -1 || ascii_isspace(prev)) { - if ((prev != -1 && strncmp(s, "ex:", (size_t)3) == 0) - || strncmp(s, "vi:", (size_t)3) == 0) { + if ((prev != -1 && strncmp(s, "ex:", 3) == 0) + || strncmp(s, "vi:", 3) == 0) { break; } // Accept both "vim" and "Vim". @@ -3866,8 +3909,8 @@ static int chk_modeline(linenr_T lnum, int flags) // "vi:set opt opt opt: foo" -- foo not interpreted // "vi:opt opt opt: foo" -- foo interpreted // Accept "se" for compatibility with Elvis. - if (strncmp(s, "set ", (size_t)4) == 0 - || strncmp(s, "se ", (size_t)3) == 0) { + if (strncmp(s, "set ", 4) == 0 + || strncmp(s, "se ", 3) == 0) { if (*e != ':') { // no terminating ':'? break; } @@ -4015,6 +4058,9 @@ char *buf_spname(buf_T *buf) if (buf->b_fname != NULL) { return buf->b_fname; } + if (buf == cmdwin_buf) { + return _("[Command Line]"); + } if (bt_prompt(buf)) { return _("[Prompt]"); } @@ -4026,67 +4072,6 @@ char *buf_spname(buf_T *buf) return NULL; } -/// Invalidate the signcolumn if needed after deleting a sign ranging from line1 to line2. -void buf_signcols_del_check(buf_T *buf, linenr_T line1, linenr_T line2) -{ - linenr_T sent = buf->b_signcols.sentinel; - if (sent >= line1 && sent <= line2) { - // When removed sign overlaps the sentinel line, entire buffer needs to be checked. - buf->b_signcols.sentinel = buf->b_signcols.size = 0; - } -} - -/// Invalidate the signcolumn if needed after adding a sign ranging from line1 to line2. -void buf_signcols_add_check(buf_T *buf, linenr_T line1, linenr_T line2) -{ - if (!buf->b_signcols.sentinel) { - return; - } - - linenr_T sent = buf->b_signcols.sentinel; - if (sent >= line1 && sent <= line2) { - // If added sign overlaps sentinel line, increment without invalidating. - if (buf->b_signcols.size == buf->b_signcols.max) { - buf->b_signcols.max++; - } - buf->b_signcols.size++; - return; - } - - if (line1 < buf->b_signcols.invalid_top) { - buf->b_signcols.invalid_top = line1; - } - if (line2 > buf->b_signcols.invalid_bot) { - buf->b_signcols.invalid_bot = line2; - } -} - -int buf_signcols(buf_T *buf, int max) -{ - if (!buf->b_signs_with_text) { - buf->b_signcols.size = 0; - } else if (max <= 1 && buf->b_signs_with_text >= (size_t)max) { - buf->b_signcols.size = max; - } else { - linenr_T sent = buf->b_signcols.sentinel; - if (!sent || max > buf->b_signcols.max) { - // Recheck if the window scoped maximum 'signcolumn' is greater than the - // previous maximum or if there is no sentinel line yet. - buf->b_signcols.invalid_top = sent ? sent : 1; - buf->b_signcols.invalid_bot = sent ? sent : buf->b_ml.ml_line_count; - } - - if (buf->b_signcols.invalid_bot) { - decor_validate_signcols(buf, max); - } - } - - buf->b_signcols.max = max; - buf->b_signcols.invalid_top = MAXLNUM; - buf->b_signcols.invalid_bot = 0; - return buf->b_signcols.size; -} - /// Get "buf->b_fname", use "[No Name]" if it is NULL. char *buf_get_fname(const buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL @@ -4193,6 +4178,7 @@ void wipe_buffer(buf_T *buf, bool aucmd) /// - Always considered 'nomodified' /// /// @param bufnr Buffer to switch to, or 0 to create a new buffer. +/// @param bufname Buffer name, or NULL. /// /// @see curbufIsChanged() /// @@ -4202,12 +4188,60 @@ int buf_open_scratch(handle_T bufnr, char *bufname) if (do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL) == FAIL) { return FAIL; } - apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); - (void)setfname(curbuf, bufname, NULL, true); - apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); - set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); - set_option_value_give_err("bt", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); - set_option_value_give_err("swf", BOOLEAN_OPTVAL(false), OPT_LOCAL); + if (bufname != NULL) { + apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); + setfname(curbuf, bufname, NULL, true); + apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); + } + set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); + set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); + set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); RESET_BINDING(curwin); return OK; } + +bool buf_is_empty(buf_T *buf) +{ + return buf->b_ml.ml_line_count == 1 && *ml_get_buf(buf, 1) == '\0'; +} + +/// Increment b:changedtick value +/// +/// Also checks b: for consistency in case of debug build. +/// +/// @param[in,out] buf Buffer to increment value in. +void buf_inc_changedtick(buf_T *const buf) + FUNC_ATTR_NONNULL_ALL +{ + buf_set_changedtick(buf, buf_get_changedtick(buf) + 1); +} + +/// Set b:changedtick, also checking b: for consistency in debug build +/// +/// @param[out] buf Buffer to set changedtick in. +/// @param[in] changedtick New value. +void buf_set_changedtick(buf_T *const buf, const varnumber_T changedtick) + FUNC_ATTR_NONNULL_ALL +{ + typval_T old_val = buf->changedtick_di.di_tv; + +#ifndef NDEBUG + dictitem_T *const changedtick_di = tv_dict_find(buf->b_vars, S_LEN("changedtick")); + assert(changedtick_di != NULL); + assert(changedtick_di->di_tv.v_type == VAR_NUMBER); + assert(changedtick_di->di_tv.v_lock == VAR_FIXED); + // For some reason formatc does not like the below. +# ifndef UNIT_TESTING_LUA_PREPROCESSING + assert(changedtick_di->di_flags == (DI_FLAGS_RO|DI_FLAGS_FIX)); +# endif + assert(changedtick_di == (dictitem_T *)&buf->changedtick_di); +#endif + buf->changedtick_di.di_tv.vval.v_number = changedtick; + + if (tv_dict_is_watched(buf->b_vars)) { + tv_dict_watcher_notify(buf->b_vars, + (char *)buf->changedtick_di.di_key, + &buf->changedtick_di.di_tv, + &old_val); + } +} diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index 36e70d1927..4c5023d39a 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -1,18 +1,15 @@ #pragma once -#include <assert.h> -#include <stdbool.h> -#include <stddef.h> +#include <stdint.h> -#include "nvim/buffer_defs.h" // IWYU pragma: export -#include "nvim/eval/typval.h" +#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" -#include "nvim/ex_cmds_defs.h" +#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" +#include "nvim/gettext_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" -#include "nvim/memline.h" -#include "nvim/memline_defs.h" -#include "nvim/pos_defs.h" +#include "nvim/marktree_defs.h" +#include "nvim/types_defs.h" /// Values for buflist_getfile() enum getf_values { @@ -74,38 +71,6 @@ EXTERN char *msg_qflist INIT( = N_("[Quickfix List]")); # include "buffer.h.generated.h" #endif -static inline void buf_set_changedtick(buf_T *buf, varnumber_T changedtick) - REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; - -/// Set b:changedtick, also checking b: for consistency in debug build -/// -/// @param[out] buf Buffer to set changedtick in. -/// @param[in] changedtick New value. -static inline void buf_set_changedtick(buf_T *const buf, const varnumber_T changedtick) -{ - typval_T old_val = buf->changedtick_di.di_tv; - -#ifndef NDEBUG - dictitem_T *const changedtick_di = tv_dict_find(buf->b_vars, S_LEN("changedtick")); - assert(changedtick_di != NULL); - assert(changedtick_di->di_tv.v_type == VAR_NUMBER); - assert(changedtick_di->di_tv.v_lock == VAR_FIXED); - // For some reason formatc does not like the below. -# ifndef UNIT_TESTING_LUA_PREPROCESSING - assert(changedtick_di->di_flags == (DI_FLAGS_RO|DI_FLAGS_FIX)); -# endif - assert(changedtick_di == (dictitem_T *)&buf->changedtick_di); -#endif - buf->changedtick_di.di_tv.vval.v_number = changedtick; - - if (tv_dict_is_watched(buf->b_vars)) { - tv_dict_watcher_notify(buf->b_vars, - (char *)buf->changedtick_di.di_key, - &buf->changedtick_di.di_tv, - &old_val); - } -} - static inline varnumber_T buf_get_changedtick(const buf_T *buf) REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; @@ -120,20 +85,7 @@ static inline varnumber_T buf_get_changedtick(const buf_T *const buf) return buf->changedtick_di.di_tv.vval.v_number; } -static inline void buf_inc_changedtick(buf_T *buf) - REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; - -/// Increment b:changedtick value -/// -/// Also checks b: for consistency in case of debug build. -/// -/// @param[in,out] buf Buffer to increment value in. -static inline void buf_inc_changedtick(buf_T *const buf) -{ - buf_set_changedtick(buf, buf_get_changedtick(buf) + 1); -} - -static inline bool buf_is_empty(buf_T *buf) +static inline uint32_t buf_meta_total(const buf_T *b, MetaIndex m) { - return buf->b_ml.ml_line_count == 1 && *ml_get_buf(buf, 1) == '\0'; + return b->b_marktree->meta_root[m]; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index e59539f900..9653b5e09c 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -4,7 +4,15 @@ #include <stdint.h> #include <stdio.h> -typedef struct file_buffer buf_T; +#include "nvim/arglist_defs.h" +#include "nvim/grid_defs.h" +#include "nvim/mapping_defs.h" +#include "nvim/marktree_defs.h" +#include "nvim/memline_defs.h" +#include "nvim/option_defs.h" +#include "nvim/os/fs_defs.h" +#include "nvim/statusline_defs.h" +#include "nvim/undo_defs.h" /// Reference to a buffer that stores the value of buf_free_count. /// bufref_valid() only needs to check "buf" when the count differs. @@ -14,24 +22,6 @@ typedef struct { int br_buf_free_count; } bufref_T; -#include "klib/kvec.h" -#include "nvim/api/private/defs.h" -#include "nvim/arglist_defs.h" -#include "nvim/eval/typval_defs.h" -#include "nvim/extmark_defs.h" -#include "nvim/garray_defs.h" -#include "nvim/grid_defs.h" -#include "nvim/hashtab_defs.h" -#include "nvim/highlight_defs.h" -#include "nvim/map_defs.h" -#include "nvim/mapping_defs.h" -#include "nvim/mark_defs.h" -#include "nvim/marktree.h" -#include "nvim/option_vars.h" -#include "nvim/pos_defs.h" -#include "nvim/statusline_defs.h" -#include "nvim/undo_defs.h" - #define GETFILE_SUCCESS(x) ((x) <= 0) #define MODIFIABLE(buf) (buf->b_p_ma) @@ -80,20 +70,12 @@ typedef struct { // Mask to check for flags that prevent normal writing #define BF_WRITE_MASK (BF_NOTEDITED + BF_NEW + BF_READERR) -typedef struct window_S win_T; typedef struct wininfo_S wininfo_T; typedef struct frame_S frame_T; typedef uint64_t disptick_T; // display tick type -#include "nvim/memline_defs.h" -#include "nvim/os/fs_defs.h" -#include "nvim/regexp_defs.h" -#include "nvim/sign_defs.h" -#include "nvim/syntax_defs.h" -#include "nvim/terminal.h" - // The taggy struct is used to store the information about a :tag command. -typedef struct taggy { +typedef struct { char *tagname; // tag name fmark_T fmark; // cursor position BEFORE ":tag" int cur_match; // match number @@ -357,8 +339,6 @@ typedef struct { #define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF, LUA_NOREF, \ LUA_NOREF, LUA_NOREF, false, false } -EXTERN int curbuf_splice_pending INIT( = 0); - #define BUF_HAS_QF_ENTRY 1 #define BUF_HAS_LL_ENTRY 2 @@ -548,6 +528,7 @@ struct file_buffer { Callback b_ofu_cb; ///< 'omnifunc' callback char *b_p_tfu; ///< 'tagfunc' Callback b_tfu_cb; ///< 'tagfunc' callback + char *b_p_urf; ///< 'userregfunc' int b_p_eof; ///< 'endoffile' int b_p_eol; ///< 'endofline' int b_p_fixeol; ///< 'fixendofline' @@ -705,11 +686,12 @@ struct file_buffer { // may use a different synblock_T. struct { - int size; // last calculated number of sign columns - int max; // maximum value size is valid for. - linenr_T sentinel; // a line number which is holding up the signcolumn - linenr_T invalid_top; // first invalid line number that needs to be checked - linenr_T invalid_bot; // last invalid line number that needs to be checked + int max; // maximum number of signs on a single line + int count[SIGN_SHOW_MAX]; // number of lines with number of signs + bool resized; // whether max changed at start of redraw + bool autom; // whether 'signcolumn' is displayed in "auto:n>1" + // configured window. "b_signcols" calculation + // is skipped if false. } b_signcols; Terminal *terminal; // Terminal instance associated with the buffer @@ -720,10 +702,6 @@ struct file_buffer { MarkTree b_marktree[1]; Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces - size_t b_virt_text_inline; // number of inline virtual texts - size_t b_virt_line_blocks; // number of virt_line blocks - size_t b_signs; // number of sign extmarks - size_t b_signs_with_text; // number of sign extmarks with text // array of channel_id:s which have asked to receive updates for this // buffer. @@ -736,7 +714,7 @@ struct file_buffer { // Measurements of the deleted or replaced region since the last update // event. Some consumers of buffer changes need to know the byte size (like - // tree-sitter) or the corresponding UTF-32/UTF-16 size (like LSP) of the + // treesitter) or the corresponding UTF-32/UTF-16 size (like LSP) of the // deleted text. size_t deleted_bytes; size_t deleted_bytes2; @@ -816,7 +794,7 @@ struct tabpage_S { // may not reflect what is actually in the buffer. When wl_valid is false, // the entries can only be used to count the number of displayed lines used. // wl_lnum and wl_lastlnum are invalid too. -typedef struct w_line { +typedef struct { linenr_T wl_lnum; // buffer line number for logical line uint16_t wl_size; // height in screen lines char wl_valid; // true values are valid for text in buffer @@ -904,12 +882,7 @@ enum { kFloatAnchorSouth = 2, }; -// NW -> 0 -// NE -> kFloatAnchorEast -// SW -> kFloatAnchorSouth -// SE -> kFloatAnchorSouth | kFloatAnchorEast -EXTERN const char *const float_anchor_str[] INIT( = { "NW", "NE", "SW", "SE" }); - +/// Keep in sync with float_relative_str[] in nvim_win_get_config() typedef enum { kFloatRelativeEditor = 0, kFloatRelativeWindow = 1, @@ -917,8 +890,13 @@ typedef enum { kFloatRelativeMouse = 3, } FloatRelative; -EXTERN const char *const float_relative_str[] INIT( = { "editor", "win", - "cursor", "mouse" }); +/// Keep in sync with win_split_str[] in nvim_win_get_config() (api/win_config.c) +typedef enum { + kWinSplitLeft = 0, + kWinSplitRight = 1, + kWinSplitAbove = 2, + kWinSplitBelow = 3, +} WinSplit; typedef enum { kWinStyleUnused = 0, @@ -936,6 +914,7 @@ typedef enum { kBorderTextFooter = 1, } BorderTextType; +/// See ":help nvim_open_win()" for documentation. typedef struct { Window window; lpos_T bufpos; @@ -945,6 +924,7 @@ typedef struct { FloatRelative relative; bool external; bool focusable; + WinSplit split; int zindex; WinStyle style; bool border; @@ -963,18 +943,19 @@ typedef struct { bool noautocmd; bool fixed; bool hide; -} FloatConfig; - -#define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \ - .bufpos = { -1, 0 }, \ - .row = 0, .col = 0, .anchor = 0, \ - .relative = 0, .external = false, \ - .focusable = true, \ - .zindex = kZIndexFloatDefault, \ - .style = kWinStyleUnused, \ - .noautocmd = false, \ - .hide = false, \ - .fixed = false }) +} WinConfig; + +#define WIN_CONFIG_INIT ((WinConfig){ .height = 0, .width = 0, \ + .bufpos = { -1, 0 }, \ + .row = 0, .col = 0, .anchor = 0, \ + .relative = 0, .external = false, \ + .focusable = true, \ + .split = 0, \ + .zindex = kZIndexFloatDefault, \ + .style = kWinStyleUnused, \ + .noautocmd = false, \ + .hide = false, \ + .fixed = false }) // Structure to store last cursor position and topline. Used by check_lnums() // and reset_lnums(). @@ -987,41 +968,41 @@ typedef struct { /// Characters from the 'listchars' option. typedef struct { - int eol; - int ext; - int prec; - int nbsp; - int space; - int tab1; ///< first tab character - int tab2; ///< second tab character - int tab3; ///< third tab character - int lead; - int trail; - int *multispace; - int *leadmultispace; - int conceal; + schar_T eol; + schar_T ext; + schar_T prec; + schar_T nbsp; + schar_T space; + schar_T tab1; ///< first tab character + schar_T tab2; ///< second tab character + schar_T tab3; ///< third tab character + schar_T lead; + schar_T trail; + schar_T *multispace; + schar_T *leadmultispace; + schar_T conceal; } lcs_chars_T; /// Characters from the 'fillchars' option. typedef struct { - int stl; - int stlnc; - int wbr; - int horiz; - int horizup; - int horizdown; - int vert; - int vertleft; - int vertright; - int verthoriz; - int fold; - int foldopen; ///< when fold is open - int foldclosed; ///< when fold is closed - int foldsep; ///< continuous fold marker - int diff; - int msgsep; - int eob; - int lastline; + schar_T stl; + schar_T stlnc; + schar_T wbr; + schar_T horiz; + schar_T horizup; + schar_T horizdown; + schar_T vert; + schar_T vertleft; + schar_T vertright; + schar_T verthoriz; + schar_T fold; + schar_T foldopen; ///< when fold is open + schar_T foldclosed; ///< when fold is closed + schar_T foldsep; ///< continuous fold marker + schar_T diff; + schar_T msgsep; + schar_T eob; + schar_T lastline; } fcs_chars_T; /// Structure which contains all information that belongs to a window. @@ -1040,6 +1021,8 @@ struct window_S { int w_ns_hl_active; int *w_ns_hl_attr; + Set(uint32_t) w_ns_set; + int w_hl_id_normal; ///< 'winhighlight' normal id int w_hl_attr_normal; ///< 'winhighlight' normal final attrs int w_hl_attr_normalnc; ///< 'winhighlight' NormalNC final attrs @@ -1297,8 +1280,9 @@ struct window_S { ScreenGrid w_grid; // the grid specific to the window ScreenGrid w_grid_alloc; // the grid specific to the window bool w_pos_changed; // true if window position changed - bool w_floating; ///< whether the window is floating - FloatConfig w_float_config; + bool w_floating; ///< whether the window is floating + bool w_float_is_info; // the floating window is info float + WinConfig w_config; // w_fraction is the fractional row of the cursor within the window, from // 0 at the top row to FRACTION_MULT at the last row. @@ -1331,9 +1315,3 @@ struct window_S { // Size of the w_statuscol_click_defs array size_t w_statuscol_click_defs_size; }; - -/// Macros defined in Vim, but not in Neovim -// uncrustify:off -#define CHANGEDTICK(buf) \ - (=== Include buffer.h & use buf_(get|set|inc) _changedtick ===) -// uncrustify:on diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index 01bcb9d7be..e725678937 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -11,7 +11,6 @@ #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/lua/executor.h" @@ -22,7 +21,7 @@ #include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "buffer_updates.c.generated.h" // IWYU pragma: export +# include "buffer_updates.c.generated.h" #endif // Register a channel. Return True if the channel was added, or already added. @@ -56,36 +55,33 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, BufUpdateCallbacks cb kv_push(buf->update_channels, channel_id); if (send_buffer) { - Array args = ARRAY_DICT_INIT; - args.size = 6; - args.items = xcalloc(args.size, sizeof(Object)); + MAXSIZE_TEMP_ARRAY(args, 6); // the first argument is always the buffer handle - args.items[0] = BUFFER_OBJ(buf->handle); - args.items[1] = INTEGER_OBJ(buf_get_changedtick(buf)); + ADD_C(args, BUFFER_OBJ(buf->handle)); + ADD_C(args, INTEGER_OBJ(buf_get_changedtick(buf))); // the first line that changed (zero-indexed) - args.items[2] = INTEGER_OBJ(0); + ADD_C(args, INTEGER_OBJ(0)); // the last line that was changed - args.items[3] = INTEGER_OBJ(-1); - Array linedata = ARRAY_DICT_INIT; + ADD_C(args, INTEGER_OBJ(-1)); // collect buffer contents STATIC_ASSERT(SIZE_MAX >= MAXLNUM, "size_t smaller than MAXLNUM"); size_t line_count = (size_t)buf->b_ml.ml_line_count; - if (line_count >= 1) { - linedata.size = line_count; - linedata.items = xcalloc(line_count, sizeof(Object)); - - buf_collect_lines(buf, line_count, 1, 0, true, &linedata, NULL, NULL); + Array linedata = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + if (line_count > 0) { + linedata = arena_array(&arena, line_count); + buf_collect_lines(buf, line_count, 1, 0, true, &linedata, NULL, &arena); } - args.items[4] = ARRAY_OBJ(linedata); - args.items[5] = BOOLEAN_OBJ(false); + ADD_C(args, ARRAY_OBJ(linedata)); + ADD_C(args, BOOLEAN_OBJ(false)); rpc_send_event(channel_id, "nvim_buf_lines_event", args); - api_free_array(args); // TODO(bfredl): no + arena_mem_free(arena_finish(&arena)); } else { buf_updates_changedtick_single(buf, channel_id); } @@ -177,16 +173,13 @@ void buf_updates_unload(buf_T *buf, bool can_reload) } if (thecb != LUA_NOREF) { - Array args = ARRAY_DICT_INIT; - Object items[1]; - args.size = 1; - args.items = items; + MAXSIZE_TEMP_ARRAY(args, 1); // the first argument is always the buffer handle - args.items[0] = BUFFER_OBJ(buf->handle); + ADD_C(args, BUFFER_OBJ(buf->handle)); TEXTLOCK_WRAP({ - nlua_call_ref(thecb, keep ? "reload" : "detach", args, false, NULL); + nlua_call_ref(thecb, keep ? "reload" : "detach", args, false, NULL, NULL); }); } @@ -220,45 +213,43 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added, // if one the channels doesn't work, put its ID here so we can remove it later uint64_t badchannelid = 0; + Arena arena = ARENA_EMPTY; + Array linedata = ARRAY_DICT_INIT; + if (num_added > 0 && kv_size(buf->update_channels)) { + STATIC_ASSERT(SIZE_MAX >= MAXLNUM, "size_t smaller than MAXLNUM"); + linedata = arena_array(&arena, (size_t)num_added); + buf_collect_lines(buf, (size_t)num_added, firstline, 0, true, &linedata, + NULL, &arena); + } + // notify each of the active channels for (size_t i = 0; i < kv_size(buf->update_channels); i++) { uint64_t channelid = kv_A(buf->update_channels, i); // send through the changes now channel contents now - Array args = ARRAY_DICT_INIT; - args.size = 6; - args.items = xcalloc(args.size, sizeof(Object)); + MAXSIZE_TEMP_ARRAY(args, 6); // the first argument is always the buffer handle - args.items[0] = BUFFER_OBJ(buf->handle); + ADD_C(args, BUFFER_OBJ(buf->handle)); // next argument is b:changedtick - args.items[1] = send_tick ? INTEGER_OBJ(buf_get_changedtick(buf)) : NIL; + ADD_C(args, send_tick ? INTEGER_OBJ(buf_get_changedtick(buf)) : NIL); // the first line that changed (zero-indexed) - args.items[2] = INTEGER_OBJ(firstline - 1); + ADD_C(args, INTEGER_OBJ(firstline - 1)); // the last line that was changed - args.items[3] = INTEGER_OBJ(firstline - 1 + num_removed); + ADD_C(args, INTEGER_OBJ(firstline - 1 + num_removed)); // linedata of lines being swapped in - Array linedata = ARRAY_DICT_INIT; - if (num_added > 0) { - STATIC_ASSERT(SIZE_MAX >= MAXLNUM, "size_t smaller than MAXLNUM"); - linedata.size = (size_t)num_added; - linedata.items = xcalloc((size_t)num_added, sizeof(Object)); - buf_collect_lines(buf, (size_t)num_added, firstline, 0, true, &linedata, - NULL, NULL); - } - args.items[4] = ARRAY_OBJ(linedata); - args.items[5] = BOOLEAN_OBJ(false); + ADD_C(args, ARRAY_OBJ(linedata)); + ADD_C(args, BOOLEAN_OBJ(false)); if (!rpc_send_event(channelid, "nvim_buf_lines_event", args)) { // We can't unregister the channel while we're iterating over the // update_channels array, so we remember its ID to unregister it at // the end. badchannelid = channelid; } - api_free_array(args); // TODO(bfredl): no } // We can only ever remove one dead channel at a time. This is OK because the @@ -269,46 +260,45 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added, buf_updates_unregister(buf, badchannelid); } - // notify each of the active channels + // callbacks don't use linedata + arena_mem_free(arena_finish(&arena)); + + // notify each of the active callbacks size_t j = 0; for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) { BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; if (cb.on_lines != LUA_NOREF && (cb.preview || !cmdpreview)) { - Array args = ARRAY_DICT_INIT; - Object items[8]; - args.size = 6; // may be increased to 8 below - args.items = items; + MAXSIZE_TEMP_ARRAY(args, 8); // 6 or 8 used // the first argument is always the buffer handle - args.items[0] = BUFFER_OBJ(buf->handle); + ADD_C(args, BUFFER_OBJ(buf->handle)); // next argument is b:changedtick - args.items[1] = send_tick ? INTEGER_OBJ(buf_get_changedtick(buf)) : NIL; + ADD_C(args, send_tick ? INTEGER_OBJ(buf_get_changedtick(buf)) : NIL); // the first line that changed (zero-indexed) - args.items[2] = INTEGER_OBJ(firstline - 1); + ADD_C(args, INTEGER_OBJ(firstline - 1)); // the last line that was changed - args.items[3] = INTEGER_OBJ(firstline - 1 + num_removed); + ADD_C(args, INTEGER_OBJ(firstline - 1 + num_removed)); // the last line in the updated range - args.items[4] = INTEGER_OBJ(firstline - 1 + num_added); + ADD_C(args, INTEGER_OBJ(firstline - 1 + num_added)); // byte count of previous contents - args.items[5] = INTEGER_OBJ((Integer)deleted_bytes); + ADD_C(args, INTEGER_OBJ((Integer)deleted_bytes)); if (cb.utf_sizes) { - args.size = 8; - args.items[6] = INTEGER_OBJ((Integer)deleted_codepoints); - args.items[7] = INTEGER_OBJ((Integer)deleted_codeunits); + ADD_C(args, INTEGER_OBJ((Integer)deleted_codepoints)); + ADD_C(args, INTEGER_OBJ((Integer)deleted_codeunits)); } Object res; TEXTLOCK_WRAP({ - res = nlua_call_ref(cb.on_lines, "lines", args, false, NULL); + res = nlua_call_ref(cb.on_lines, "lines", args, kRetNilBool, NULL, NULL); }); - if (res.type == kObjectTypeBoolean && res.data.boolean == true) { + if (LUARET_TRUTHY(res)) { buffer_update_callbacks_free(cb); keep = false; } @@ -355,10 +345,10 @@ void buf_updates_send_splice(buf_T *buf, int start_row, colnr_T start_col, bcoun Object res; TEXTLOCK_WRAP({ - res = nlua_call_ref(cb.on_bytes, "bytes", args, false, NULL); + res = nlua_call_ref(cb.on_bytes, "bytes", args, kRetNilBool, NULL, NULL); }); - if (res.type == kObjectTypeBoolean && res.data.boolean == true) { + if (LUARET_TRUTHY(res)) { buffer_update_callbacks_free(cb); keep = false; } @@ -391,10 +381,10 @@ void buf_updates_changedtick(buf_T *buf) Object res; TEXTLOCK_WRAP({ - res = nlua_call_ref(cb.on_changedtick, "changedtick", args, false, NULL); + res = nlua_call_ref(cb.on_changedtick, "changedtick", args, kRetNilBool, NULL, NULL); }); - if (res.type == kObjectTypeBoolean && res.data.boolean == true) { + if (LUARET_TRUTHY(res)) { buffer_update_callbacks_free(cb); keep = false; } diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c index f774fcb057..94a6604fc1 100644 --- a/src/nvim/bufwrite.c +++ b/src/nvim/bufwrite.c @@ -12,6 +12,7 @@ #include "auto/config.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/bufwrite.h" @@ -23,21 +24,24 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_eval.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/iconv_defs.h" #include "nvim/input.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/sha256.h" @@ -45,6 +49,7 @@ #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/undo_defs.h" #include "nvim/vim_defs.h" static const char *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')"; @@ -187,7 +192,7 @@ static int buf_write_convert_with_iconv(struct bw_info *ip, char **bufp, int *le size_t save_len = tolen; // output the initial shift state sequence - (void)iconv(ip->bw_iconv_fd, NULL, NULL, &to, &tolen); + iconv(ip->bw_iconv_fd, NULL, NULL, &to, &tolen); // There is a bug in iconv() on Linux (which appears to be // wide-spread) which sets "to" to NULL and messes up "tolen". @@ -379,7 +384,7 @@ static int make_bom(char *buf_in, char *name) return 3; } char *p = (char *)buf; - (void)ucs2bytes(0xfeff, &p, flags); + ucs2bytes(0xfeff, &p, flags); return (int)((uint8_t *)p - buf); } @@ -481,8 +486,7 @@ static int buf_write_do_autocmds(buf_T *buf, char **fnamep, char **sfnamep, char semsg(_(e_no_matching_autocommands_for_buftype_str_buffer), curbuf->b_p_bt); } - if (nofile_err - || aborting()) { + if (nofile_err || aborting()) { // An aborting error, interrupt or exception in the // autocommands. return FAIL; @@ -721,7 +725,7 @@ static int get_fileinfo(buf_T *buf, char *fname, bool overwriting, bool forceit, static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_old, vim_acl_T acl, int perm, unsigned bkc, bool file_readonly, bool forceit, - int *backup_copyp, char **backupp, Error_T *err) + bool *backup_copyp, char **backupp, Error_T *err) { FileInfo file_info; const bool no_prepend_dot = false; @@ -799,7 +803,7 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o char *backup_ext = *p_bex == NUL ? ".bak" : p_bex; if (*backup_copyp) { - int some_error = false; + bool some_error = false; // Try to make the backup in each directory in the 'bdir' option. // @@ -898,7 +902,7 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o // set file protection same as original file, but // strip s-bit. - (void)os_setperm(*backupp, perm & 0777); + os_setperm(*backupp, perm & 0777); #ifdef UNIX // @@ -1059,14 +1063,14 @@ nobackup: /// /// @return FAIL for failure, OK otherwise int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T end, exarg_T *eap, - int append, int forceit, int reset_changed, int filtering) + bool append, bool forceit, bool reset_changed, bool filtering) { int retval = OK; int msg_save = msg_scroll; - int prev_got_int = got_int; + bool prev_got_int = got_int; // writing everything - int whole = (start == 1 && end == buf->b_ml.ml_line_count); - int write_undo_file = false; + bool whole = (start == 1 && end == buf->b_ml.ml_line_count); + bool write_undo_file = false; context_sha256_T sha_ctx; unsigned bkc = get_bkc_value(buf); @@ -1135,8 +1139,8 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en fname = sfname; #endif -// true if writing over original - int overwriting = buf->b_ffname != NULL && path_fnamecmp(ffname, buf->b_ffname) == 0; + // true if writing over original + bool overwriting = buf->b_ffname != NULL && path_fnamecmp(ffname, buf->b_ffname) == 0; no_wait_return++; // don't wait for return yet @@ -1219,7 +1223,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en dobackup = false; } - int backup_copy = false; // copy the original file? + bool backup_copy = false; // copy the original file? // Save the value of got_int and reset it. We don't want a previous // interruption cancel writing, only hitting CTRL-C while writing should @@ -1245,14 +1249,14 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en } #if defined(UNIX) - int made_writable = false; // 'w' bit has been set + bool made_writable = false; // 'w' bit has been set // When using ":w!" and the file was read-only: make it writable if (forceit && perm >= 0 && !(perm & 0200) && file_info_old.stat.st_uid == getuid() && vim_strchr(p_cpo, CPO_FWRITE) == NULL) { perm |= 0200; - (void)os_setperm(fname, perm); + os_setperm(fname, perm); made_writable = true; } #endif @@ -1279,8 +1283,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en // This makes all block numbers positive so that recovery does not need // the original file. // Don't do this if there is a backup file and we are exiting. - if (reset_changed && !newfile && overwriting - && !(exiting && backup != NULL)) { + if (reset_changed && !newfile && overwriting && !(exiting && backup != NULL)) { ml_preserve(buf, false, !!p_fs); if (got_int) { err = set_err(_(e_interr)); @@ -1304,7 +1307,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en } // Check if the file needs to be converted. - int converted = need_conversion(fenc); + bool converted = need_conversion(fenc); int wb_flags = 0; // Check if UTF-8 to UCS-2/4 or Latin1 conversion needs to be done. Or @@ -1352,7 +1355,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en } } - int notconverted = false; + bool notconverted = false; if (converted && wb_flags == 0 && write_info.bw_iconv_fd == (iconv_t)-1 @@ -1364,11 +1367,11 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en notconverted = true; } - int no_eol = false; // no end-of-line written + bool no_eol = false; // no end-of-line written int nchars; linenr_T lnum; int fileformat; - int checking_conversion; + bool checking_conversion; int fd; @@ -1606,7 +1609,7 @@ restore_backup: if (!buf->b_p_fixeol && buf->b_p_eof) { // write trailing CTRL-Z - (void)write_eintr(write_info.bw_fd, "\x1a", 1); + write_eintr(write_info.bw_fd, "\x1a", 1); } // Stop when writing done or an error was encountered. @@ -1655,7 +1658,7 @@ restore_backup: || file_info.stat.st_gid != file_info_old.stat.st_gid) { os_fchown(fd, (uv_uid_t)file_info_old.stat.st_uid, (uv_gid_t)file_info_old.stat.st_gid); if (perm >= 0) { // Set permission again, may have changed. - (void)os_setperm(wfname, perm); + os_setperm(wfname, perm); } } buf_set_file_id(buf); @@ -1676,7 +1679,7 @@ restore_backup: } #endif if (perm >= 0) { // Set perm. of new file same as old file. - (void)os_setperm(wfname, perm); + os_setperm(wfname, perm); } // Probably need to set the ACL before changing the user (can't set the // ACL on a file the user doesn't own). diff --git a/src/nvim/bufwrite.h b/src/nvim/bufwrite.h index 9ed6216847..ce9e04d2b1 100644 --- a/src/nvim/bufwrite.h +++ b/src/nvim/bufwrite.h @@ -1,8 +1,8 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "bufwrite.h.generated.h" diff --git a/src/nvim/change.c b/src/nvim/change.c index 81a55b92ee..1c7724f010 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -8,6 +8,7 @@ #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" @@ -20,18 +21,23 @@ #include "nvim/eval.h" #include "nvim/ex_cmds_defs.h" #include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/insexpand.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" +#include "nvim/marktree_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" @@ -43,8 +49,10 @@ #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/textformat.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" @@ -86,7 +94,7 @@ void change_warning(buf_T *buf, int col) msg_puts_attr(_(w_readonly), HL_ATTR(HLF_W) | MSG_HIST); set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1); msg_clr_eos(); - (void)msg_end(); + msg_end(); if (msg_silent == 0 && !silent_mode && ui_active()) { ui_flush(); os_delay(1002, true); // give the user time to think about it @@ -155,6 +163,71 @@ void changed_internal(buf_T *buf) need_maketitle = true; // set window title later } +/// Invalidate a window's w_valid flags and w_lines[] entries after changing lines. +static void changed_lines_invalidate_win(win_T *wp, linenr_T lnum, colnr_T col, linenr_T lnume, + linenr_T xtra) +{ + // If the changed line is in a range of previously folded lines, + // compare with the first line in that range. + if (wp->w_cursor.lnum <= lnum) { + int i = find_wl_entry(wp, lnum); + if (i >= 0 && wp->w_cursor.lnum > wp->w_lines[i].wl_lnum) { + changed_line_abv_curs_win(wp); + } + } + + if (wp->w_cursor.lnum > lnum) { + changed_line_abv_curs_win(wp); + } else if (wp->w_cursor.lnum == lnum && wp->w_cursor.col >= col) { + changed_cline_bef_curs(wp); + } + if (wp->w_botline >= lnum) { + // Assume that botline doesn't change (inserted lines make + // other lines scroll down below botline). + approximate_botline_win(wp); + } + + // Check if any w_lines[] entries have become invalid. + // For entries below the change: Correct the lnums for inserted/deleted lines. + // Makes it possible to stop displaying after the change. + for (int i = 0; i < wp->w_lines_valid; i++) { + if (wp->w_lines[i].wl_valid) { + if (wp->w_lines[i].wl_lnum >= lnum) { + // Do not change wl_lnum at index zero, it is used to compare with w_topline. + // Invalidate it instead. + // If lines haven been inserted/deleted and the buffer has virt_lines, + // invalidate the line after the changed lines as some virt_lines may + // now be drawn above a different line. + if (i == 0 || wp->w_lines[i].wl_lnum < lnume + || (xtra != 0 && wp->w_lines[i].wl_lnum == lnume + && buf_meta_total(wp->w_buffer, kMTMetaLines) > 0)) { + // line included in change + wp->w_lines[i].wl_valid = false; + } else if (xtra != 0) { + // line below change + wp->w_lines[i].wl_lnum += xtra; + wp->w_lines[i].wl_lastlnum += xtra; + } + } else if (wp->w_lines[i].wl_lastlnum >= lnum) { + // change somewhere inside this range of folded lines, + // may need to be redrawn + wp->w_lines[i].wl_valid = false; + } + } + } +} + +/// Line changed_lines_invalidate_win(), but for all windows displaying a buffer. +void changed_lines_invalidate_buf(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, + linenr_T xtra) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_buffer == buf) { + changed_lines_invalidate_win(wp, lnum, col, lnume, xtra); + } + } +} + /// Common code for when a change was made. /// See changed_lines() for the arguments. /// Careful: may trigger autocommands that reload the buffer. @@ -185,7 +258,7 @@ static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnum // Create a new entry if a new undo-able change was started or we // don't have an entry yet. if (buf->b_new_change || buf->b_changelistlen == 0) { - int add; + bool add; if (buf->b_changelistlen == 0) { add = true; } else { @@ -249,10 +322,17 @@ static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnum FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { // Mark this window to be redrawn later. - if (wp->w_redr_type < UPD_VALID) { + if (!redraw_not_allowed && wp->w_redr_type < UPD_VALID) { wp->w_redr_type = UPD_VALID; } + // When inserting/deleting lines and the window has specific lines + // to be redrawn, w_redraw_top and w_redraw_bot may now be invalid, + // so just redraw everything. + if (xtra != 0 && wp->w_redraw_top != 0) { + redraw_later(wp, UPD_NOT_VALID); + } + linenr_T last = lnume + xtra - 1; // last line after the change // Reset "w_skipcol" if the topline length has become smaller to @@ -288,55 +368,7 @@ static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnum wp->w_cline_folded = folded; } - // If the changed line is in a range of previously folded lines, - // compare with the first line in that range. - if (wp->w_cursor.lnum <= lnum) { - int i = find_wl_entry(wp, lnum); - if (i >= 0 && wp->w_cursor.lnum > wp->w_lines[i].wl_lnum) { - changed_line_abv_curs_win(wp); - } - } - - if (wp->w_cursor.lnum > lnum) { - changed_line_abv_curs_win(wp); - } else if (wp->w_cursor.lnum == lnum && wp->w_cursor.col >= col) { - changed_cline_bef_curs(wp); - } - if (wp->w_botline >= lnum) { - // Assume that botline doesn't change (inserted lines make - // other lines scroll down below botline). - approximate_botline_win(wp); - } - - // Check if any w_lines[] entries have become invalid. - // For entries below the change: Correct the lnums for - // inserted/deleted lines. Makes it possible to stop displaying - // after the change. - for (int i = 0; i < wp->w_lines_valid; i++) { - if (wp->w_lines[i].wl_valid) { - if (wp->w_lines[i].wl_lnum >= lnum) { - // Do not change wl_lnum at index zero, it is used to - // compare with w_topline. Invalidate it instead. - // If the buffer has virt_lines, invalidate the line - // after the changed lines as the virt_lines for a - // changed line may become invalid. - if (i == 0 || wp->w_lines[i].wl_lnum < lnume - || (wp->w_lines[i].wl_lnum == lnume - && wp->w_buffer->b_virt_line_blocks > 0)) { - // line included in change - wp->w_lines[i].wl_valid = false; - } else if (xtra != 0) { - // line below change - wp->w_lines[i].wl_lnum += xtra; - wp->w_lines[i].wl_lastlnum += xtra; - } - } else if (wp->w_lines[i].wl_lastlnum >= lnum) { - // change somewhere inside this range of folded lines, - // may need to be redrawn - wp->w_lines[i].wl_valid = false; - } - } - } + changed_lines_invalidate_win(wp, lnum, col, lnume, xtra); // Take care of side effects for setting w_topline when folds have // changed. Esp. when the buffer was changed in another window. @@ -345,20 +377,19 @@ static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnum } // If lines have been added or removed, relative numbering always - // requires a redraw. + // requires an update even if cursor didn't move. if (wp->w_p_rnu && xtra != 0) { wp->w_last_cursor_lnum_rnu = 0; - redraw_later(wp, UPD_VALID); } - // Cursor line highlighting probably need to be updated with - // "UPD_VALID" if it's below the change. - // If the cursor line is inside the change we need to redraw more. - if (wp->w_p_cul) { - if (xtra == 0) { - redraw_later(wp, UPD_VALID); - } else if (lnum <= wp->w_last_cursorline) { - redraw_later(wp, UPD_SOME_VALID); + if (wp->w_p_cul && wp->w_last_cursorline >= lnum) { + if (wp->w_last_cursorline < lnume) { + // If 'cursorline' was inside the change, it has already + // been invalidated in w_lines[] by the loop above. + wp->w_last_cursorline = 0; + } else { + // If 'cursorline' was below the change, adjust its lnum. + wp->w_last_cursorline += xtra; } } } @@ -366,9 +397,7 @@ static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnum // Call update_screen() later, which checks out what needs to be redrawn, // since it notices b_mod_set and then uses b_mod_*. - if (must_redraw < UPD_VALID) { - must_redraw = UPD_VALID; - } + set_must_redraw(UPD_VALID); // when the cursor line is changed always trigger CursorMoved if (last_cursormoved_win == curwin && curwin->w_buffer == buf @@ -481,13 +510,13 @@ void deleted_lines_mark(linenr_T lnum, int count) } /// Marks the area to be redrawn after a change. -/// Consider also calling changed_line_display_buf(). +/// Consider also calling changed_lines_invalidate_buf(). /// /// @param buf the buffer where lines were changed /// @param lnum first line with change /// @param lnume line below last changed line /// @param xtra number of extra lines (negative when deleting) -void buf_redraw_changed_lines_later(buf_T *buf, linenr_T lnum, linenr_T lnume, linenr_T xtra) +void changed_lines_redraw_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, linenr_T xtra) { if (buf->b_mod_set) { // find the maximum area that must be redisplayed @@ -535,7 +564,7 @@ void buf_redraw_changed_lines_later(buf_T *buf, linenr_T lnum, linenr_T lnume, l void changed_lines(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra, bool do_buf_event) { - buf_redraw_changed_lines_later(buf, lnum, lnume, xtra); + changed_lines_redraw_buf(buf, lnum, lnume, xtra); if (xtra == 0 && curwin->w_p_diff && curwin->w_buffer == buf && !diff_internal()) { // When the number of lines doesn't change then mark_adjust() isn't @@ -548,8 +577,7 @@ void changed_lines(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, linen redraw_later(wp, UPD_VALID); wlnum = diff_lnum_win(lnum, wp); if (wlnum > 0) { - buf_redraw_changed_lines_later(wp->w_buffer, wlnum, - lnume - lnum + wlnum, 0); + changed_lines_redraw_buf(wp->w_buffer, wlnum, lnume - lnum + wlnum, 0); } } } @@ -568,7 +596,7 @@ void changed_lines(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, linen /// When `ff` is true also reset 'fileformat'. /// When `always_inc_changedtick` is true b:changedtick is incremented even /// when the changed flag was off. -void unchanged(buf_T *buf, int ff, bool always_inc_changedtick) +void unchanged(buf_T *buf, bool ff, bool always_inc_changedtick) { if (buf->b_changed || (ff && file_ff_differs(buf, false))) { buf->b_changed = false; @@ -923,8 +951,9 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) /// Copy the indent from ptr to the current line (and fill to size). /// Leaves the cursor on the first non-blank in the line. +/// /// @return true if the line was changed. -int copy_indent(int size, char *src) +bool copy_indent(int size, char *src) { char *p = NULL; char *line = NULL; @@ -1054,7 +1083,7 @@ int copy_indent(int size, char *src) /// @param dir FORWARD or BACKWARD /// /// @return true on success, false on failure -int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) +bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) { char *next_line = NULL; // copy of the next line char *p_extra = NULL; // what goes to next line @@ -1075,7 +1104,6 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) char saved_char = NUL; // init for GCC pos_T *pos; bool do_si = may_do_si(); - bool do_cindent; bool no_si = false; // reset did_si afterwards int first_char = NUL; // init for GCC int vreplace_mode; @@ -1142,9 +1170,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // indent to use for the new line. if (curbuf->b_p_ai || do_si) { // count white space on current line - newindent = get_indent_str_vtab(saved_line, - curbuf->b_p_ts, - curbuf->b_p_vts_array, false); + newindent = indent_size_ts(saved_line, curbuf->b_p_ts, curbuf->b_p_vts_array); if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) { newindent = second_line_indent; // for ^^D command in insert mode } @@ -1156,10 +1182,8 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // "if (condition) {" if (!trunc_line && do_si && *saved_line != NUL && (p_extra == NULL || first_char != '{')) { - char *ptr; - old_cursor = curwin->w_cursor; - ptr = saved_line; + char *ptr = saved_line; if (flags & OPENLINE_DO_COM) { lead_len = get_leader_len(ptr, NULL, false, true); } else { @@ -1289,9 +1313,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } // May do indenting after opening a new line. - do_cindent = !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL) - && in_cinkeys(dir == FORWARD ? KEY_OPEN_FORW : KEY_OPEN_BACK, - ' ', linewhite(curwin->w_cursor.lnum)); + bool do_cindent = !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL) + && in_cinkeys(dir == FORWARD ? KEY_OPEN_FORW : KEY_OPEN_BACK, + ' ', linewhite(curwin->w_cursor.lnum)); // Find out if the current line starts with a comment leader. // This may then be inserted in front of the new line. @@ -1322,8 +1346,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) char lead_end[COM_MAX_LEN]; // end-comment string char *comment_end = NULL; // where lead_end has been found int extra_space = false; // append extra space - int current_flag; - int require_blank = false; // requires blank after middle + bool require_blank = false; // requires blank after middle char *p2; // If the comment leader has the start, middle or end flag, it may not @@ -1334,7 +1357,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) continue; } if (*p == COM_START || *p == COM_MIDDLE) { - current_flag = (unsigned char)(*p); + int current_flag = (unsigned char)(*p); if (*p == COM_START) { // Doing "O" on a start of comment does not insert leader. if (dir == BACKWARD) { @@ -1343,7 +1366,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } // find start of middle part - (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ","); + copy_option_part(&p, lead_middle, COM_MAX_LEN, ","); require_blank = false; } @@ -1354,7 +1377,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } p++; } - (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ","); + copy_option_part(&p, lead_middle, COM_MAX_LEN, ","); while (*p && p[-1] != ':') { // find end of end flags // Check whether we allow automatic ending of comments @@ -1499,13 +1522,12 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) int repl_size = vim_strnsize(lead_repl, lead_repl_len); int old_size = 0; char *endp = p; - int l; while (old_size < repl_size && p > leader) { MB_PTR_BACK(leader, p); old_size += ptr2cells(p); } - l = lead_repl_len - (int)(endp - p); + int l = lead_repl_len - (int)(endp - p); if (l != 0) { memmove(endp + l, endp, (size_t)((leader + lead_len) - endp)); @@ -1590,9 +1612,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Recompute the indent, it may have changed. if (curbuf->b_p_ai || do_si) { - newindent = get_indent_str_vtab(leader, - curbuf->b_p_ts, - curbuf->b_p_vts_array, false); + newindent = indent_size_ts(leader, curbuf->b_p_ts, curbuf->b_p_vts_array); } // Add the indent offset @@ -1732,7 +1752,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) if (curwin->w_cursor.lnum >= Insstart.lnum + vr_lines_changed) { // In case we NL to a new line, BS to the previous one, and NL // again, we don't want to save the new line for undo twice. - (void)u_save_cursor(); // errors are ignored! + u_save_cursor(); // errors are ignored! vr_lines_changed++; } ml_replace(curwin->w_cursor.lnum, p_extra, true); @@ -1755,14 +1775,14 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } // Copy the indent if (curbuf->b_p_ci) { - (void)copy_indent(newindent, saved_line); + copy_indent(newindent, saved_line); // Set the 'preserveindent' option so that any further screwing // with the line doesn't entirely destroy our efforts to preserve // it. It gets restored at the function end. curbuf->b_p_pi = true; } else { - (void)set_indent(newindent, SIN_INSERT|SIN_NOMARK); + set_indent(newindent, SIN_INSERT|SIN_NOMARK); } less_cols -= curwin->w_cursor.col; @@ -1840,11 +1860,11 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) curwin->w_cursor.lnum = old_cursor.lnum + 1; } if (did_append) { - changed_lines(curbuf, curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1, true); // bail out and just get the final length of the line we just manipulated bcount_t extra = (bcount_t)strlen(ml_get(curwin->w_cursor.lnum)); extmark_splice(curbuf, (int)curwin->w_cursor.lnum - 1, 0, 0, 0, 0, 1, 0, 1 + extra, kExtmarkUndo); + changed_lines(curbuf, curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1, true); } curbuf_splice_pending--; @@ -1980,7 +2000,7 @@ void del_lines(linenr_T nlines, bool undo) int get_leader_len(char *line, char **flags, bool backward, bool include_space) { int j; - int got_com = false; + bool got_com = false; char part_buf[COM_MAX_LEN]; // buffer for one option part char *string; // pointer to comment string int middle_match_len = 0; @@ -1995,7 +2015,7 @@ int get_leader_len(char *line, char **flags, bool backward, bool include_space) // Repeat to match several nested comment strings. while (line[i] != NUL) { // scan through the 'comments' option for a match - int found_one = false; + bool found_one = false; for (char *list = curbuf->b_p_com; *list;) { // Get one option part into part_buf[]. Advance "list" to next // one. Put "string" at start of string. @@ -2003,7 +2023,7 @@ int get_leader_len(char *line, char **flags, bool backward, bool include_space) *flags = list; // remember where flags started } char *prev_list = list; - (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); + copy_option_part(&list, part_buf, COM_MAX_LEN, ","); string = vim_strchr(part_buf, ':'); if (string == NULL) { // missing ':', ignore this part continue; @@ -2122,24 +2142,22 @@ int get_last_leader_offset(char *line, char **flags) int result = -1; int j; int lower_check_bound = 0; - char *string; char *com_leader; char *com_flags; - char *list; char part_buf[COM_MAX_LEN]; // buffer for one option part // Repeat to match several nested comment strings. int i = (int)strlen(line); while (--i >= lower_check_bound) { // scan through the 'comments' option for a match - int found_one = false; - for (list = curbuf->b_p_com; *list;) { + bool found_one = false; + for (char *list = curbuf->b_p_com; *list;) { char *flags_save = list; // Get one option part into part_buf[]. Advance list to next one. // put string at start of string. - (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); - string = vim_strchr(part_buf, ':'); + copy_option_part(&list, part_buf, COM_MAX_LEN, ","); + char *string = vim_strchr(part_buf, ':'); if (string == NULL) { // If everything is fine, this cannot actually // happen. continue; @@ -2217,14 +2235,14 @@ int get_last_leader_offset(char *line, char **flags) } int len1 = (int)strlen(com_leader); - for (list = curbuf->b_p_com; *list;) { + for (char *list = curbuf->b_p_com; *list;) { char *flags_save = list; - (void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ","); + copy_option_part(&list, part_buf2, COM_MAX_LEN, ","); if (flags_save == com_flags) { continue; } - string = vim_strchr(part_buf2, ':'); + char *string = vim_strchr(part_buf2, ':'); string++; while (ascii_iswhite(*string)) { string++; diff --git a/src/nvim/change.h b/src/nvim/change.h index 06155b6da6..bd1094d57e 100644 --- a/src/nvim/change.h +++ b/src/nvim/change.h @@ -1,7 +1,9 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include <stddef.h> // IWYU pragma: keep + #include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep /// flags for open_line() enum { diff --git a/src/nvim/channel.c b/src/nvim/channel.c index e8fe80a3b6..ebeaffe5a1 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -10,16 +10,21 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer_defs.h" #include "nvim/channel.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" +#include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" +#include "nvim/event/process.h" #include "nvim/event/rstream.h" #include "nvim/event/socket.h" +#include "nvim/event/stream.h" #include "nvim/event/wstream.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/lua/executor.h" @@ -28,10 +33,14 @@ #include "nvim/message.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/server.h" +#include "nvim/os/fs.h" #include "nvim/os/os_defs.h" #include "nvim/os/shell.h" #include "nvim/path.h" #include "nvim/rbuffer.h" +#include "nvim/rbuffer_defs.h" +#include "nvim/terminal.h" +#include "nvim/types_defs.h" #ifdef MSWIN # include "nvim/os/fs.h" @@ -53,12 +62,24 @@ static uint64_t next_chan_id = CHAN_STDERR + 1; /// Teardown the module void channel_teardown(void) { - Channel *channel; + Channel *chan; + map_foreach_value(&channels, chan, { + channel_close(chan->id, kChannelPartAll, NULL); + }); +} - map_foreach_value(&channels, channel, { - channel_close(channel->id, kChannelPartAll, NULL); +#ifdef EXITFREE +void channel_free_all_mem(void) +{ + Channel *chan; + map_foreach_value(&channels, chan, { + channel_destroy(chan); }); + map_destroy(uint64_t, &channels); + + callback_free(&on_print); } +#endif /// Closes a channel /// @@ -217,16 +238,17 @@ void channel_create_event(Channel *chan, const char *ext_source) } assert(chan->id <= VARNUMBER_MAX); - Dictionary info = channel_info(chan->id); + Arena arena = ARENA_EMPTY; + Dictionary info = channel_info(chan->id, &arena); typval_T tv = TV_INITIAL_VALUE; // TODO(bfredl): do the conversion in one step. Also would be nice // to pretty print top level dict in defined order - (void)object_to_vim(DICTIONARY_OBJ(info), &tv, NULL); + object_to_vim(DICTIONARY_OBJ(info), &tv, NULL); assert(tv.v_type == VAR_DICT); char *str = encode_tv2json(&tv, NULL); ILOG("new channel %" PRIu64 " (%s) : %s", chan->id, source, str); xfree(str); - api_free_dictionary(info); + arena_mem_free(arena_finish(&arena)); #else (void)ext_source; @@ -244,7 +266,7 @@ void channel_decref(Channel *chan) { if (!(--chan->refcount)) { // delay free, so that libuv is done with the handles - multiqueue_put(main_loop.events, free_channel_event, 1, chan); + multiqueue_put(main_loop.events, free_channel_event, chan); } } @@ -260,9 +282,8 @@ void callback_reader_start(CallbackReader *reader, const char *type) reader->type = type; } -static void free_channel_event(void **argv) +static void channel_destroy(Channel *chan) { - Channel *chan = argv[0]; if (chan->is_rpc) { rpc_free(chan); } @@ -275,11 +296,17 @@ static void free_channel_event(void **argv) callback_reader_free(&chan->on_stderr); callback_free(&chan->on_exit); - pmap_del(uint64_t)(&channels, chan->id, NULL); multiqueue_free(chan->events); xfree(chan); } +static void free_channel_event(void **argv) +{ + Channel *chan = argv[0]; + pmap_del(uint64_t)(&channels, chan->id, NULL); + channel_destroy(chan); +} + static void channel_destroy_early(Channel *chan) { if ((chan->id != --next_chan_id)) { @@ -293,7 +320,7 @@ static void channel_destroy_early(Channel *chan) } // uv will keep a reference to handles until next loop tick, so delay free - multiqueue_put(main_loop.events, free_channel_event, 1, chan); + multiqueue_put(main_loop.events, free_channel_event, chan); } static void close_cb(Stream *stream, void *data) @@ -553,7 +580,10 @@ size_t channel_send(uint64_t id, char *data, size_t len, bool data_owned, const goto retfree; } // unbuffered write - written = len * fwrite(data, len, 1, stderr); + ptrdiff_t wres = os_write(STDERR_FILENO, data, len, false); + if (wres >= 0) { + written = (size_t)wres; + } goto retfree; } @@ -657,7 +687,7 @@ static void schedule_channel_event(Channel *chan) { if (!chan->callback_scheduled) { if (!chan->callback_busy) { - multiqueue_put(chan->events, on_channel_event, 1, chan); + multiqueue_put(chan->events, on_channel_event, chan); channel_incref(chan); } chan->callback_scheduled = true; @@ -682,7 +712,7 @@ static void on_channel_event(void **args) chan->callback_busy = false; if (chan->callback_scheduled) { // further callback was deferred to avoid recursion. - multiqueue_put(chan->events, on_channel_event, 1, chan); + multiqueue_put(chan->events, on_channel_event, chan); channel_incref(chan); } @@ -777,19 +807,21 @@ static void channel_callback_call(Channel *chan, CallbackReader *reader) /// and `buf` is assumed to be a new, unmodified buffer. void channel_terminal_open(buf_T *buf, Channel *chan) { - TerminalOptions topts; - topts.data = chan; - topts.width = chan->stream.pty.width; - topts.height = chan->stream.pty.height; - topts.write_cb = term_write; - topts.resize_cb = term_resize; - topts.close_cb = term_close; + TerminalOptions topts = { + .data = chan, + .width = chan->stream.pty.width, + .height = chan->stream.pty.height, + .write_cb = term_write, + .resize_cb = term_resize, + .close_cb = term_close, + .force_crlf = false, + }; buf->b_p_channel = (OptInt)chan->id; // 'channel' option channel_incref(chan); terminal_open(&chan->term, buf, topts); } -static void term_write(char *buf, size_t size, void *data) +static void term_write(const char *buf, size_t size, void *data) { Channel *chan = data; if (chan->stream.proc.in.closed) { @@ -812,7 +844,7 @@ static inline void term_delayed_free(void **argv) { Channel *chan = argv[0]; if (chan->stream.proc.in.pending_reqs || chan->stream.proc.out.pending_reqs) { - multiqueue_put(chan->events, term_delayed_free, 1, chan); + multiqueue_put(chan->events, term_delayed_free, chan); return; } @@ -826,7 +858,7 @@ static void term_close(void *data) { Channel *chan = data; process_stop(&chan->stream.proc); - multiqueue_put(chan->events, term_delayed_free, 1, data); + multiqueue_put(chan->events, term_delayed_free, data); } void channel_info_changed(Channel *chan, bool new_chan) @@ -834,7 +866,7 @@ void channel_info_changed(Channel *chan, bool new_chan) event_T event = new_chan ? EVENT_CHANOPEN : EVENT_CHANINFO; if (has_event(event)) { channel_incref(chan); - multiqueue_put(main_loop.events, set_info_event, 2, chan, event); + multiqueue_put(main_loop.events, set_info_event, chan, (void *)(intptr_t)event); } } @@ -845,9 +877,10 @@ static void set_info_event(void **argv) save_v_event_T save_v_event; dict_T *dict = get_v_event(&save_v_event); - Dictionary info = channel_info(chan->id); + Arena arena = ARENA_EMPTY; + Dictionary info = channel_info(chan->id, &arena); typval_T retval; - (void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL); + object_to_vim(DICTIONARY_OBJ(info), &retval, NULL); assert(retval.v_type == VAR_DICT); tv_dict_add_dict(dict, S_LEN("info"), retval.vval.v_dict); tv_dict_set_keys_readonly(dict); @@ -855,7 +888,7 @@ static void set_info_event(void **argv) apply_autocmds(event, NULL, NULL, false, curbuf); restore_v_event(dict, &save_v_event); - api_free_dictionary(info); + arena_mem_free(arena_finish(&arena)); channel_decref(chan); } @@ -867,15 +900,15 @@ bool channel_job_running(uint64_t id) && !process_is_stopped(&chan->stream.proc)); } -Dictionary channel_info(uint64_t id) +Dictionary channel_info(uint64_t id, Arena *arena) { Channel *chan = find_channel(id); if (!chan) { return (Dictionary)ARRAY_DICT_INIT; } - Dictionary info = ARRAY_DICT_INIT; - PUT(info, "id", INTEGER_OBJ((Integer)chan->id)); + Dictionary info = arena_dict(arena, 8); + PUT_C(info, "id", INTEGER_OBJ((Integer)chan->id)); const char *stream_desc, *mode_desc; switch (chan->streamtype) { @@ -883,18 +916,20 @@ Dictionary channel_info(uint64_t id) stream_desc = "job"; if (chan->stream.proc.type == kProcessTypePty) { const char *name = pty_process_tty_name(&chan->stream.pty); - PUT(info, "pty", CSTR_TO_OBJ(name)); + PUT_C(info, "pty", CSTR_TO_ARENA_OBJ(arena, name)); } - char **p = chan->stream.proc.argv; + char **args = chan->stream.proc.argv; Array argv = ARRAY_DICT_INIT; - if (p != NULL) { - while (*p != NULL) { - ADD(argv, CSTR_TO_OBJ(*p)); - p++; + if (args != NULL) { + size_t n; + for (n = 0; args[n] != NULL; n++) {} + argv = arena_array(arena, n); + for (size_t i = 0; i < n; i++) { + ADD_C(argv, CSTR_AS_OBJ(args[i])); } } - PUT(info, "argv", ARRAY_OBJ(argv)); + PUT_C(info, "argv", ARRAY_OBJ(argv)); break; } @@ -907,51 +942,51 @@ Dictionary channel_info(uint64_t id) break; case kChannelStreamInternal: - PUT(info, "internal", BOOLEAN_OBJ(true)); + PUT_C(info, "internal", BOOLEAN_OBJ(true)); FALLTHROUGH; case kChannelStreamSocket: stream_desc = "socket"; break; } - PUT(info, "stream", CSTR_TO_OBJ(stream_desc)); + PUT_C(info, "stream", CSTR_AS_OBJ(stream_desc)); if (chan->is_rpc) { mode_desc = "rpc"; - PUT(info, "client", DICTIONARY_OBJ(rpc_client_info(chan))); + PUT_C(info, "client", DICTIONARY_OBJ(chan->rpc.info)); } else if (chan->term) { mode_desc = "terminal"; - PUT(info, "buffer", BUFFER_OBJ(terminal_buf(chan->term))); + PUT_C(info, "buffer", BUFFER_OBJ(terminal_buf(chan->term))); } else { mode_desc = "bytes"; } - PUT(info, "mode", CSTR_TO_OBJ(mode_desc)); + PUT_C(info, "mode", CSTR_AS_OBJ(mode_desc)); return info; } /// Simple int64_t comparison function for use with qsort() -static int int64_t_cmp(const void *a, const void *b) +static int int64_t_cmp(const void *pa, const void *pb) { - int64_t diff = *(int64_t *)a - *(int64_t *)b; - return (diff < 0) ? -1 : (diff > 0); + const int64_t a = *(const int64_t *)pa; + const int64_t b = *(const int64_t *)pb; + return a == b ? 0 : a > b ? 1 : -1; } -Array channel_all_info(void) +Array channel_all_info(Arena *arena) { // order the items in the array by channel number, for Determinism™ kvec_t(int64_t) ids = KV_INITIAL_VALUE; - kv_resize(ids, map_size(&channels)); + kv_fixsize_arena(arena, ids, map_size(&channels)); uint64_t id; map_foreach_key(&channels, id, { kv_push(ids, (int64_t)id); }); qsort(ids.items, ids.size, sizeof ids.items[0], int64_t_cmp); - Array ret = ARRAY_DICT_INIT; + Array ret = arena_array(arena, ids.size); for (size_t i = 0; i < ids.size; i++) { - ADD(ret, DICTIONARY_OBJ(channel_info((uint64_t)ids.items[i]))); + ADD_C(ret, DICTIONARY_OBJ(channel_info((uint64_t)ids.items[i], arena))); } - kv_destroy(ids); return ret; } diff --git a/src/nvim/channel.h b/src/nvim/channel.h index 5c9d708ac2..35d369e513 100644 --- a/src/nvim/channel.h +++ b/src/nvim/channel.h @@ -4,75 +4,17 @@ #include <stdint.h> #include <stdlib.h> +#include "nvim/channel_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" +#include "nvim/event/defs.h" #include "nvim/event/libuv_process.h" -#include "nvim/event/multiqueue.h" -#include "nvim/event/process.h" -#include "nvim/event/socket.h" -#include "nvim/event/stream.h" -#include "nvim/garray_defs.h" +#include "nvim/func_attr.h" #include "nvim/macros_defs.h" -#include "nvim/main.h" #include "nvim/map_defs.h" #include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/os/pty_process.h" -#include "nvim/terminal.h" #include "nvim/types_defs.h" -#define CHAN_STDIO 1 -#define CHAN_STDERR 2 - -typedef enum { - kChannelStreamProc, - kChannelStreamSocket, - kChannelStreamStdio, - kChannelStreamStderr, - kChannelStreamInternal, -} ChannelStreamType; - -typedef enum { - kChannelPartStdin, - kChannelPartStdout, - kChannelPartStderr, - kChannelPartRpc, - kChannelPartAll, -} ChannelPart; - -typedef enum { - kChannelStdinPipe, - kChannelStdinNull, -} ChannelStdinMode; - -typedef struct { - Stream in; - Stream out; -} StdioPair; - -typedef struct { - bool closed; -} StderrState; - -typedef struct { - LuaRef cb; - bool closed; -} InternalState; - -typedef struct { - Callback cb; - dict_T *self; - garray_T buffer; - bool eof; - bool buffered; - bool fwd_err; - const char *type; -} CallbackReader; - -#define CALLBACK_READER_INIT ((CallbackReader){ .cb = CALLBACK_NONE, \ - .self = NULL, \ - .buffer = GA_EMPTY_INIT_VALUE, \ - .buffered = false, \ - .fwd_err = false, \ - .type = NULL }) static inline bool callback_reader_set(CallbackReader reader) { return reader.cb.type != kCallbackNone || reader.self; @@ -122,7 +64,9 @@ static inline Channel *find_channel(uint64_t id) } static inline Stream *channel_instream(Channel *chan) - FUNC_ATTR_NONNULL_ALL + REAL_FATTR_NONNULL_ALL; + +static inline Stream *channel_instream(Channel *chan) { switch (chan->streamtype) { case kChannelStreamProc: @@ -142,7 +86,9 @@ static inline Stream *channel_instream(Channel *chan) } static inline Stream *channel_outstream(Channel *chan) - FUNC_ATTR_NONNULL_ALL + REAL_FATTR_NONNULL_ALL; + +static inline Stream *channel_outstream(Channel *chan) { switch (chan->streamtype) { case kChannelStreamProc: diff --git a/src/nvim/channel_defs.h b/src/nvim/channel_defs.h new file mode 100644 index 0000000000..d4f1895420 --- /dev/null +++ b/src/nvim/channel_defs.h @@ -0,0 +1,61 @@ +#pragma once +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#include "nvim/event/defs.h" + +#define CHAN_STDIO 1 +#define CHAN_STDERR 2 + +typedef enum { + kChannelStreamProc, + kChannelStreamSocket, + kChannelStreamStdio, + kChannelStreamStderr, + kChannelStreamInternal, +} ChannelStreamType; + +typedef enum { + kChannelPartStdin, + kChannelPartStdout, + kChannelPartStderr, + kChannelPartRpc, + kChannelPartAll, +} ChannelPart; + +typedef enum { + kChannelStdinPipe, + kChannelStdinNull, +} ChannelStdinMode; + +typedef struct { + Stream in; + Stream out; +} StdioPair; + +typedef struct { + bool closed; +} StderrState; + +typedef struct { + LuaRef cb; + bool closed; +} InternalState; + +typedef struct { + Callback cb; + dict_T *self; + garray_T buffer; + bool eof; + bool buffered; + bool fwd_err; + const char *type; +} CallbackReader; + +#define CALLBACK_READER_INIT ((CallbackReader){ .cb = CALLBACK_NONE, \ + .self = NULL, \ + .buffer = GA_EMPTY_INIT_VALUE, \ + .buffered = false, \ + .fwd_err = false, \ + .type = NULL }) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 48a9808b31..20bd364c7e 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -8,7 +8,7 @@ #include <limits.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "auto/config.h" #include "klib/kvec.h" @@ -18,6 +18,7 @@ #include "nvim/cursor.h" #include "nvim/eval/typval_defs.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/globals.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" @@ -27,6 +28,7 @@ #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -84,7 +86,7 @@ int init_chartab(void) /// /// @return FAIL if 'iskeyword', 'isident', 'isfname' or 'isprint' option has /// an error, OK otherwise. -int buf_init_chartab(buf_T *buf, int global) +int buf_init_chartab(buf_T *buf, bool global) { int c; @@ -489,7 +491,7 @@ char *str_foldcase(char *str, int orglen, char *buf, int buflen) } } } - (void)utf_char2bytes(lc, STR_PTR(i)); + utf_char2bytes(lc, STR_PTR(i)); } // skip to next multi-byte char @@ -847,11 +849,11 @@ bool vim_isfilec(int c) } /// Check if "c" is a valid file-name character, including characters left -/// out of 'isfname' to make "gf" work, such as comma, space, '@', etc. +/// out of 'isfname' to make "gf" work, such as ',', ' ', '@', ':', etc. bool vim_is_fname_char(int c) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return vim_isfilec(c) || c == ',' || c == ' ' || c == '@'; + return vim_isfilec(c) || c == ',' || c == ' ' || c == '@' || c == ':'; } /// Check that "c" is a valid file-name character or a wildcard character @@ -1445,9 +1447,9 @@ bool rem_backslash(const char *str) && str[1] != '?' && !vim_isfilec((uint8_t)str[1]))); -#else // ifdef BACKSLASH_IN_FILENAME +#else return str[0] == '\\' && str[1] != NUL; -#endif // ifdef BACKSLASH_IN_FILENAME +#endif } /// Halve the number of backslashes in a file name argument. diff --git a/src/nvim/charset.h b/src/nvim/charset.h index cfab0f8517..62a38660a8 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -3,12 +3,9 @@ #include <stdbool.h> #include <stdint.h> -#include "nvim/buffer_defs.h" -#include "nvim/eval/typval_defs.h" -#include "nvim/option_defs.h" +#include "nvim/func_attr.h" #include "nvim/option_vars.h" -#include "nvim/pos_defs.h" -#include "nvim/strings.h" +#include "nvim/strings.h" // IWYU pragma: keep /// Return the folded-case equivalent of the given character /// diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 367b86ec55..f172646edf 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -6,7 +6,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" @@ -14,6 +14,7 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cmdhist.h" @@ -24,18 +25,20 @@ #include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/help.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/keycodes.h" #include "nvim/log.h" @@ -43,6 +46,7 @@ #include "nvim/macros_defs.h" #include "nvim/mapping.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" @@ -51,11 +55,13 @@ #include "nvim/os/fs.h" #include "nvim/os/lang.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/popupmenu.h" #include "nvim/profile.h" #include "nvim/regexp.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/search.h" #include "nvim/sign.h" #include "nvim/statusline.h" @@ -64,6 +70,7 @@ #include "nvim/tag.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/usercmd.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -75,7 +82,7 @@ typedef void *(*user_expand_func_T)(const char *, int, typval_T *); # include "cmdexpand.c.generated.h" #endif -static int cmd_showtail; ///< Only show path tail in lists ? +static bool cmd_showtail; ///< Only show path tail in lists ? /// "compl_match_array" points the currently displayed list of entries in the /// popup menu. It is NULL when there is no popup menu. @@ -101,6 +108,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp) && xp->xp_context != EXPAND_FILES_IN_PATH && xp->xp_context != EXPAND_FILETYPE && xp->xp_context != EXPAND_HELP + && xp->xp_context != EXPAND_KEYMAP && xp->xp_context != EXPAND_LUA && xp->xp_context != EXPAND_OLD_SETTING && xp->xp_context != EXPAND_STRING_SETTING @@ -327,7 +335,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape) beep_flush(); } else if (xp->xp_numfiles == 1) { // free expanded pattern - (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE); + ExpandOne(xp, NULL, NULL, 0, WILD_FREE); } return OK; @@ -336,7 +344,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape) /// Create and display a cmdline completion popup menu with items from /// "matches". static int cmdline_pum_create(CmdlineInfo *ccline, expand_T *xp, char **matches, int numMatches, - int showtail) + bool showtail) { assert(numMatches >= 0); // Add all the completion matches @@ -443,7 +451,7 @@ static int wildmenu_match_len(expand_T *xp, char *s) /// If inversion is possible we use it. Else '=' characters are used. /// /// @param matches list of matches -static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail) +static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, bool showtail) { int len; int clen; // length in screen cells @@ -509,7 +517,7 @@ static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int m } } - int fillchar = fillchar_status(&attr, curwin); + schar_T fillchar = fillchar_status(&attr, curwin); if (first_match == 0) { *buf = NUL; @@ -842,7 +850,7 @@ static char *find_longest_match(expand_T *xp, int options) char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode) { char *ss = NULL; - int orig_saved = false; + bool orig_saved = false; // first handle the case of using an old match if (mode == WILD_NEXT || mode == WILD_PREV @@ -966,7 +974,7 @@ void ExpandCleanup(expand_T *xp) /// @param showtail display only the tail of the full path of a file name /// @param dir_attr highlight attribute to use for directory names static void showmatches_oneline(expand_T *xp, char **matches, int numMatches, int lines, int linenr, - int maxlen, int showtail, int dir_attr) + int maxlen, bool showtail, int dir_attr) { char *p; int lastlen = 999; @@ -1025,7 +1033,7 @@ static void showmatches_oneline(expand_T *xp, char **matches, int numMatches, in /// Show all matches for completion on the command line. /// Returns EXPAND_NOTHING when the character that triggered expansion should /// be inserted like a normal character. -int showmatches(expand_T *xp, int wildmenu) +int showmatches(expand_T *xp, bool wildmenu) { CmdlineInfo *const ccline = get_cmdline_info(); int numMatches; @@ -1034,8 +1042,7 @@ int showmatches(expand_T *xp, int wildmenu) int maxlen; int lines; int columns; - int attr; - int showtail; + bool showtail; if (xp->xp_numfiles == -1) { set_expand_context(xp); @@ -1104,7 +1111,7 @@ int showmatches(expand_T *xp, int wildmenu) lines = (numMatches + columns - 1) / columns; } - attr = HL_ATTR(HLF_D); // find out highlighting for directories + int attr = HL_ATTR(HLF_D); // find out highlighting for directories if (xp->xp_context == EXPAND_TAGS_LISTFILES) { msg_puts_attr(_("tagname"), HL_ATTR(HLF_T)); @@ -1218,6 +1225,7 @@ char *addstar(char *fname, size_t len, int context) || context == EXPAND_COMPILER || context == EXPAND_OWNSYNTAX || context == EXPAND_FILETYPE + || context == EXPAND_KEYMAP || context == EXPAND_PACKADD || context == EXPAND_RUNTIME || ((context == EXPAND_TAGS_LISTFILES || context == EXPAND_TAGS) @@ -2583,7 +2591,7 @@ static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx) if (last_gen != get_cmdline_last_prompt_id() || last_gen == 0) { Array a = ARRAY_DICT_INIT; Error err = ERROR_INIT; - Object res = nlua_exec(STATIC_CSTR_AS_STRING("return vim.health._complete()"), a, &err); + Object res = NLUA_EXEC_STATIC("return vim.health._complete()", a, kRetObject, NULL, &err); api_clear_error(&err); api_free_object(names); names = res; @@ -2744,6 +2752,10 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM char *directories[] = { "syntax", "indent", "ftplugin", NULL }; return ExpandRTDir(pat, 0, numMatches, matches, directories); } + if (xp->xp_context == EXPAND_KEYMAP) { + char *directories[] = { "keymap", NULL }; + return ExpandRTDir(pat, 0, numMatches, matches, directories); + } if (xp->xp_context == EXPAND_USER_LIST) { return ExpandUserList(xp, matches, numMatches); } @@ -3242,11 +3254,11 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir copy_option_part(&path, buf, MAXPATHL, ","); if (strlen(buf) + strlen(file) + 2 < MAXPATHL) { add_pathsep(buf); - STRCAT(buf, file); // NOLINT + STRCAT(buf, file); char **p; int num_p = 0; - (void)ExpandFromContext(&xpc, buf, &p, &num_p, WILD_SILENT | expand_options); + ExpandFromContext(&xpc, buf, &p, &num_p, WILD_SILENT | expand_options); if (num_p > 0) { ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options); @@ -3266,7 +3278,7 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir } /// Translate some keys pressed when 'wildmenu' is used. -int wildmenu_translate_key(CmdlineInfo *cclp, int key, expand_T *xp, int did_wild_list) +int wildmenu_translate_key(CmdlineInfo *cclp, int key, expand_T *xp, bool did_wild_list) { int c = key; @@ -3312,7 +3324,7 @@ static int wildmenu_process_key_menunames(CmdlineInfo *cclp, int key, expand_T * } else if (key == K_UP) { // Hitting <Up>: Remove one submenu name in front of the // cursor - int found = false; + bool found = false; int j = (int)(xp->xp_pattern - cclp->cmdbuff); int i = 0; @@ -3368,7 +3380,7 @@ static int wildmenu_process_key_filenames(CmdlineInfo *cclp, int key, expand_T * KeyTyped = true; // in case the key was mapped } else if (strncmp(xp->xp_pattern, upseg + 1, 3) == 0 && key == K_DOWN) { // If in a direct ancestor, strip off one ../ to go down - int found = false; + bool found = false; int j = cclp->cmdpos; int i = (int)(xp->xp_pattern - cclp->cmdbuff); @@ -3389,7 +3401,7 @@ static int wildmenu_process_key_filenames(CmdlineInfo *cclp, int key, expand_T * } } else if (key == K_UP) { // go up a directory - int found = false; + bool found = false; int j = cclp->cmdpos - 1; int i = (int)(xp->xp_pattern - cclp->cmdbuff); diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h index b0772d26a3..33ff787589 100644 --- a/src/nvim/cmdexpand.h +++ b/src/nvim/cmdexpand.h @@ -1,9 +1,10 @@ #pragma once -#include "nvim/cmdexpand_defs.h" // IWYU pragma: export +#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep -#include "nvim/ex_getln.h" +#include "nvim/ex_getln_defs.h" // IWYU pragma: keep #include "nvim/garray_defs.h" // IWYU pragma: keep +#include "nvim/regexp_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep // Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. diff --git a/src/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h index 97307c4e50..c1fb85859c 100644 --- a/src/nvim/cmdexpand_defs.h +++ b/src/nvim/cmdexpand_defs.h @@ -4,7 +4,6 @@ #include <stddef.h> #include "nvim/eval/typval_defs.h" -#include "nvim/types_defs.h" typedef enum { XP_PREFIX_NONE, ///< prefix not used @@ -15,7 +14,7 @@ typedef enum { enum { EXPAND_BUF_LEN = 256, }; /// used for completion on the command line -typedef struct expand { +typedef struct { char *xp_pattern; ///< start of item to expand, guaranteed ///< to be part of xp_line int xp_context; ///< type of expansion @@ -105,6 +104,7 @@ enum { EXPAND_STRING_SETTING, EXPAND_SETTING_SUBTRACT, EXPAND_ARGOPT, + EXPAND_KEYMAP, EXPAND_CHECKHEALTH, EXPAND_LUA, }; diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c index 4556b74396..6a9290270a 100644 --- a/src/nvim/cmdhist.c +++ b/src/nvim/cmdhist.c @@ -15,14 +15,15 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_getln.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option_vars.h" +#include "nvim/os/time.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/vim_defs.h" @@ -374,7 +375,7 @@ static int calc_hist_idx(int histype, int num) histentry_T *hist = history[histype]; if (num > 0) { - int wrapped = false; + bool wrapped = false; while (hist[i].hisnum > num) { if (--i < 0) { if (wrapped) { diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h index cce0f92898..43be397cee 100644 --- a/src/nvim/cmdhist.h +++ b/src/nvim/cmdhist.h @@ -1,10 +1,10 @@ #pragma once -#include "nvim/cmdexpand_defs.h" // IWYU pragma: export +#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" -#include "nvim/ex_cmds_defs.h" // IWYU pragma: export -#include "nvim/os/time.h" -#include "nvim/types_defs.h" +#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep +#include "nvim/os/time_defs.h" +#include "nvim/types_defs.h" // IWYU pragma: keep /// Present history tables typedef enum { @@ -17,11 +17,10 @@ typedef enum { HIST_DEBUG, ///< Debug commands. } HistoryType; -/// Number of history tables -#define HIST_COUNT (HIST_DEBUG + 1) +enum { HIST_COUNT = HIST_DEBUG + 1, }; ///< Number of history tables /// History entry definition -typedef struct hist_entry { +typedef struct { int hisnum; ///< Entry identifier number. char *hisstr; ///< Actual entry, separator char after the NUL. Timestamp timestamp; ///< Time when entry was added. diff --git a/src/nvim/context.c b/src/nvim/context.c index 59309fcf16..95e2618f62 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -14,13 +14,14 @@ #include "nvim/context.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/hashtab.h" #include "nvim/keycodes.h" #include "nvim/memory.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/shada.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -139,8 +140,8 @@ bool ctx_restore(Context *ctx, const int flags) free_ctx = true; } - OptVal op_shada = get_option_value("shada", NULL, OPT_GLOBAL, NULL); - set_option_value("shada", STATIC_CSTR_AS_OPTVAL("!,'100,%"), OPT_GLOBAL); + OptVal op_shada = get_option_value(kOptShada, OPT_GLOBAL); + set_option_value(kOptShada, STATIC_CSTR_AS_OPTVAL("!,'100,%"), OPT_GLOBAL); if (flags & kCtxRegs) { ctx_restore_regs(ctx); @@ -166,7 +167,7 @@ bool ctx_restore(Context *ctx, const int flags) ctx_free(ctx); } - set_option_value("shada", op_shada, OPT_GLOBAL); + set_option_value(kOptShada, op_shada, OPT_GLOBAL); optval_free(op_shada); return true; @@ -295,7 +296,7 @@ static inline void ctx_restore_funcs(Context *ctx) /// @param[in] sbuf msgpack_sbuffer to convert. /// /// @return readfile()-style array representation of "sbuf". -static inline Array sbuf_to_array(msgpack_sbuffer sbuf) +static inline Array sbuf_to_array(msgpack_sbuffer sbuf, Arena *arena) { list_T *const list = tv_list_alloc(kListLenMayKnow); tv_list_append_string(list, "", 0); @@ -309,7 +310,7 @@ static inline Array sbuf_to_array(msgpack_sbuffer sbuf) .vval.v_list = list }; - Array array = vim_to_object(&list_tv).data.array; + Array array = vim_to_object(&list_tv, arena, false).data.array; tv_clear(&list_tv); return array; } @@ -327,9 +328,7 @@ static inline msgpack_sbuffer array_to_sbuf(Array array, Error *err) msgpack_sbuffer_init(&sbuf); typval_T list_tv; - if (!object_to_vim(ARRAY_OBJ(array), &list_tv, err)) { - return sbuf; - } + object_to_vim(ARRAY_OBJ(array), &list_tv, err); assert(list_tv.v_type == VAR_LIST); if (!encode_vim_list_to_buf(list_tv.vval.v_list, &sbuf.size, &sbuf.data)) { @@ -347,18 +346,18 @@ static inline msgpack_sbuffer array_to_sbuf(Array array, Error *err) /// @param[in] ctx Context to convert. /// /// @return Dictionary representing "ctx". -Dictionary ctx_to_dict(Context *ctx) +Dictionary ctx_to_dict(Context *ctx, Arena *arena) FUNC_ATTR_NONNULL_ALL { assert(ctx != NULL); - Dictionary rv = ARRAY_DICT_INIT; + Dictionary rv = arena_dict(arena, 5); - PUT(rv, "regs", ARRAY_OBJ(sbuf_to_array(ctx->regs))); - PUT(rv, "jumps", ARRAY_OBJ(sbuf_to_array(ctx->jumps))); - PUT(rv, "bufs", ARRAY_OBJ(sbuf_to_array(ctx->bufs))); - PUT(rv, "gvars", ARRAY_OBJ(sbuf_to_array(ctx->gvars))); - PUT(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs, NULL))); + PUT_C(rv, "regs", ARRAY_OBJ(sbuf_to_array(ctx->regs, arena))); + PUT_C(rv, "jumps", ARRAY_OBJ(sbuf_to_array(ctx->jumps, arena))); + PUT_C(rv, "bufs", ARRAY_OBJ(sbuf_to_array(ctx->bufs, arena))); + PUT_C(rv, "gvars", ARRAY_OBJ(sbuf_to_array(ctx->gvars, arena))); + PUT_C(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs, arena))); return rv; } diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 1110fe1e64..e93e658f1e 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -11,9 +11,9 @@ #include "nvim/drawscreen.h" #include "nvim/fold.h" #include "nvim/globals.h" -#include "nvim/macros_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/move.h" @@ -22,6 +22,8 @@ #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/state.h" +#include "nvim/state_defs.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -140,17 +142,18 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a } } - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line); - while (cts.cts_vcol <= wcol && *cts.cts_ptr != NUL) { - // Count a tab for what it's worth (if list mode not on) - csize = win_lbr_chartabsize(&cts, &head); - MB_PTR_ADV(cts.cts_ptr); - cts.cts_vcol += csize; + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, pos->lnum, line); + StrCharInfo ci = utf_ptr2StrCharInfo(line); + col = 0; + while (col <= wcol && *ci.ptr != NUL) { + CharSize cs = win_charsize(cstype, col, ci.ptr, ci.chr.value, &csarg); + csize = cs.width; + head = cs.head; + col += cs.width; + ci = utfc_next(ci); } - col = cts.cts_vcol; - idx = (int)(cts.cts_ptr - line); - clear_chartabsize_arg(&cts); + idx = (int)(ci.ptr - line); // Handle all the special cases. The virtual_active() check // is needed to ensure that a virtual position off the end of @@ -291,7 +294,7 @@ linenr_T get_cursor_rel_lnum(win_T *wp, linenr_T lnum) // Loop until we reach to_line, skipping folds. for (; from_line < to_line; from_line++, retval++) { // If from_line is in a fold, set it to the last line of that fold. - (void)hasFoldingWin(wp, from_line, NULL, &from_line, true, NULL); + hasFoldingWin(wp, from_line, NULL, &from_line, true, NULL); } // If to_line is in a closed fold, the line count is off by +1. Correct it. diff --git a/src/nvim/cursor.h b/src/nvim/cursor.h index d50976b598..19107dfec9 100644 --- a/src/nvim/cursor.h +++ b/src/nvim/cursor.h @@ -1,7 +1,7 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "cursor.h.generated.h" diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 5aff3b5598..67bb34d4ea 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -8,8 +8,7 @@ #include "nvim/charset.h" #include "nvim/cursor_shape.h" #include "nvim/ex_getln.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight_group.h" #include "nvim/log.h" @@ -102,16 +101,10 @@ Array mode_style_array(Arena *arena) /// @returns error message for an illegal option, NULL otherwise. const char *parse_shape_opt(int what) { - char *colonp; - char *commap; - char *slashp; char *p = NULL; - char *endp; int idx = 0; // init for GCC - int all_idx; int len; - int i; - int found_ve = false; // found "ve" flag + bool found_ve = false; // found "ve" flag // First round: check for errors; second round: do it for real. for (int round = 1; round <= 2; round++) { @@ -127,8 +120,8 @@ const char *parse_shape_opt(int what) // Repeat for all comma separated parts. char *modep = p_guicursor; while (modep != NULL && *modep != NUL) { - colonp = vim_strchr(modep, ':'); - commap = vim_strchr(modep, ','); + char *colonp = vim_strchr(modep, ':'); + char *commap = vim_strchr(modep, ','); if (colonp == NULL || (commap != NULL && commap < colonp)) { return N_("E545: Missing colon"); @@ -139,7 +132,7 @@ const char *parse_shape_opt(int what) // Repeat for all modes before the colon. // For the 'a' mode, we loop to handle all the modes. - all_idx = -1; + int all_idx = -1; while (modep < colonp || all_idx >= 0) { if (all_idx < 0) { // Find the mode @@ -176,7 +169,7 @@ const char *parse_shape_opt(int what) for (p = colonp + 1; *p && *p != ',';) { { // First handle the ones with a number argument. - i = (uint8_t)(*p); + int i = (uint8_t)(*p); len = 0; if (STRNICMP(p, "ver", 3) == 0) { len = 3; @@ -222,7 +215,7 @@ const char *parse_shape_opt(int what) } p += 5; } else { // must be a highlight group name then - endp = vim_strchr(p, '-'); + char *endp = vim_strchr(p, '-'); if (commap == NULL) { // last part if (endp == NULL) { endp = p + strlen(p); // find end of part @@ -230,7 +223,7 @@ const char *parse_shape_opt(int what) } else if (endp > commap || endp == NULL) { endp = commap; } - slashp = vim_strchr(p, '/'); + char *slashp = vim_strchr(p, '/'); if (slashp != NULL && slashp < endp) { // "group/langmap_group" i = syn_check_group(p, (size_t)(slashp - p)); diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 4c1d6d4eec..21967a81f4 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -38,7 +38,7 @@ typedef enum { #define SHAPE_MOUSE 1 // used for mouse pointer shape #define SHAPE_CURSOR 2 // used for text cursor shape -typedef struct cursor_entry { +typedef struct { char *full_name; ///< mode description CursorShape shape; ///< cursor shape: one of the SHAPE_ defines int mshape; ///< mouse shape: one of the MSHAPE defines diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c index a343c1ad6b..7d87b61ce5 100644 --- a/src/nvim/debugger.c +++ b/src/nvim/debugger.c @@ -8,31 +8,37 @@ #include <string.h> #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/state_defs.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" /// batch mode debugging: don't save and restore typeahead. @@ -284,7 +290,7 @@ void do_debug(char *cmd) // don't debug this command n = debug_break_level; debug_break_level = -1; - (void)do_cmdline(cmdline, getexline, NULL, DOCMD_VERBOSE|DOCMD_EXCRESET); + do_cmdline(cmdline, getexline, NULL, DOCMD_VERBOSE|DOCMD_EXCRESET); debug_break_level = n; } lines_left = Rows - 1; @@ -407,7 +413,7 @@ static linenr_T debug_breakpoint_lnum; /// debug_skipped_name is then set to the source name in the breakpoint case. If /// a skipped command decides itself that a debug prompt should be displayed, it /// can do so by calling dbg_check_skipped(). -static int debug_skipped; +static bool debug_skipped; static char *debug_skipped_name; /// Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is @@ -462,7 +468,7 @@ bool dbg_check_skipped(exarg_T *eap) // Save the value of got_int and reset it. We don't want a previous // interruption cause flushing the input buffer. - int prev_got_int = got_int; + bool prev_got_int = got_int; got_int = false; debug_breakpoint_name = debug_skipped_name; // eap->skip is true @@ -787,7 +793,6 @@ static linenr_T debuggy_find(bool file, char *fname, linenr_T after, garray_T *g struct debuggy *bp; linenr_T lnum = 0; char *name = fname; - int prev_got_int; // Return quickly when there are no breakpoints. if (GA_EMPTY(gap)) { @@ -812,7 +817,7 @@ static linenr_T debuggy_find(bool file, char *fname, linenr_T after, garray_T *g // Save the value of got_int and reset it. We don't want a // previous interruption cancel matching, only hitting CTRL-C // while matching should abort it. - prev_got_int = got_int; + bool prev_got_int = got_int; got_int = false; if (vim_regexec_prog(&bp->dbg_prog, false, name, 0)) { lnum = bp->dbg_lnum; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 11204a1b31..51d5d08f78 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -2,18 +2,24 @@ #include <limits.h> #include <stddef.h> #include <stdlib.h> +#include <string.h> #include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/ascii_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" +#include "nvim/change.h" #include "nvim/decoration.h" #include "nvim/drawscreen.h" #include "nvim/extmark.h" #include "nvim/fold.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" +#include "nvim/marktree.h" #include "nvim/memory.h" #include "nvim/move.h" #include "nvim/option_vars.h" @@ -82,26 +88,22 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start extmark_set(buf, (uint32_t)src_id, NULL, (int)lnum - 1, hl_start, (int)lnum - 1 + end_off, hl_end, - decor, MT_FLAG_DECOR_HL, true, false, true, false, NULL); + decor, MT_FLAG_DECOR_HL, true, false, true, false, false, NULL); } } -void decor_redraw(buf_T *buf, int row1, int row2, DecorInline decor) +void decor_redraw(buf_T *buf, int row1, int row2, int col1, DecorInline decor) { - if (row2 >= row1) { - redraw_buf_range_later(buf, row1 + 1, row2 + 1); - } - if (decor.ext) { DecorVirtText *vt = decor.data.ext.vt; while (vt) { - if (vt->flags & kVTIsLines) { - redraw_buf_line_later(buf, row1 + 1 + ((vt->flags & kVTLinesAbove) ? 0 : 1), true); - changed_line_display_buf(buf); - } else { - if (vt->pos == kVPosInline) { - changed_line_display_buf(buf); - } + bool below = (vt->flags & kVTIsLines) && !(vt->flags & kVTLinesAbove); + linenr_T vt_lnum = row1 + 1 + below; + redraw_buf_line_later(buf, vt_lnum, true); + if (vt->flags & kVTIsLines || vt->pos == kVPosInline) { + // changed_lines_redraw_buf(buf, vt_lnum, vt_lnum + 1, 0); + colnr_T vt_col = vt->flags & kVTIsLines ? 0 : col1; + changed_lines_invalidate_buf(buf, vt_lnum, vt_col, vt_lnum + 1, 0); } vt = vt->next; } @@ -119,7 +121,8 @@ void decor_redraw(buf_T *buf, int row1, int row2, DecorInline decor) void decor_redraw_sh(buf_T *buf, int row1, int row2, DecorSignHighlight sh) { - if (sh.hl_id || (sh.flags & (kSHIsSign|kSHSpellOn|kSHSpellOff))) { + if (sh.hl_id || (sh.url != NULL) + || (sh.flags & (kSHIsSign | kSHSpellOn | kSHSpellOff | kSHConceal))) { if (row2 >= row1) { redraw_buf_range_later(buf, row1 + 1, row2 + 1); } @@ -158,7 +161,7 @@ DecorSignHighlight decor_sh_from_inline(DecorHighlightInline item) DecorSignHighlight conv = { .flags = item.flags, .priority = item.priority, - .text.sc[0] = item.conceal_char, + .text[0] = item.conceal_char, .hl_id = item.hl_id, .number_hl_id = 0, .line_hl_id = 0, @@ -172,12 +175,6 @@ DecorSignHighlight decor_sh_from_inline(DecorHighlightInline item) void buf_put_decor(buf_T *buf, DecorInline decor, int row, int row2) { if (decor.ext) { - DecorVirtText *vt = decor.data.ext.vt; - while (vt) { - buf_put_decor_virt(buf, vt); - vt = vt->next; - } - uint32_t idx = decor.data.ext.sh_idx; while (idx != DECOR_ID_INVALID) { DecorSignHighlight *sh = &kv_A(decor_items, idx); @@ -187,46 +184,25 @@ void buf_put_decor(buf_T *buf, DecorInline decor, int row, int row2) } } -void buf_put_decor_virt(buf_T *buf, DecorVirtText *vt) -{ - if (vt->flags &kVTIsLines) { - buf->b_virt_line_blocks++; - } else { - if (vt->pos == kVPosInline) { - buf->b_virt_text_inline++; - } - } - if (vt->next) { - buf_put_decor_virt(buf, vt->next); - } -} - static int sign_add_id = 0; -void buf_put_decor_sh(buf_T *buf, DecorSignHighlight *sh, int row, int row2) +void buf_put_decor_sh(buf_T *buf, DecorSignHighlight *sh, int row1, int row2) { if (sh->flags & kSHIsSign) { sh->sign_add_id = sign_add_id++; - buf->b_signs++; - if (sh->text.ptr) { - buf->b_signs_with_text++; - buf_signcols_add_check(buf, row + 1, row2 + 1); + if (sh->text[0]) { + buf_signcols_count_range(buf, row1, row2, 1, kFalse); } } } -void buf_decor_remove(buf_T *buf, int row, int row2, DecorInline decor, bool free) +void buf_decor_remove(buf_T *buf, int row1, int row2, int col1, DecorInline decor, bool free) { - decor_redraw(buf, row, row2, decor); + decor_redraw(buf, row1, row2, col1, decor); if (decor.ext) { - DecorVirtText *vt = decor.data.ext.vt; - while (vt) { - buf_remove_decor_virt(buf, vt); - vt = vt->next; - } uint32_t idx = decor.data.ext.sh_idx; while (idx != DECOR_ID_INVALID) { DecorSignHighlight *sh = &kv_A(decor_items, idx); - buf_remove_decor_sh(buf, row, row2, sh); + buf_remove_decor_sh(buf, row1, row2, sh); idx = sh->next; } if (free) { @@ -235,29 +211,15 @@ void buf_decor_remove(buf_T *buf, int row, int row2, DecorInline decor, bool fre } } -void buf_remove_decor_virt(buf_T *buf, DecorVirtText *vt) -{ - if (vt->flags &kVTIsLines) { - assert(buf->b_virt_line_blocks > 0); - buf->b_virt_line_blocks--; - } else { - if (vt->pos == kVPosInline) { - assert(buf->b_virt_text_inline > 0); - buf->b_virt_text_inline--; - } - } -} - -void buf_remove_decor_sh(buf_T *buf, int row, int row2, DecorSignHighlight *sh) +void buf_remove_decor_sh(buf_T *buf, int row1, int row2, DecorSignHighlight *sh) { if (sh->flags & kSHIsSign) { - assert(buf->b_signs > 0); - buf->b_signs--; - if (sh->text.ptr) { - assert(buf->b_signs_with_text > 0); - buf->b_signs_with_text--; - if (row2 >= row) { - buf_signcols_del_check(buf, row + 1, row2 + 1); + if (sh->text[0]) { + if (buf_meta_total(buf, kMTMetaSignText)) { + buf_signcols_count_range(buf, row1, row2, -1, kFalse); + } else { + buf->b_signcols.resized = true; + buf->b_signcols.max = buf->b_signcols.count[0] = 0; } } } @@ -295,7 +257,7 @@ void decor_free(DecorInline decor) } } -void decor_free_inner(DecorVirtText *vt, uint32_t first_idx) +static void decor_free_inner(DecorVirtText *vt, uint32_t first_idx) { while (vt) { if (vt->flags & kVTIsLines) { @@ -312,12 +274,12 @@ void decor_free_inner(DecorVirtText *vt, uint32_t first_idx) while (idx != DECOR_ID_INVALID) { DecorSignHighlight *sh = &kv_A(decor_items, idx); if (sh->flags & kSHIsSign) { - xfree(sh->text.ptr); - } - if (sh->flags & kSHIsSign) { xfree(sh->sign_name); } sh->flags = 0; + if (sh->url != NULL) { + XFREE_CLEAR(sh->url); + } if (sh->next == DECOR_ID_INVALID) { sh->next = decor_freelist; decor_freelist = first_idx; @@ -362,8 +324,11 @@ void decor_check_invalid_glyphs(void) { for (size_t i = 0; i < kv_size(decor_items); i++) { DecorSignHighlight *it = &kv_A(decor_items, i); - if ((it->flags & kSHConceal) && schar_high(it->text.sc[0])) { - it->text.sc[0] = schar_from_char(schar_get_first_codepoint(it->text.sc[0])); + int width = (it->flags & kSHIsSign) ? SIGN_WIDTH : ((it->flags & kSHConceal) ? 1 : 0); + for (int j = 0; j < width; j++) { + if (schar_high(it->text[j])) { + it->text[j] = schar_from_char(schar_get_first_codepoint(it->text[j])); + } } } } @@ -394,10 +359,10 @@ DecorVirtText *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id) MTKey mark = marktree_itr_current(itr); if (mark.pos.row < 0 || mark.pos.row > row) { break; - } else if (mt_invalid(mark) || !(mark.flags & MT_FLAG_DECOR_EXT)) { + } else if (mt_invalid(mark)) { goto next_mark; } - DecorVirtText *decor = mark.decor_data.ext.vt; + DecorVirtText *decor = mt_decor_virt(mark); while (decor && (decor->flags & kVTIsLines)) { decor = decor->next; } @@ -489,18 +454,21 @@ static void decor_range_add_from_inline(DecorState *state, int start_row, int st if (decor.ext) { DecorVirtText *vt = decor.data.ext.vt; while (vt) { - decor_range_add_virt(state, start_row, start_col, end_row, end_col, vt, owned); + decor_range_add_virt(state, start_row, start_col, end_row, end_col, vt, owned, + DECOR_PRIORITY_BASE); vt = vt->next; } uint32_t idx = decor.data.ext.sh_idx; while (idx != DECOR_ID_INVALID) { DecorSignHighlight *sh = &kv_A(decor_items, idx); - decor_range_add_sh(state, start_row, start_col, end_row, end_col, sh, owned, ns, mark_id); + decor_range_add_sh(state, start_row, start_col, end_row, end_col, sh, owned, ns, mark_id, + DECOR_PRIORITY_BASE); idx = sh->next; } } else { DecorSignHighlight sh = decor_sh_from_inline(decor.data.hl); - decor_range_add_sh(state, start_row, start_col, end_row, end_col, &sh, owned, ns, mark_id); + decor_range_add_sh(state, start_row, start_col, end_row, end_col, &sh, owned, ns, mark_id, + DECOR_PRIORITY_BASE); } } @@ -510,7 +478,8 @@ static void decor_range_insert(DecorState *state, DecorRange range) size_t index; for (index = kv_size(state->active) - 1; index > 0; index--) { DecorRange item = kv_A(state->active, index - 1); - if (item.priority <= range.priority) { + if ((item.priority < range.priority) + || ((item.priority == range.priority) && (item.subpriority <= range.subpriority))) { break; } kv_A(state->active, index) = kv_A(state->active, index - 1); @@ -519,7 +488,7 @@ static void decor_range_insert(DecorState *state, DecorRange range) } void decor_range_add_virt(DecorState *state, int start_row, int start_col, int end_row, int end_col, - DecorVirtText *vt, bool owned) + DecorVirtText *vt, bool owned, DecorPriority subpriority) { bool is_lines = vt->flags & kVTIsLines; DecorRange range = { @@ -529,13 +498,15 @@ void decor_range_add_virt(DecorState *state, int start_row, int start_col, int e .attr_id = 0, .owned = owned, .priority = vt->priority, + .subpriority = subpriority, .draw_col = -10, }; decor_range_insert(state, range); } void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end_row, int end_col, - DecorSignHighlight *sh, bool owned, uint32_t ns, uint32_t mark_id) + DecorSignHighlight *sh, bool owned, uint32_t ns, uint32_t mark_id, + DecorPriority subpriority) { if (sh->flags & kSHIsSign) { return; @@ -548,10 +519,12 @@ void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end .attr_id = 0, .owned = owned, .priority = sh->priority, + .subpriority = subpriority, .draw_col = -10, }; - if (sh->hl_id || (sh->flags & (kSHConceal | kSHSpellOn | kSHSpellOff))) { + if (sh->hl_id || (sh->url != NULL) + || (sh->flags & (kSHConceal | kSHSpellOn | kSHSpellOff))) { if (sh->hl_id) { range.attr_id = syn_id2attr(sh->hl_id); } @@ -568,7 +541,7 @@ void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end } /// Initialize the draw_col of a newly-added virtual text item. -static void decor_init_draw_col(int win_col, bool hidden, DecorRange *item) +void decor_init_draw_col(int win_col, bool hidden, DecorRange *item) { DecorVirtText *vt = item->kind == kDecorKindVirtText ? item->data.vt : NULL; VirtTextPos pos = decor_virt_pos_kind(item); @@ -609,15 +582,15 @@ int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *s break; } - if (mt_invalid(mark) || mt_end(mark) || !mt_decor_any(mark)) { + if (!mt_scoped_in_win(mark, wp)) { goto next_mark; } - MTPos endpos = marktree_get_altpos(buf->b_marktree, mark, NULL); - if (endpos.row == -1) { - endpos = mark.pos; + if (mt_invalid(mark) || mt_end(mark) || !mt_decor_any(mark)) { + goto next_mark; } + MTPos endpos = marktree_get_altpos(buf->b_marktree, mark, NULL); decor_range_add_from_inline(state, mark.pos.row, mark.pos.col, endpos.row, endpos.col, mt_decor(mark), false, mark.ns, mark.id); @@ -661,7 +634,7 @@ next_mark: if (item.start_row == state->row && item.start_col == col) { DecorSignHighlight *sh = &item.data.sh; conceal = 2; - conceal_char = sh->text.sc[0]; + conceal_char = sh->text[0]; state->col_until = MIN(state->col_until, item.start_col); conceal_attr = item.attr_id; } @@ -672,6 +645,9 @@ next_mark: } else if (item.data.sh.flags & kSHSpellOff) { spell = kFalse; } + if (item.data.sh.url != NULL) { + attr = hl_add_url(attr, item.data.sh.url); + } } if (item.start_row == state->row && item.start_col <= col && decor_virt_pos(&item) && item.draw_col == -10) { @@ -679,9 +655,13 @@ next_mark: } if (keep) { kv_A(state->active, j++) = item; - } else if (item.owned && item.kind == kDecorKindVirtText) { - clear_virttext(&item.data.vt->data.virt_text); - xfree(item.data.vt); + } else if (item.owned) { + if (item.kind == kDecorKindVirtText) { + clear_virttext(&item.data.vt->data.virt_text); + xfree(item.data.vt); + } else if (item.kind == kDecorKindHighlight) { + xfree((void *)item.data.sh.url); + } } } kv_size(state->active) = j; @@ -693,21 +673,29 @@ next_mark: return attr; } -typedef struct { - DecorSignHighlight *sh; - uint32_t id; -} SignItem; - int sign_item_cmp(const void *p1, const void *p2) { const SignItem *s1 = (SignItem *)p1; const SignItem *s2 = (SignItem *)p2; - int n = s2->sh->priority - s1->sh->priority; - return n ? n : (n = (int)(s2->id - s1->id)) - ? n : (s2->sh->sign_add_id - s1->sh->sign_add_id); + if (s1->sh->priority != s2->sh->priority) { + return s1->sh->priority < s2->sh->priority ? 1 : -1; + } + + if (s1->id != s2->id) { + return s1->id < s2->id ? 1 : -1; + } + + if (s1->sh->sign_add_id != s2->sh->sign_add_id) { + return s1->sh->sign_add_id < s2->sh->sign_add_id ? 1 : -1; + } + + return 0; } +static const uint32_t sign_filter[4] = {[kMTMetaSignText] = kMTFilterSelect, + [kMTMetaSignHL] = kMTFilterSelect }; + /// Return the sign attributes on the currently refreshed row. /// /// @param[out] sattrs Output array for sign text and texthl id @@ -717,35 +705,39 @@ int sign_item_cmp(const void *p1, const void *p2) void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[], int *line_id, int *cul_id, int *num_id) { - MarkTreeIter itr[1]; - if (!buf->b_signs || !marktree_itr_get_overlap(buf->b_marktree, row, 0, itr)) { + if (!buf_has_signs(buf)) { return; } MTPair pair; int num_text = 0; + MarkTreeIter itr[1]; kvec_t(SignItem) signs = KV_INITIAL_VALUE; // TODO(bfredl): integrate with main decor loop. + marktree_itr_get_overlap(buf->b_marktree, row, 0, itr); while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) { if (!mt_invalid(pair.start) && mt_decor_sign(pair.start)) { DecorSignHighlight *sh = decor_find_sign(mt_decor(pair.start)); - num_text += (sh->text.ptr != NULL); + num_text += (sh->text[0] != NUL); kv_push(signs, ((SignItem){ sh, pair.start.id })); } } + marktree_itr_step_out_filter(buf->b_marktree, itr, sign_filter); + while (itr->x) { MTKey mark = marktree_itr_current(itr); if (mark.pos.row != row) { break; } - if (!mt_end(mark) && !mt_invalid(mark) && mt_decor_sign(mark)) { + if (!mt_end(mark) && !mt_invalid(mark) && mt_decor_sign(mark) + && mt_scoped_in_win(mark, wp)) { DecorSignHighlight *sh = decor_find_sign(mt_decor(mark)); - num_text += (sh->text.ptr != NULL); + num_text += (sh->text[0] != NUL); kv_push(signs, ((SignItem){ sh, mark.id })); } - marktree_itr_next(buf->b_marktree, itr); + marktree_itr_next_filter(buf->b_marktree, itr, row + 1, 0, sign_filter); } if (kv_size(signs)) { @@ -755,8 +747,8 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[], for (size_t i = 0; i < kv_size(signs); i++) { DecorSignHighlight *sh = kv_A(signs, i).sh; - if (idx >= 0 && sh->text.ptr) { - sattrs[idx].text = sh->text.ptr; + if (idx >= 0 && sh->text[0]) { + memcpy(sattrs[idx].text, sh->text, SIGN_WIDTH * sizeof(sattr_T)); sattrs[idx--].hl_id = sh->hl_id; } if (*num_id == 0) { @@ -791,50 +783,74 @@ DecorSignHighlight *decor_find_sign(DecorInline decor) } } -// Increase the signcolumn size and update the sentinel line if necessary for -// the invalidated range. -void decor_validate_signcols(buf_T *buf, int max) +static const uint32_t signtext_filter[4] = {[kMTMetaSignText] = kMTFilterSelect }; + +/// Count the number of signs in a range after adding/removing a sign, or to +/// (re-)initialize a range in "b_signcols.count". +/// +/// @param add 1, -1 or 0 for an added, deleted or initialized range. +/// @param clear kFalse, kTrue or kNone for an, added/deleted, cleared, or initialized range. +void buf_signcols_count_range(buf_T *buf, int row1, int row2, int add, TriState clear) { - int signcols = 0; // highest value of count - int currow = buf->b_signcols.invalid_top - 1; - // TODO(bfredl): only need to use marktree_itr_get_overlap once. - // then we can process both start and end events and update state for each row - for (; currow < buf->b_signcols.invalid_bot; currow++) { - MarkTreeIter itr[1]; - if (!marktree_itr_get_overlap(buf->b_marktree, currow, 0, itr)) { - continue; - } + if (!buf->b_signcols.autom || !buf_meta_total(buf, kMTMetaSignText)) { + return; + } + + // Allocate an array of integers holding the number of signs in the range. + assert(row2 >= row1); + int *count = xcalloc(sizeof(int), (size_t)(row2 + 1 - row1)); + MarkTreeIter itr[1]; + MTPair pair = { 0 }; - int count = 0; - MTPair pair; - while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) { - if (!mt_invalid(pair.start) && (pair.start.flags & MT_FLAG_DECOR_SIGNTEXT)) { - count++; + // Increment count array for signs that start before "row1" but do overlap the range. + marktree_itr_get_overlap(buf->b_marktree, row1, 0, itr); + while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) { + if ((pair.start.flags & MT_FLAG_DECOR_SIGNTEXT) && !mt_invalid(pair.start)) { + for (int i = row1; i <= MIN(row2, pair.end_pos.row); i++) { + count[i - row1]++; } } + } - while (itr->x) { - MTKey mark = marktree_itr_current(itr); - if (mark.pos.row != currow) { - break; - } - if (!mt_invalid(mark) && !mt_end(mark) && (mark.flags & MT_FLAG_DECOR_SIGNTEXT)) { - count++; + marktree_itr_step_out_filter(buf->b_marktree, itr, signtext_filter); + + // Continue traversing the marktree until beyond "row2". + while (itr->x) { + MTKey mark = marktree_itr_current(itr); + if (mark.pos.row > row2) { + break; + } + if ((mark.flags & MT_FLAG_DECOR_SIGNTEXT) && !mt_invalid(mark) && !mt_end(mark)) { + // Increment count array for the range of a paired sign mark. + MTPos end = marktree_get_altpos(buf->b_marktree, mark, NULL); + for (int i = mark.pos.row; i <= MIN(row2, end.row); i++) { + count[i - row1]++; } - marktree_itr_next(buf->b_marktree, itr); } - if (count > signcols) { - if (count >= buf->b_signcols.size) { - buf->b_signcols.size = count; - buf->b_signcols.sentinel = currow + 1; - } - if (count >= max) { - return; + marktree_itr_next_filter(buf->b_marktree, itr, row2 + 1, 0, signtext_filter); + } + + // For each row increment "b_signcols.count" at the number of counted signs, + // and decrement at the previous number of signs. These two operations are + // split in separate calls if "clear" is not kFalse (surrounding a marktree splice). + for (int i = 0; i < row2 + 1 - row1; i++) { + int prevwidth = MIN(SIGN_SHOW_MAX, count[i] - add); + if (clear != kNone && prevwidth > 0) { + buf->b_signcols.count[prevwidth - 1]--; + assert(buf->b_signcols.count[prevwidth - 1] >= 0); + } + int width = MIN(SIGN_SHOW_MAX, count[i]); + if (clear != kTrue && width > 0) { + buf->b_signcols.count[width - 1]++; + if (width > buf->b_signcols.max) { + buf->b_signcols.resized = true; + buf->b_signcols.max = width; } - signcols = count; } } + + xfree(count); } void decor_redraw_end(DecorState *state) @@ -861,11 +877,13 @@ bool decor_redraw_eol(win_T *wp, DecorState *state, int *eol_attr, int eol_col) return has_virt_pos; } +static const uint32_t lines_filter[4] = {[kMTMetaLines] = kMTFilterSelect }; + /// @param has_fold whether line "lnum" has a fold, or kNone when not calculated yet int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fold) { buf_T *buf = wp->w_buffer; - if (!buf->b_virt_line_blocks) { + if (!buf_meta_total(buf, kMTMetaLines)) { // Only pay for what you use: in case virt_lines feature is not active // in a buffer, plines do not need to access the marktree at all return 0; @@ -884,39 +902,43 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fo return 0; } - int virt_lines = 0; MarkTreeIter itr[1] = { 0 }; - marktree_itr_get(buf->b_marktree, start_row, 0, itr); + if (!marktree_itr_get_filter(buf->b_marktree, start_row, 0, end_row, 0, lines_filter, itr)) { + return 0; + } + + int virt_lines = 0; while (true) { MTKey mark = marktree_itr_current(itr); - if (mark.pos.row < 0 || mark.pos.row >= end_row) { - break; - } else if (mt_end(mark) || !(mark.flags & MT_FLAG_DECOR_VIRT_LINES)) { - goto next_mark; - } - DecorVirtText *vt = mark.decor_data.ext.vt; - while (vt) { - if (vt->flags & kVTIsLines) { - bool above = vt->flags & kVTLinesAbove; - int draw_row = mark.pos.row + (above ? 0 : 1); - if (draw_row == row) { - virt_lines += (int)kv_size(vt->data.virt_lines); - if (lines) { - kv_splice(*lines, vt->data.virt_lines); + DecorVirtText *vt = mt_decor_virt(mark); + if (mt_scoped_in_win(mark, wp)) { + while (vt) { + if (vt->flags & kVTIsLines) { + bool above = vt->flags & kVTLinesAbove; + int draw_row = mark.pos.row + (above ? 0 : 1); + if (draw_row == row) { + virt_lines += (int)kv_size(vt->data.virt_lines); + if (lines) { + kv_splice(*lines, vt->data.virt_lines); + } } } + vt = vt->next; } - vt = vt->next; } -next_mark: - marktree_itr_next(buf->b_marktree, itr); + + if (!marktree_itr_next_filter(buf->b_marktree, itr, end_row, 0, lines_filter)) { + break; + } } return virt_lines; } /// This assumes maximum one entry of each kind, which will not always be the case. -void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name) +/// +/// NB: assumes caller has allocated enough space in dict for all fields! +void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name, Arena *arena) { DecorSignHighlight sh_hl = DECOR_SIGN_HIGHLIGHT_INIT; DecorSignHighlight sh_sign = DECOR_SIGN_HIGHLIGHT_INIT; @@ -950,64 +972,70 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name) } if (sh_hl.hl_id) { - PUT(*dict, "hl_group", hl_group_name(sh_hl.hl_id, hl_name)); - PUT(*dict, "hl_eol", BOOLEAN_OBJ(sh_hl.flags & kSHHlEol)); - if (sh_hl.flags & kSHConceal) { - char buf[MAX_SCHAR_SIZE]; - schar_get(buf, sh_hl.text.sc[0]); - PUT(*dict, "conceal", CSTR_TO_OBJ(buf)); - } + PUT_C(*dict, "hl_group", hl_group_name(sh_hl.hl_id, hl_name)); + PUT_C(*dict, "hl_eol", BOOLEAN_OBJ(sh_hl.flags & kSHHlEol)); + priority = sh_hl.priority; + } - if (sh_hl.flags & kSHSpellOn) { - PUT(*dict, "spell", BOOLEAN_OBJ(true)); - } else if (sh_hl.flags & kSHSpellOff) { - PUT(*dict, "spell", BOOLEAN_OBJ(false)); - } + if (sh_hl.flags & kSHConceal) { + char buf[MAX_SCHAR_SIZE]; + schar_get(buf, sh_hl.text[0]); + PUT_C(*dict, "conceal", CSTR_TO_ARENA_OBJ(arena, buf)); + } - priority = sh_hl.priority; + if (sh_hl.flags & kSHSpellOn) { + PUT_C(*dict, "spell", BOOLEAN_OBJ(true)); + } else if (sh_hl.flags & kSHSpellOff) { + PUT_C(*dict, "spell", BOOLEAN_OBJ(false)); } if (sh_hl.flags & kSHUIWatched) { - PUT(*dict, "ui_watched", BOOLEAN_OBJ(true)); + PUT_C(*dict, "ui_watched", BOOLEAN_OBJ(true)); + } + + if (sh_hl.url != NULL) { + PUT_C(*dict, "url", STRING_OBJ(cstr_as_string(sh_hl.url))); } if (virt_text) { if (virt_text->hl_mode) { - PUT(*dict, "hl_mode", CSTR_TO_OBJ(hl_mode_str[virt_text->hl_mode])); + PUT_C(*dict, "hl_mode", CSTR_AS_OBJ(hl_mode_str[virt_text->hl_mode])); } - Array chunks = virt_text_to_array(virt_text->data.virt_text, hl_name); - PUT(*dict, "virt_text", ARRAY_OBJ(chunks)); - PUT(*dict, "virt_text_hide", BOOLEAN_OBJ(virt_text->flags & kVTHide)); + Array chunks = virt_text_to_array(virt_text->data.virt_text, hl_name, arena); + PUT_C(*dict, "virt_text", ARRAY_OBJ(chunks)); + PUT_C(*dict, "virt_text_hide", BOOLEAN_OBJ(virt_text->flags & kVTHide)); + PUT_C(*dict, "virt_text_repeat_linebreak", BOOLEAN_OBJ(virt_text->flags & kVTRepeatLinebreak)); if (virt_text->pos == kVPosWinCol) { - PUT(*dict, "virt_text_win_col", INTEGER_OBJ(virt_text->col)); + PUT_C(*dict, "virt_text_win_col", INTEGER_OBJ(virt_text->col)); } - PUT(*dict, "virt_text_pos", - CSTR_TO_OBJ(virt_text_pos_str[virt_text->pos])); + PUT_C(*dict, "virt_text_pos", CSTR_AS_OBJ(virt_text_pos_str[virt_text->pos])); priority = virt_text->priority; } if (virt_lines) { - Array all_chunks = ARRAY_DICT_INIT; + Array all_chunks = arena_array(arena, kv_size(virt_lines->data.virt_lines)); bool virt_lines_leftcol = false; for (size_t i = 0; i < kv_size(virt_lines->data.virt_lines); i++) { virt_lines_leftcol = kv_A(virt_lines->data.virt_lines, i).left_col; - Array chunks = virt_text_to_array(kv_A(virt_lines->data.virt_lines, i).line, hl_name); + Array chunks = virt_text_to_array(kv_A(virt_lines->data.virt_lines, i).line, hl_name, arena); ADD(all_chunks, ARRAY_OBJ(chunks)); } - PUT(*dict, "virt_lines", ARRAY_OBJ(all_chunks)); - PUT(*dict, "virt_lines_above", BOOLEAN_OBJ(virt_lines->flags & kVTLinesAbove)); - PUT(*dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol)); + PUT_C(*dict, "virt_lines", ARRAY_OBJ(all_chunks)); + PUT_C(*dict, "virt_lines_above", BOOLEAN_OBJ(virt_lines->flags & kVTLinesAbove)); + PUT_C(*dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol)); priority = virt_lines->priority; } if (sh_sign.flags & kSHIsSign) { - if (sh_sign.text.ptr) { - PUT(*dict, "sign_text", CSTR_TO_OBJ(sh_sign.text.ptr)); + if (sh_sign.text[0]) { + char buf[SIGN_WIDTH * MAX_SCHAR_SIZE]; + describe_sign_text(buf, sh_sign.text); + PUT_C(*dict, "sign_text", CSTR_TO_ARENA_OBJ(arena, buf)); } if (sh_sign.sign_name) { - PUT(*dict, "sign_name", CSTR_TO_OBJ(sh_sign.sign_name)); + PUT_C(*dict, "sign_name", CSTR_AS_OBJ(sh_sign.sign_name)); } // uncrustify:off @@ -1024,14 +1052,14 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name) for (int j = 0; hls[j].name; j++) { if (hls[j].val) { - PUT(*dict, hls[j].name, hl_group_name(hls[j].val, hl_name)); + PUT_C(*dict, hls[j].name, hl_group_name(hls[j].val, hl_name)); } } priority = sh_sign.priority; } if (priority != -1) { - PUT(*dict, "priority", INTEGER_OBJ(priority)); + PUT_C(*dict, "priority", INTEGER_OBJ(priority)); } } @@ -1059,7 +1087,7 @@ uint16_t decor_type_flags(DecorInline decor) Object hl_group_name(int hl_id, bool hl_name) { if (hl_name) { - return CSTR_TO_OBJ(syn_id2name(hl_id)); + return CSTR_AS_OBJ(syn_id2name(hl_id)); } else { return INTEGER_OBJ(hl_id); } diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index f5448c051b..a63cc7afc0 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -5,18 +5,20 @@ #include <stdint.h> #include "klib/kvec.h" -#include "nvim/buffer_defs.h" -#include "nvim/decoration_defs.h" // IWYU pragma: export +#include "nvim/decoration_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" -#include "nvim/marktree.h" +#include "nvim/marktree_defs.h" #include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/sign_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // actual Decor* data is in decoration_defs.h -EXTERN const char *const virt_text_pos_str[] INIT( = { "eol", "overlay", "win_col", "right_align", - "inline" }); +/// Keep in sync with VirtTextPos in decoration_defs.h +EXTERN const char *const virt_text_pos_str[] +INIT( = { "eol", "overlay", "win_col", "right_align", "inline" }); +/// Keep in sync with HlMode in decoration_defs.h EXTERN const char *const hl_mode_str[] INIT( = { "", "replace", "combine", "blend" }); typedef enum { @@ -43,15 +45,17 @@ typedef struct { VirtTextPos pos; } ui; } data; - int attr_id; // cached lookup of inl.hl_id if it was a highlight - bool owned; // ephemeral decoration, free memory immediately + int attr_id; ///< cached lookup of inl.hl_id if it was a highlight + bool owned; ///< ephemeral decoration, free memory immediately DecorPriority priority; + DecorPriority subpriority; ///< Secondary priority value used for ordering (#27131). + ///< Reflects the order of patterns/captures in the query file. DecorRangeKind kind; /// Screen column to draw the virtual text. - /// When -1, the virtual text may be drawn after deciding where. - /// When -3, the virtual text should be drawn on the next screen line. - /// When -10, the virtual text has just been added. - /// When INT_MIN, the virtual text should no longer be drawn. + /// When -1, it should be drawn on the current screen line after deciding where. + /// When -3, it may be drawn at a position yet to be assigned. + /// When -10, it has just been added. + /// When INT_MIN, it should no longer be drawn. int draw_col; } DecorRange; diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h index dc5d7b9ae4..8d0075b169 100644 --- a/src/nvim/decoration_defs.h +++ b/src/nvim/decoration_defs.h @@ -3,6 +3,7 @@ #include <stdint.h> #include "klib/kvec.h" +#include "nvim/api/private/defs.h" #include "nvim/types_defs.h" #define DECOR_ID_INVALID UINT32_MAX @@ -15,6 +16,7 @@ typedef struct { typedef kvec_t(VirtTextChunk) VirtText; #define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) +/// Keep in sync with virt_text_pos_str[] in decoration.h typedef enum { kVPosEndOfLine, kVPosOverlay, @@ -28,6 +30,7 @@ typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines; typedef uint16_t DecorPriority; #define DECOR_PRIORITY_BASE 0x1000 +/// Keep in sync with hl_mode_str[] in decoration.h typedef enum { kHlModeUnknown, kHlModeReplace, @@ -57,11 +60,7 @@ typedef struct { uint16_t flags; DecorPriority priority; int hl_id; // if sign: highlight of sign text - // TODO(bfredl): Later signs should use sc[2] as well. - union { - char *ptr; // sign - schar_T sc[2]; // conceal text (only sc[0] used) - } text; + schar_T text[SIGN_WIDTH]; // conceal text only uses text[0] // NOTE: if more functionality is added to a Highlight these should be overloaded // or restructured char *sign_name; @@ -70,15 +69,17 @@ typedef struct { int line_hl_id; int cursorline_hl_id; uint32_t next; + const char *url; } DecorSignHighlight; -#define DECOR_SIGN_HIGHLIGHT_INIT { 0, DECOR_PRIORITY_BASE, 0, { .ptr = NULL }, NULL, 0, 0, 0, 0, \ - DECOR_ID_INVALID } +#define DECOR_SIGN_HIGHLIGHT_INIT { 0, DECOR_PRIORITY_BASE, 0, { 0, 0 }, NULL, 0, 0, 0, 0, \ + DECOR_ID_INVALID, NULL } enum { kVTIsLines = 1, kVTHide = 2, kVTLinesAbove = 4, + kVTRepeatLinebreak = 8, }; typedef struct DecorVirtText DecorVirtText; @@ -123,3 +124,26 @@ typedef struct { // initializes in a valid state for the DecorHighlightInline branch #define DECOR_INLINE_INIT { .ext = false, .data.hl = DECOR_HIGHLIGHT_INLINE_INIT } + +typedef struct { + NS ns_id; + + enum { + kDecorProviderActive = 1, + kDecorProviderWinDisabled = 2, + kDecorProviderRedrawDisabled = 3, + kDecorProviderDisabled = 4, + } state; + + LuaRef redraw_start; + LuaRef redraw_buf; + LuaRef redraw_win; + LuaRef redraw_line; + LuaRef redraw_end; + LuaRef hl_def; + LuaRef spell_nav; + int hl_valid; + bool hl_cached; + + uint8_t error_count; +} DecorProvider; diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c index 172eb569c9..2417c14f7f 100644 --- a/src/nvim/decoration_provider.c +++ b/src/nvim/decoration_provider.c @@ -1,5 +1,6 @@ #include <assert.h> #include <lauxlib.h> +#include <stdint.h> #include <string.h> #include "klib/kvec.h" @@ -8,18 +9,26 @@ #include "nvim/api/private/helpers.h" #include "nvim/buffer_defs.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/decoration_provider.h" #include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/log.h" #include "nvim/lua/executor.h" #include "nvim/message.h" +#include "nvim/move.h" #include "nvim/pos_defs.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "decoration_provider.c.generated.h" +#endif + +enum { DP_MAX_ERROR = 3, }; + static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE; #define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \ - { ns_id, false, LUA_NOREF, LUA_NOREF, \ + { ns_id, kDecorProviderDisabled, LUA_NOREF, LUA_NOREF, \ LUA_NOREF, LUA_NOREF, LUA_NOREF, \ LUA_NOREF, -1, false, false, 0 } @@ -30,17 +39,23 @@ static void decor_provider_error(DecorProvider *provider, const char *name, cons msg_schedule_semsg_multiline("Error in decoration provider %s.%s:\n%s", ns_name, name, msg); } -static bool decor_provider_invoke(DecorProvider *provider, const char *name, LuaRef ref, Array args, +// Note we pass in a provider index as this function may cause decor_providers providers to be +// reallocated so we need to be careful with DecorProvider pointers +static bool decor_provider_invoke(int provider_idx, const char *name, LuaRef ref, Array args, bool default_true) { Error err = ERROR_INIT; textlock++; provider_active = true; - Object ret = nlua_call_ref(ref, name, args, true, &err); + Object ret = nlua_call_ref(ref, name, args, kRetNilBool, NULL, &err); provider_active = false; textlock--; + // We get the provider here via an index in case the above call to nlua_call_ref causes + // decor_providers to be reallocated. + DecorProvider *provider = &kv_A(decor_providers, provider_idx); + if (!ERROR_SET(&err) && api_object_to_bool(ret, "provider %s retval", default_true, &err)) { provider->error_count = 0; @@ -52,7 +67,7 @@ static bool decor_provider_invoke(DecorProvider *provider, const char *name, Lua provider->error_count++; if (provider->error_count >= DP_MAX_ERROR) { - provider->active = false; + provider->state = kDecorProviderDisabled; } } @@ -65,11 +80,7 @@ void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int e { for (size_t i = 0; i < kv_size(decor_providers); i++) { DecorProvider *p = &kv_A(decor_providers, i); - if (!p->active) { - continue; - } - - if (p->spell_nav != LUA_NOREF) { + if (p->state != kDecorProviderDisabled && p->spell_nav != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 6); ADD_C(args, INTEGER_OBJ(wp->handle)); ADD_C(args, INTEGER_OBJ(wp->w_buffer->handle)); @@ -77,7 +88,7 @@ void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int e ADD_C(args, INTEGER_OBJ(start_col)); ADD_C(args, INTEGER_OBJ(end_row)); ADD_C(args, INTEGER_OBJ(end_col)); - decor_provider_invoke(p, "spell", p->spell_nav, args, true); + decor_provider_invoke((int)i, "spell", p->spell_nav, args, true); } } } @@ -86,27 +97,15 @@ void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int e /// /// @param[out] providers Decoration providers /// @param[out] err Provider err -void decor_providers_start(DecorProviders *providers) +void decor_providers_start(void) { - kvi_init(*providers); - for (size_t i = 0; i < kv_size(decor_providers); i++) { DecorProvider *p = &kv_A(decor_providers, i); - if (!p->active) { - continue; - } - - bool active; - if (p->redraw_start != LUA_NOREF) { + if (p->state != kDecorProviderDisabled && p->redraw_start != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 2); ADD_C(args, INTEGER_OBJ((int)display_tick)); - active = decor_provider_invoke(p, "start", p->redraw_start, args, true); - } else { - active = true; - } - - if (active) { - kvi_push(*providers, p); + bool active = decor_provider_invoke((int)i, "start", p->redraw_start, args, true); + kv_A(decor_providers, i).state = active ? kDecorProviderActive : kDecorProviderRedrawDisabled; } } } @@ -118,30 +117,32 @@ void decor_providers_start(DecorProviders *providers) /// @param providers Decoration providers /// @param[out] line_providers Enabled line providers to invoke in win_line /// @param[out] err Provider error -void decor_providers_invoke_win(win_T *wp, DecorProviders *providers, - DecorProviders *line_providers) +void decor_providers_invoke_win(win_T *wp) { - kvi_init(*line_providers); // this might change in the future // then we would need decor_state.running_decor_provider just like "on_line" below assert(kv_size(decor_state.active) == 0); - linenr_T knownmax = MIN(wp->w_buffer->b_ml.ml_line_count, - ((wp->w_valid & VALID_BOTLINE) - ? wp->w_botline - : MAX(wp->w_topline + wp->w_height_inner, wp->w_botline))); + if (kv_size(decor_providers) > 0) { + validate_botline(wp); + } + linenr_T botline = MIN(wp->w_botline, wp->w_buffer->b_ml.ml_line_count); - for (size_t k = 0; k < kv_size(*providers); k++) { - DecorProvider *p = kv_A(*providers, k); - if (p && p->redraw_win != LUA_NOREF) { + for (size_t i = 0; i < kv_size(decor_providers); i++) { + DecorProvider *p = &kv_A(decor_providers, i); + if (p->state == kDecorProviderWinDisabled) { + p->state = kDecorProviderActive; + } + + if (p->state == kDecorProviderActive && p->redraw_win != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 4); ADD_C(args, WINDOW_OBJ(wp->handle)); ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle)); // TODO(bfredl): we are not using this, but should be first drawn line? ADD_C(args, INTEGER_OBJ(wp->w_topline - 1)); - ADD_C(args, INTEGER_OBJ(knownmax - 1)); - if (decor_provider_invoke(p, "win", p->redraw_win, args, true)) { - kvi_push(*line_providers, p); + ADD_C(args, INTEGER_OBJ(botline - 1)); + if (!decor_provider_invoke((int)i, "win", p->redraw_win, args, true)) { + kv_A(decor_providers, i).state = kDecorProviderWinDisabled; } } } @@ -154,21 +155,21 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers, /// @param row Row to invoke line callback for /// @param[out] has_decor Set when at least one provider invokes a line callback /// @param[out] err Provider error -void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *has_decor) +void decor_providers_invoke_line(win_T *wp, int row, bool *has_decor) { decor_state.running_decor_provider = true; - for (size_t k = 0; k < kv_size(*providers); k++) { - DecorProvider *p = kv_A(*providers, k); - if (p && p->redraw_line != LUA_NOREF) { + for (size_t i = 0; i < kv_size(decor_providers); i++) { + DecorProvider *p = &kv_A(decor_providers, i); + if (p->state == kDecorProviderActive && p->redraw_line != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, WINDOW_OBJ(wp->handle)); ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle)); ADD_C(args, INTEGER_OBJ(row)); - if (decor_provider_invoke(p, "line", p->redraw_line, args, true)) { + if (decor_provider_invoke((int)i, "line", p->redraw_line, args, true)) { *has_decor = true; } else { // return 'false' or error: skip rest of this window - kv_A(*providers, k) = NULL; + kv_A(decor_providers, i).state = kDecorProviderWinDisabled; } hl_check_ns(); @@ -182,15 +183,15 @@ void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row, /// @param buf Buffer /// @param providers Decoration providers /// @param[out] err Provider error -void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers) +void decor_providers_invoke_buf(buf_T *buf) { - for (size_t i = 0; i < kv_size(*providers); i++) { - DecorProvider *p = kv_A(*providers, i); - if (p && p->redraw_buf != LUA_NOREF) { + for (size_t i = 0; i < kv_size(decor_providers); i++) { + DecorProvider *p = &kv_A(decor_providers, i); + if (p->state == kDecorProviderActive && p->redraw_buf != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 2); ADD_C(args, BUFFER_OBJ(buf->handle)); ADD_C(args, INTEGER_OBJ((int64_t)display_tick)); - decor_provider_invoke(p, "buf", p->redraw_buf, args, true); + decor_provider_invoke((int)i, "buf", p->redraw_buf, args, true); } } } @@ -200,14 +201,15 @@ void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers) /// @param providers Decoration providers /// @param displaytick Display tick /// @param[out] err Provider error -void decor_providers_invoke_end(DecorProviders *providers) +void decor_providers_invoke_end(void) { - for (size_t i = 0; i < kv_size(*providers); i++) { - DecorProvider *p = kv_A(*providers, i); - if (p && p->active && p->redraw_end != LUA_NOREF) { + for (size_t i = 0; i < kv_size(decor_providers); i++) { + DecorProvider *p = &kv_A(decor_providers, i); + if (p->state != kDecorProviderDisabled && p->redraw_end != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 1); ADD_C(args, INTEGER_OBJ((int)display_tick)); - decor_provider_invoke(p, "end", p->redraw_end, args, true); + decor_provider_invoke((int)i, "end", p->redraw_end, args, true); + kv_A(decor_providers, i).state = kDecorProviderActive; } } decor_check_to_be_deleted(); @@ -220,10 +222,8 @@ void decor_providers_invoke_end(DecorProviders *providers) /// like highlight_changed() (throttled to the next redraw or mode change) void decor_provider_invalidate_hl(void) { - size_t len = kv_size(decor_providers); - for (size_t i = 0; i < len; i++) { - DecorProvider *item = &kv_A(decor_providers, i); - item->hl_cached = false; + for (size_t i = 0; i < kv_size(decor_providers); i++) { + kv_A(decor_providers, i).hl_cached = false; } if (ns_hl_active) { @@ -235,14 +235,11 @@ void decor_provider_invalidate_hl(void) DecorProvider *get_decor_provider(NS ns_id, bool force) { assert(ns_id > 0); - size_t i; size_t len = kv_size(decor_providers); - for (i = 0; i < len; i++) { - DecorProvider *item = &kv_A(decor_providers, i); - if (item->ns_id == ns_id) { - return item; - } else if (item->ns_id > ns_id) { - break; + for (size_t i = 0; i < len; i++) { + DecorProvider *p = &kv_A(decor_providers, i); + if (p->ns_id == ns_id) { + return p; } } @@ -250,16 +247,7 @@ DecorProvider *get_decor_provider(NS ns_id, bool force) return NULL; } - // Adding a new provider, so allocate room in the vector - (void)kv_a(decor_providers, len); - if (i < len) { - // New ns_id needs to be inserted between existing providers to maintain - // ordering, so shift other providers with larger ns_id - memmove(&kv_A(decor_providers, i + 1), - &kv_A(decor_providers, i), - (len - i) * sizeof(kv_a(decor_providers, i))); - } - DecorProvider *item = &kv_a(decor_providers, i); + DecorProvider *item = &kv_a(decor_providers, len); *item = DECORATION_PROVIDER_INIT(ns_id); return item; @@ -276,7 +264,7 @@ void decor_provider_clear(DecorProvider *p) NLUA_CLEAR_REF(p->redraw_line); NLUA_CLEAR_REF(p->redraw_end); NLUA_CLEAR_REF(p->spell_nav); - p->active = false; + p->state = kDecorProviderDisabled; } void decor_free_all_mem(void) diff --git a/src/nvim/decoration_provider.h b/src/nvim/decoration_provider.h index e0dd67a6f7..ad6fb7ac19 100644 --- a/src/nvim/decoration_provider.h +++ b/src/nvim/decoration_provider.h @@ -1,32 +1,10 @@ #pragma once #include <stdbool.h> -#include <stdint.h> -#include "klib/kvec.h" -#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include "nvim/decoration_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" -#include "nvim/types_defs.h" - -#define DP_MAX_ERROR 3 - -typedef struct { - NS ns_id; - bool active; - LuaRef redraw_start; - LuaRef redraw_buf; - LuaRef redraw_win; - LuaRef redraw_line; - LuaRef redraw_end; - LuaRef hl_def; - LuaRef spell_nav; - int hl_valid; - bool hl_cached; - - uint8_t error_count; -} DecorProvider; - -typedef kvec_withinit_t(DecorProvider *, 4) DecorProviders; +#include "nvim/types_defs.h" // IWYU pragma: keep EXTERN bool provider_active INIT( = false); diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 0b7f6f266b..2b3010e063 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -18,6 +18,7 @@ #include "auto/config.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/bufwrite.h" #include "nvim/change.h" @@ -30,25 +31,30 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/linematch.h" #include "nvim/mark.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/shell.h" #include "nvim/path.h" #include "nvim/pos_defs.h" @@ -60,7 +66,7 @@ #include "nvim/window.h" #include "xdiff/xdiff.h" -static int diff_busy = false; // using diff structs, don't change them +static bool diff_busy = false; // using diff structs, don't change them static bool diff_need_update = false; // ex_diffupdate needs to be called // Flags obtained from the 'diffopt' option @@ -385,7 +391,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T } dp->df_lnum[idx] += amount_after; } else { - int check_unchanged = false; + bool check_unchanged = false; // 2. 3. 4. 5.: inserted/deleted lines touching this diff. if (deleted > 0) { @@ -1004,7 +1010,7 @@ theend: static int check_external_diff(diffio_T *diffio) { // May try twice, first with "-a" and then without. - int io_error = false; + bool io_error = false; TriState ok = kFalse; while (true) { ok = kFalse; @@ -1013,7 +1019,7 @@ static int check_external_diff(diffio_T *diffio) if (fd == NULL) { io_error = true; } else { - if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1) { + if (fwrite("line1\n", 6, 1, fd) != 1) { io_error = true; } fclose(fd); @@ -1022,7 +1028,7 @@ static int check_external_diff(diffio_T *diffio) if (fd == NULL) { io_error = true; } else { - if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1) { + if (fwrite("line2\n", 6, 1, fd) != 1) { io_error = true; } fclose(fd); @@ -1167,9 +1173,9 @@ static int diff_file(diffio_T *dio) tmp_orig, tmp_new); append_redir(cmd, len, p_srr, tmp_diff); block_autocmds(); // Avoid ShellCmdPost stuff - (void)call_shell(cmd, - kShellOptFilter | kShellOptSilent | kShellOptDoOut, - NULL); + call_shell(cmd, + kShellOptFilter | kShellOptSilent | kShellOptDoOut, + NULL); unblock_autocmds(); xfree(cmd); return OK; @@ -1251,7 +1257,7 @@ void ex_diffpatch(exarg_T *eap) vim_snprintf(buf, buflen, "patch -o %s %s < %s", tmp_new, tmp_orig, esc_name); block_autocmds(); // Avoid ShellCmdPost stuff - (void)call_shell(buf, kShellOptFilter, NULL); + call_shell(buf, kShellOptFilter, NULL); unblock_autocmds(); } @@ -1390,7 +1396,7 @@ static void set_diff_option(win_T *wp, bool value) curwin = wp; curbuf = curwin->w_buffer; curbuf->b_ro_locked++; - set_option_value_give_err("diff", BOOLEAN_OPTVAL(value), OPT_LOCAL); + set_option_value_give_err(kOptDiff, BOOLEAN_OPTVAL(value), OPT_LOCAL); curbuf->b_ro_locked--; curwin = old_curwin; curbuf = curwin->w_buffer; @@ -1399,7 +1405,7 @@ static void set_diff_option(win_T *wp, bool value) /// Set options in window "wp" for diff mode. /// /// @param addbuf Add buffer to diff. -void diff_win_options(win_T *wp, int addbuf) +void diff_win_options(win_T *wp, bool addbuf) { win_T *old_curwin = curwin; @@ -1431,7 +1437,7 @@ void diff_win_options(win_T *wp, int addbuf) } wp->w_p_fdm_save = xstrdup(wp->w_p_fdm); } - set_string_option_direct_in_win(wp, "fdm", -1, "diff", OPT_LOCAL | OPT_FREE, 0); + set_string_option_direct_in_win(wp, kOptFoldmethod, "diff", OPT_LOCAL, 0); if (!wp->w_p_diff) { wp->w_p_fen_save = wp->w_p_fen; @@ -1473,7 +1479,7 @@ void diff_win_options(win_T *wp, int addbuf) /// @param eap void ex_diffoff(exarg_T *eap) { - int diffwin = false; + bool diffwin = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (eap->forceit ? wp->w_p_diff : (wp == curwin)) { @@ -1889,7 +1895,7 @@ static void count_filler_lines_and_topline(int *curlinenum_to, int *linesfiller, { const diff_T *curdif = thistopdiff; int ch_virtual_lines = 0; - int isfiller = 0; + bool isfiller = false; while (virtual_lines_passed > 0) { if (ch_virtual_lines) { virtual_lines_passed--; @@ -1902,7 +1908,7 @@ static void count_filler_lines_and_topline(int *curlinenum_to, int *linesfiller, } else { (*linesfiller) = 0; ch_virtual_lines = get_max_diff_length(curdif); - isfiller = (curdif->df_count[toidx] ? 0 : 1); + isfiller = (curdif->df_count[toidx] ? false : true); if (isfiller) { while (curdif && curdif->df_next && curdif->df_lnum[toidx] == curdif->df_next->df_lnum[toidx] @@ -2156,12 +2162,12 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus) } if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) { - int zero = false; + bool zero = false; // Changed or inserted line. If the other buffers have a count of // zero, the lines were inserted. If the other buffers have the same // count, check if the lines are identical. - int cmp = false; + bool cmp = false; for (int i = 0; i < DB_COUNT; i++) { if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) { @@ -2196,7 +2202,7 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus) // the difference. Can't remove the entry here, we might be halfway // through updating the window. Just report the text as unchanged. // Other windows might still show the change though. - if (zero == false) { + if (!zero) { return 0; } return -2; @@ -2445,8 +2451,8 @@ void diff_set_topline(win_T *fromwin, win_T *towin) changed_line_abv_curs_win(towin); check_topfill(towin, false); - (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline, - NULL, true, NULL); + hasFoldingWin(towin, towin->w_topline, &towin->w_topline, + NULL, true, NULL); } /// This is called when 'diffopt' is changed. @@ -2846,7 +2852,7 @@ void ex_diffgetput(exarg_T *eap) } if (*eap->arg == NUL) { - int found_not_ma = false; + bool found_not_ma = false; // No argument: Find the other buffer in the list of diff buffers. for (idx_other = 0; idx_other < DB_COUNT; idx_other++) { if ((curtab->tp_diffbuf[idx_other] != curbuf) diff --git a/src/nvim/diff.h b/src/nvim/diff.h index 8b58887890..fd897498df 100644 --- a/src/nvim/diff.h +++ b/src/nvim/diff.h @@ -2,16 +2,17 @@ #include <stdbool.h> -#include "nvim/ex_cmds_defs.h" +#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" -#include "nvim/pos_defs.h" +#include "nvim/pos_defs.h" // IWYU pragma: keep // Value set from 'diffopt'. -EXTERN int diff_context INIT( = 6); // context for folds -EXTERN int diff_foldcolumn INIT( = 2); // 'foldcolumn' for diff mode +EXTERN int diff_context INIT( = 6); ///< context for folds +EXTERN int diff_foldcolumn INIT( = 2); ///< 'foldcolumn' for diff mode EXTERN bool diff_need_scrollbind INIT( = false); -EXTERN bool need_diff_redraw INIT( = false); // need to call diff_redraw() +EXTERN bool need_diff_redraw INIT( = false); ///< need to call diff_redraw() #ifdef INCLUDE_GENERATED_DECLARATIONS # include "diff.h.generated.h" diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 99d5cf1035..f91ff9274b 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -17,15 +17,16 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/keycodes.h" #include "nvim/mapping.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/normal.h" @@ -39,7 +40,7 @@ typedef int result_T; -typedef struct digraph { +typedef struct { uint8_t char1; uint8_t char2; result_T result; @@ -2043,13 +2044,10 @@ char *keymap_init(void) keymap_unload(); do_cmdline_cmd("unlet! b:keymap_name"); } else { - char *buf; - size_t buflen; - // Source the keymap file. It will contain a ":loadkeymap" command // which will call ex_loadkeymap() below. - buflen = strlen(curbuf->b_p_keymap) + strlen(p_enc) + 14; - buf = xmalloc(buflen); + size_t buflen = strlen(curbuf->b_p_keymap) + strlen(p_enc) + 14; + char *buf = xmalloc(buflen); // try finding "keymap/'keymap'_'encoding'.vim" in 'runtimepath' vim_snprintf(buf, buflen, "keymap/%s_%s.vim", @@ -2131,7 +2129,7 @@ void ex_loadkeymap(exarg_T *eap) vim_snprintf(buf, sizeof(buf), "<buffer> %s %s", ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].from, ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].to); - (void)do_map(MAPTYPE_MAP, buf, MODE_LANGMAP, false); + do_map(MAPTYPE_MAP, buf, MODE_LANGMAP, false); } p_cpo = save_cpo; @@ -2168,7 +2166,7 @@ static void keymap_unload(void) for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) { vim_snprintf(buf, sizeof(buf), "<buffer> %s", kp[i].from); - (void)do_map(MAPTYPE_UNMAP, buf, MODE_LANGMAP, false); + do_map(MAPTYPE_UNMAP, buf, MODE_LANGMAP, false); } keymap_ga_clear(&curbuf->b_kmap_ga); @@ -2194,13 +2192,12 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len) buf_T *old_curbuf = curbuf; win_T *old_curwin = curwin; - char *s; curbuf = wp->w_buffer; curwin = wp; STRCPY(buf, "b:keymap_name"); // must be writable emsg_skip++; - s = p = eval_to_string(buf, false); + char *s = p = eval_to_string(buf, false); emsg_skip--; curbuf = old_curbuf; curwin = old_curwin; diff --git a/src/nvim/digraph.h b/src/nvim/digraph.h index 267004124b..a3782cdd58 100644 --- a/src/nvim/digraph.h +++ b/src/nvim/digraph.h @@ -1,6 +1,5 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/garray_defs.h" // IWYU pragma: keep diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index e0887ed1d0..4281cdff33 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -11,10 +11,12 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/cursor_shape.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/drawline.h" @@ -24,59 +26,53 @@ #include "nvim/fold_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" -#include "nvim/mark.h" +#include "nvim/mark_defs.h" +#include "nvim/marktree_defs.h" #include "nvim/match.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/option_vars.h" +#include "nvim/os/os_defs.h" #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/quickfix.h" -#include "nvim/sign.h" +#include "nvim/sign_defs.h" #include "nvim/spell.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/statusline.h" +#include "nvim/statusline_defs.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/terminal.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/vim_defs.h" #define MB_FILLER_CHAR '<' // character used when a double-width character doesn't fit. -/// possible draw states in win_line(), drawn in sequence. -typedef enum { - WL_START = 0, // nothing done yet - WL_CMDLINE, // cmdline window column - WL_FOLD, // 'foldcolumn' - WL_SIGN, // column for signs - WL_NR, // line number - WL_STC, // 'statuscolumn' - WL_BRI, // 'breakindent' - WL_SBR, // 'showbreak' or 'diff' - WL_LINE, // text in the line -} LineDrawState; - /// structure with variables passed between win_line() and other functions typedef struct { - LineDrawState draw_state; ///< what to draw next - - linenr_T lnum; ///< line number to be drawn - foldinfo_T foldinfo; ///< fold info for this line + const linenr_T lnum; ///< line number to be drawn + const foldinfo_T foldinfo; ///< fold info for this line - int startrow; ///< first row in the window to be drawn + const int startrow; ///< first row in the window to be drawn int row; ///< row in the window, excl w_winrow colnr_T vcol; ///< virtual column, before wrapping int col; ///< visual column on screen, after wrapping int boguscols; ///< nonexistent columns added to "col" to force wrapping + int old_boguscols; ///< bogus boguscols int vcol_off; ///< offset for concealed characters int off; ///< offset relative start of line @@ -96,24 +92,14 @@ typedef struct { int n_extra; ///< number of extra bytes int n_attr; ///< chars with special attr char *p_extra; ///< string of extra chars, plus NUL, only used - ///< when c_extra and c_final are NUL + ///< when sc_extra and sc_final are NUL int extra_attr; ///< attributes for p_extra - int c_extra; ///< extra chars, all the same - int c_final; ///< final char, mandatory if set - - int n_closing; ///< number of chars in fdc which will be closing + schar_T sc_extra; ///< extra chars, all the same + schar_T sc_final; ///< final char, mandatory if set bool extra_for_extmark; ///< n_extra set for inline virtual text - // saved "extra" items for when draw_state becomes WL_LINE (again) - int saved_n_extra; - char *saved_p_extra; - bool saved_extra_for_extmark; - int saved_c_extra; - int saved_c_final; - int saved_char_attr; - - char extra[57]; ///< sign, line number and 'fdc' must fit in here + char extra[11]; ///< must be as large as transchar_charbuf[] in charset.c hlf_T diff_hlf; ///< type of diff highlighting @@ -135,6 +121,8 @@ typedef struct { ///< or w_skipcol or concealing int skipped_cells; ///< nr of skipped cells for virtual text ///< to be added to wlv.vcol later + + int *color_cols; ///< if not NULL, highlight colorcolumn using according columns array } winlinevars_T; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -162,15 +150,17 @@ void drawline_free_all_mem(void) } #endif -/// Advance **color_cols -/// -/// @return true when there are columns to draw. -static bool advance_color_col(int vcol, int **color_cols) +/// Advance wlv->color_cols if not NULL +static void advance_color_col(winlinevars_T *wlv, int vcol) { - while (**color_cols >= 0 && vcol > **color_cols) { - (*color_cols)++; + if (wlv->color_cols) { + while (*wlv->color_cols >= 0 && vcol > *wlv->color_cols) { + wlv->color_cols++; + } + if (*wlv->color_cols < 0) { + wlv->color_cols = NULL; + } } - return **color_cols >= 0; } /// Used when 'cursorlineopt' contains "screenline": compute the margins between @@ -224,6 +214,9 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col) /// Handles composing chars static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells, int vcol) { + // Caller should handle overwriting the right half of a double-width char. + assert(dest[0] != 0); + const char *p = *pp; int cells = utf_ptr2cells(p); int c_len = utfc_ptr2len(p); @@ -237,6 +230,7 @@ static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells); } + // When overwriting the left half of a double-width char, clear the right half. if (cells < maxcells && dest[cells] == 0) { dest[cells] = schar_from_ascii(' '); } @@ -305,10 +299,12 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int int vcol = item->draw_col - col_off; col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text, vt->hl_mode, max_col, vcol); + if (vt->pos == kVPosEndOfLine && do_eol) { + state->eol_col = col + 1; + } } - item->draw_col = INT_MIN; // deactivate - if (vt && vt->pos == kVPosEndOfLine && do_eol) { - state->eol_col = col + 1; + if (!vt || !(vt->flags & kVTRepeatLinebreak)) { + item->draw_col = INT_MIN; // deactivate } *end_col = MAX(*end_col, col); @@ -346,8 +342,15 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, } else { attr = virt_attr; } - schar_T dummy[2]; + schar_T dummy[2] = { schar_from_ascii(' '), schar_from_ascii(' ') }; int maxcells = max_col - col; + // When overwriting the right half of a double-width char, clear the left half. + if (!through && linebuf_char[col] == 0) { + assert(col > 0); + linebuf_char[col - 1] = schar_from_ascii(' '); + // Clear the right half as well for the assertion in line_putchar(). + linebuf_char[col] = schar_from_ascii(' '); + } int cells = line_putchar(buf, &p, through ? dummy : &linebuf_char[col], maxcells, vcol); for (int c = 0; c < cells; c++) { @@ -359,6 +362,38 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, return col; } +// TODO(bfredl): integrate with grid.c linebuf code? madness? +static void draw_col_buf(win_T *wp, winlinevars_T *wlv, const char *text, size_t len, int attr, + bool vcol) +{ + const char *ptr = text; + while (ptr < text + len && wlv->off < wp->w_grid.cols) { + int cells = line_putchar(wp->w_buffer, &ptr, &linebuf_char[wlv->off], + wp->w_grid.cols - wlv->off, wlv->off); + int myattr = attr; + if (vcol) { + advance_color_col(wlv, wlv->vcol); + if (wlv->color_cols && wlv->vcol == *wlv->color_cols) { + myattr = hl_combine_attr(win_hl_attr(wp, HLF_MC), myattr); + } + } + for (int c = 0; c < cells; c++) { + linebuf_attr[wlv->off] = myattr; + linebuf_vcol[wlv->off] = vcol ? wlv->vcol++ : -1; + wlv->off++; + } + } +} + +static void draw_col_fill(winlinevars_T *wlv, schar_T fillchar, int width, int attr) +{ + for (int i = 0; i < width; i++) { + linebuf_char[wlv->off] = fillchar; + linebuf_attr[wlv->off] = attr; + wlv->off++; + } +} + /// Return true if CursorLineSign highlight is to be used. static bool use_cursor_line_highlight(win_T *wp, linenr_T lnum) { @@ -367,129 +402,78 @@ static bool use_cursor_line_highlight(win_T *wp, linenr_T lnum) && (wp->w_p_culopt_flags & CULOPT_NBR); } -static char fdc_buf[MB_MAXCHAR * 10 + 1]; - /// Setup for drawing the 'foldcolumn', if there is one. -static void handle_foldcolumn(win_T *wp, winlinevars_T *wlv) +static void draw_foldcolumn(win_T *wp, winlinevars_T *wlv) { int fdc = compute_foldcolumn(wp, 0); - if (fdc <= 0) { - return; - } - - // Use a separate buffer as `extra_buf` might be in use. - wlv->n_extra = (int)fill_foldcolumn(fdc_buf, wp, wlv->foldinfo, wlv->lnum, - &wlv->n_closing); - fdc_buf[wlv->n_extra] = NUL; - wlv->p_extra = fdc_buf; - wlv->c_extra = NUL; - wlv->c_final = NUL; - if (use_cursor_line_highlight(wp, wlv->lnum)) { - wlv->char_attr = win_hl_attr(wp, HLF_CLF); - } else { - wlv->char_attr = win_hl_attr(wp, HLF_FC); + if (fdc > 0) { + int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLF : HLF_FC); + fill_foldcolumn(wp, wlv->foldinfo, wlv->lnum, attr, fdc, &wlv->off, NULL); } } -/// Fills the foldcolumn at "p" for window "wp". -/// Only to be called when 'foldcolumn' > 0. -/// -/// @param[out] p Char array to write into -/// @param lnum Absolute current line number -/// @param closed Whether it is in 'foldcolumn' mode +/// Draw the foldcolumn or fill "out_buffer". Assume monocell characters. /// -/// Assume monocell characters -/// @return number of chars added to \param p -size_t fill_foldcolumn(char *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int *n_closing) +/// @param fdc Current width of the foldcolumn +/// @param[out] wlv_off Pointer to linebuf offset, incremented for default column +/// @param[out] out_buffer Char array to fill, only used for 'statuscolumn' +void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, int fdc, int *wlv_off, + schar_T *out_buffer) { - int i = 0; - int fdc = compute_foldcolumn(wp, 0); // available cell width - size_t char_counter = 0; - int symbol = 0; - int len = 0; bool closed = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; - // Init to all spaces. - memset(p, ' ', MB_MAXCHAR * (size_t)fdc + 1); - int level = foldinfo.fi_level; // If the column is too narrow, we start at the lowest level that // fits and use numbers to indicate the depth. - int first_level = level - fdc - closed + 1; - if (first_level < 1) { - first_level = 1; - } - - for (i = 0; i < MIN(fdc, level); i++) { - if (foldinfo.fi_lnum == lnum - && first_level + i >= foldinfo.fi_low_level) { + int first_level = MAX(level - fdc - closed + 1, 1); + int closedcol = MIN(fdc, level); + + for (int i = 0; i < fdc; i++) { + schar_T symbol = 0; + if (i >= level) { + symbol = schar_from_ascii(' '); + } else if (i == closedcol - 1 && closed) { + symbol = wp->w_p_fcs_chars.foldclosed; + } else if (foldinfo.fi_lnum == lnum && first_level + i >= foldinfo.fi_low_level) { symbol = wp->w_p_fcs_chars.foldopen; } else if (first_level == 1) { symbol = wp->w_p_fcs_chars.foldsep; } else if (first_level + i <= 9) { - symbol = '0' + first_level + i; + symbol = schar_from_ascii('0' + first_level + i); } else { - symbol = '>'; - } - - len = utf_char2bytes(symbol, &p[char_counter]); - char_counter += (size_t)len; - if (first_level + i >= level) { - i++; - break; + symbol = schar_from_ascii('>'); } - } - int n_closing_val = i; - - if (closed) { - if (symbol != 0) { - // rollback previous write - char_counter -= (size_t)len; - memset(&p[char_counter], ' ', (size_t)len); - n_closing_val--; + if (out_buffer) { + out_buffer[i] = symbol; + } else { + linebuf_vcol[*wlv_off] = i >= level ? -1 : (i == closedcol - 1 && closed) ? -2 : -3; + linebuf_attr[*wlv_off] = attr; + linebuf_char[(*wlv_off)++] = symbol; } - len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, &p[char_counter]); - char_counter += (size_t)len; - } - - if (n_closing) { - *n_closing = n_closing_val; } - - return MAX(char_counter + (size_t)(fdc - i), (size_t)fdc); } /// Get information needed to display the sign in line "wlv->lnum" in window "wp". /// If "nrcol" is true, the sign is going to be displayed in the number column. /// Otherwise the sign is going to be displayed in the sign column. If there is no /// sign, draw blank cells instead. -static void get_sign_display_info(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, - int sign_cul_attr) +static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, int sign_cul_attr) { SignTextAttrs sattr = wlv->sattrs[sign_idx]; - wlv->c_final = NUL; - - if (sattr.text && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) { - size_t fill = nrcol ? (size_t)number_width(wp) - SIGN_WIDTH : 0; - size_t sign_len = strlen(sattr.text); - - // Spaces + sign: " " + ">>" + ' ' - wlv->n_extra = (int)(fill + sign_len + nrcol); - if (nrcol) { - memset(wlv->extra, ' ', (size_t)wlv->n_extra); - } - memcpy(wlv->extra + fill, sattr.text, sign_len); - wlv->p_extra = wlv->extra; - wlv->c_extra = NUL; - wlv->char_attr = (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr) - ? sign_cul_attr : sattr.hl_id ? syn_id2attr(sattr.hl_id) : 0; + + if (sattr.text[0] && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) { + int attr = (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr) + ? sign_cul_attr : sattr.hl_id ? syn_id2attr(sattr.hl_id) : 0; + int fill = nrcol ? number_width(wp) + 1 : SIGN_WIDTH; + draw_col_fill(wlv, schar_from_ascii(' '), fill, attr); + int sign_pos = wlv->off - SIGN_WIDTH - (int)nrcol; + linebuf_char[sign_pos] = sattr.text[0]; + linebuf_char[sign_pos + 1] = sattr.text[1]; } else { - wlv->c_extra = ' '; - wlv->n_extra = nrcol ? number_width(wp) + 1 : SIGN_WIDTH; - if (!nrcol) { - wlv->char_attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC); - } + assert(!nrcol); // handled in draw_lnum_col() + int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC); + draw_col_fill(wlv, schar_from_ascii(' '), SIGN_WIDTH, attr); } } @@ -556,7 +540,7 @@ static int get_line_number_attr(win_T *wp, winlinevars_T *wlv) /// Display the absolute or relative line number. After the first row fill with /// blanks when the 'n' flag isn't in 'cpo'. -static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int sign_cul_attr) +static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int sign_cul_attr) { bool has_cpo_n = vim_strchr(p_cpo, CPO_NUMCOL) != NULL; @@ -567,221 +551,186 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, in && !((has_cpo_n && !wp->w_p_bri) && wp->w_skipcol > 0 && wlv->lnum == wp->w_topline)) { // If 'signcolumn' is set to 'number' and a sign is present in "lnum", // then display the sign instead of the line number. - if (wp->w_minscwidth == SCL_NUM && wlv->sattrs[0].text) { - get_sign_display_info(true, wp, wlv, 0, sign_cul_attr); + if (wp->w_minscwidth == SCL_NUM && wlv->sattrs[0].text[0] + && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) { + draw_sign(true, wp, wlv, 0, sign_cul_attr); } else { // Draw the line number (empty space after wrapping). + int width = number_width(wp) + 1; + int attr = (sign_num_attr > 0 && wlv->filler_todo <= 0) + ? sign_num_attr : get_line_number_attr(wp, wlv); if (wlv->row == wlv->startrow + wlv->filler_lines && (wp->w_skipcol == 0 || wlv->row > 0 || (wp->w_p_nu && wp->w_p_rnu))) { - get_line_number_str(wp, wlv->lnum, wlv->extra, sizeof(wlv->extra)); + char buf[32]; + get_line_number_str(wp, wlv->lnum, buf, sizeof(buf)); if (wp->w_skipcol > 0 && wlv->startrow == 0) { - for (wlv->p_extra = wlv->extra; *wlv->p_extra == ' '; wlv->p_extra++) { - *wlv->p_extra = '-'; + for (char *c = buf; *c == ' '; c++) { + *c = '-'; } } if (wp->w_p_rl) { // reverse line numbers - char *num = skipwhite(wlv->extra); + char *num = skipwhite(buf); rl_mirror_ascii(num, skiptowhite(num)); } - wlv->p_extra = wlv->extra; - wlv->c_extra = NUL; - } else { - wlv->c_extra = ' '; - } - wlv->c_final = NUL; - wlv->n_extra = number_width(wp) + 1; - if (sign_num_attr > 0) { - wlv->char_attr = sign_num_attr; + draw_col_buf(wp, wlv, buf, (size_t)width, attr, false); } else { - wlv->char_attr = get_line_number_attr(wp, wlv); + draw_col_fill(wlv, schar_from_ascii(' '), width, attr); } } } } -/// Prepare and build the 'statuscolumn' string for line "lnum" in window "wp". -/// Fill "stcp" with the built status column string and attributes. -/// This can be called three times per win_line(), once for virt_lines, once for -/// the start of the buffer line "lnum" and once for the wrapped lines. -/// -/// @param[out] stcp Status column attributes -static void get_statuscol_str(win_T *wp, linenr_T lnum, int virtnum, statuscol_T *stcp) +/// Build and draw the 'statuscolumn' string for line "lnum" in window "wp". +static void draw_statuscol(win_T *wp, winlinevars_T *wlv, linenr_T lnum, int virtnum, int col_rows, + statuscol_T *stcp) { // When called for the first non-filler row of line "lnum" set num v:vars linenr_T relnum = virtnum == 0 ? abs(get_cursor_rel_lnum(wp, lnum)) : -1; + char buf[MAXPATHL]; // When a buffer's line count has changed, make a best estimate for the full // width of the status column by building with "w_nrwidth_line_count". Add // potentially truncated width and rebuild before drawing anything. if (wp->w_statuscol_line_count != wp->w_nrwidth_line_count) { wp->w_statuscol_line_count = wp->w_nrwidth_line_count; set_vim_var_nr(VV_VIRTNUM, 0); - build_statuscol_str(wp, wp->w_nrwidth_line_count, 0, stcp); - if (stcp->truncate > 0) { - // Add truncated width to avoid unnecessary redraws - int addwidth = MIN(stcp->truncate, MAX_NUMBERWIDTH - wp->w_nrwidth); - stcp->truncate = 0; - stcp->width += addwidth; + int width = build_statuscol_str(wp, wp->w_nrwidth_line_count, 0, buf, stcp); + if (width > stcp->width) { + int addwidth = MIN(width - stcp->width, MAX_STCWIDTH - stcp->width); wp->w_nrwidth += addwidth; wp->w_nrwidth_width = wp->w_nrwidth; + if (col_rows > 0) { + // If only column is being redrawn, we now need to redraw the text as well + wp->w_redr_statuscol = true; + return; + } + stcp->width += addwidth; wp->w_valid &= ~VALID_WCOL; } } set_vim_var_nr(VV_VIRTNUM, virtnum); - int width = build_statuscol_str(wp, lnum, relnum, stcp); + int width = build_statuscol_str(wp, lnum, relnum, buf, stcp); // Force a redraw in case of error or when truncated - if (*wp->w_p_stc == NUL || (stcp->truncate > 0 && wp->w_nrwidth < MAX_NUMBERWIDTH)) { - if (stcp->truncate > 0) { // Avoid truncating 'statuscolumn' - wp->w_nrwidth = MIN(MAX_NUMBERWIDTH, wp->w_nrwidth + stcp->truncate); - wp->w_nrwidth_width = wp->w_nrwidth; - } else { // 'statuscolumn' reset due to error + if (*wp->w_p_stc == NUL || (width > stcp->width && stcp->width < MAX_STCWIDTH)) { + if (*wp->w_p_stc == NUL) { // 'statuscolumn' reset due to error wp->w_nrwidth_line_count = 0; wp->w_nrwidth = (wp->w_p_nu || wp->w_p_rnu) * number_width(wp); + } else { // Avoid truncating 'statuscolumn' + wp->w_nrwidth += MIN(width - stcp->width, MAX_STCWIDTH - stcp->width); + wp->w_nrwidth_width = wp->w_nrwidth; } wp->w_redr_statuscol = true; return; } - // Reset text/highlight pointer and current attr for new line - stcp->textp = stcp->text; - stcp->hlrecp = stcp->hlrec; - stcp->cur_attr = stcp->num_attr; - stcp->text_end = stcp->text + strlen(stcp->text); - - int fill = stcp->width - width; - if (fill > 0) { - // Fill up with ' ' - memset(stcp->text_end, ' ', (size_t)fill); - *(stcp->text_end += fill) = NUL; - } -} - -/// Get information needed to display the next segment in the 'statuscolumn'. -/// If not yet at the end, prepare for next segment and decrement "wlv->draw_state". -/// -/// @param stcp Status column attributes -/// @param[in,out] wlv -static void get_statuscol_display_info(statuscol_T *stcp, winlinevars_T *wlv) -{ - wlv->c_extra = NUL; - wlv->c_final = NUL; - do { - wlv->draw_state = WL_STC; - wlv->char_attr = stcp->cur_attr; - wlv->p_extra = stcp->textp; - char *const section_end = stcp->hlrecp->start ? stcp->hlrecp->start : stcp->text_end; - wlv->n_extra = (int)(section_end - stcp->textp); - // Prepare for next highlight section if not yet at the end - if (section_end < stcp->text_end) { - int hl = stcp->hlrecp->userhl; - stcp->textp = stcp->hlrecp->start; - stcp->cur_attr = hl < 0 ? syn_id2attr(-hl) : stcp->num_attr; - stcp->hlrecp++; - wlv->draw_state = WL_STC - 1; - } - // Skip over empty highlight sections - } while (wlv->n_extra == 0 && stcp->textp < stcp->text_end); - if (wlv->n_extra > 0) { - static char transbuf[(MAX_NUMBERWIDTH + 9 + 9 * 2) * MB_MAXBYTES + 1]; - wlv->n_extra = (int)transstr_buf(wlv->p_extra, wlv->n_extra, transbuf, sizeof transbuf, true); - wlv->p_extra = transbuf; + char *p = buf; + char transbuf[MAXPATHL]; + int attr = stcp->num_attr; + size_t len = strlen(buf); + + // Draw each segment with the specified highlighting. + for (stl_hlrec_t *sp = stcp->hlrec; sp->start != NULL; sp++) { + ptrdiff_t textlen = sp->start - p; + // Make all characters printable. + size_t translen = transstr_buf(p, textlen, transbuf, MAXPATHL, true); + draw_col_buf(wp, wlv, transbuf, translen, attr, false); + p = sp->start; + int hl = sp->userhl; + attr = hl < 0 ? syn_id2attr(-hl) : stcp->num_attr; } + size_t translen = transstr_buf(p, buf + len - p, transbuf, MAXPATHL, true); + draw_col_buf(wp, wlv, transbuf, translen, attr, false); + draw_col_fill(wlv, schar_from_ascii(' '), stcp->width - width, stcp->num_attr); } static void handle_breakindent(win_T *wp, winlinevars_T *wlv) { - if (wp->w_briopt_sbr && wlv->draw_state == WL_BRI - 1 - && *get_showbreak_value(wp) != NUL) { - // draw indent after showbreak value - wlv->draw_state = WL_BRI; - } else if (wp->w_briopt_sbr && wlv->draw_state == WL_SBR) { - // after the showbreak, draw the breakindent - wlv->draw_state = WL_BRI - 1; - } - // draw 'breakindent': indent wrapped text accordingly - if (wlv->draw_state == WL_BRI - 1 && wlv->n_extra == 0) { - wlv->draw_state = WL_BRI; - // if wlv->need_showbreak is set, breakindent also applies - if (wp->w_p_bri && (wlv->row != wlv->startrow || wlv->need_showbreak) - && wlv->filler_lines == 0) { - wlv->char_attr = 0; - if (wlv->diff_hlf != (hlf_T)0) { - wlv->char_attr = win_hl_attr(wp, (int)wlv->diff_hlf); - } - wlv->p_extra = NULL; - wlv->c_extra = ' '; - wlv->c_final = NUL; - wlv->n_extra = get_breakindent_win(wp, ml_get_buf(wp->w_buffer, wlv->lnum)); - if (wlv->row == wlv->startrow) { - wlv->n_extra -= win_col_off2(wp); - if (wlv->n_extra < 0) { - wlv->n_extra = 0; - } - } - if (wp->w_skipcol > 0 && wlv->startrow == 0 && wp->w_p_wrap && wp->w_briopt_sbr) { - wlv->need_showbreak = false; + // if wlv->need_showbreak is set, breakindent also applies + if (wp->w_p_bri && (wlv->row > wlv->startrow + wlv->filler_lines + || wlv->need_showbreak)) { + int attr = 0; + if (wlv->diff_hlf != (hlf_T)0) { + attr = win_hl_attr(wp, (int)wlv->diff_hlf); + } + int num = get_breakindent_win(wp, ml_get_buf(wp->w_buffer, wlv->lnum)); + if (wlv->row == wlv->startrow) { + num -= win_col_off2(wp); + if (wlv->n_extra < 0) { + num = 0; } - // Correct end of highlighted area for 'breakindent', - // required wen 'linebreak' is also set. - if (wlv->tocol == wlv->vcol) { - wlv->tocol += wlv->n_extra; + } + + colnr_T vcol_before = wlv->vcol; + + for (int i = 0; i < num; i++) { + linebuf_char[wlv->off] = schar_from_ascii(' '); + + advance_color_col(wlv, wlv->vcol); + int myattr = attr; + if (wlv->color_cols && wlv->vcol == *wlv->color_cols) { + myattr = hl_combine_attr(win_hl_attr(wp, HLF_MC), myattr); } + linebuf_attr[wlv->off] = myattr; + linebuf_vcol[wlv->off] = wlv->vcol++; // These are vcols, sorry I don't make the rules + wlv->off++; } + + // Correct start of highlighted area for 'breakindent', + if (wlv->fromcol >= vcol_before && wlv->fromcol < wlv->vcol) { + wlv->fromcol = wlv->vcol; + } + + // Correct end of highlighted area for 'breakindent', + // required wen 'linebreak' is also set. + if (wlv->tocol == vcol_before) { + wlv->tocol = wlv->vcol; + } + } + + if (wp->w_skipcol > 0 && wlv->startrow == 0 && wp->w_p_wrap && wp->w_briopt_sbr) { + wlv->need_showbreak = false; } } static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv) { + int remaining = wp->w_grid.cols - wlv->off; if (wlv->filler_todo > wlv->filler_lines - wlv->n_virt_lines) { // TODO(bfredl): check this doesn't inhibit TUI-style // clear-to-end-of-line. - wlv->c_extra = ' '; - wlv->c_final = NUL; - wlv->n_extra = wp->w_grid.cols - wlv->col; - wlv->char_attr = 0; + draw_col_fill(wlv, schar_from_ascii(' '), remaining, 0); } else if (wlv->filler_todo > 0) { // Draw "deleted" diff line(s) - if (char2cells(wp->w_p_fcs_chars.diff) > 1) { - wlv->c_extra = '-'; - wlv->c_final = NUL; - } else { - wlv->c_extra = wp->w_p_fcs_chars.diff; - wlv->c_final = NUL; - } - wlv->n_extra = wp->w_grid.cols - wlv->col; - wlv->char_attr = win_hl_attr(wp, HLF_DED); + schar_T c = wp->w_p_fcs_chars.diff; + draw_col_fill(wlv, c, remaining, win_hl_attr(wp, HLF_DED)); } char *const sbr = get_showbreak_value(wp); if (*sbr != NUL && wlv->need_showbreak) { // Draw 'showbreak' at the start of each broken line. - wlv->p_extra = sbr; - wlv->c_extra = NUL; - wlv->c_final = NUL; - wlv->n_extra = (int)strlen(sbr); - wlv->char_attr = win_hl_attr(wp, HLF_AT); - if (wp->w_skipcol == 0 || wlv->startrow != 0 || !wp->w_p_wrap) { - wlv->need_showbreak = false; - } - wlv->vcol_sbr = wlv->vcol + mb_charlen(sbr); + // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'. + int attr = hl_combine_attr(wlv->cul_attr, win_hl_attr(wp, HLF_AT)); + colnr_T vcol_before = wlv->vcol; + draw_col_buf(wp, wlv, sbr, strlen(sbr), attr, true); + wlv->vcol_sbr = wlv->vcol; // Correct start of highlighted area for 'showbreak'. - if (wlv->fromcol >= wlv->vcol && wlv->fromcol < wlv->vcol_sbr) { - wlv->fromcol = wlv->vcol_sbr; + if (wlv->fromcol >= vcol_before && wlv->fromcol < wlv->vcol) { + wlv->fromcol = wlv->vcol; } // Correct end of highlighted area for 'showbreak', // required when 'linebreak' is also set. - if (wlv->tocol == wlv->vcol) { - wlv->tocol += wlv->n_extra; - } - // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'. - if (wlv->cul_attr) { - wlv->char_attr = hl_combine_attr(wlv->cul_attr, wlv->char_attr); + if (wlv->tocol == vcol_before) { + wlv->tocol = wlv->vcol; } } + + if (wp->w_skipcol == 0 || wlv->startrow > 0 || !wp->w_p_wrap || !wp->w_briopt_sbr) { + wlv->need_showbreak = false; + } } static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv) @@ -825,7 +774,7 @@ static bool has_more_inline_virt(winlinevars_T *wlv, ptrdiff_t v) return false; } -static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t v) +static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t v, bool selected) { while (wlv->n_extra == 0) { if (wlv->virt_inline_i >= kv_size(wlv->virt_inline)) { @@ -835,6 +784,11 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t DecorState *state = &decor_state; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange *item = &kv_A(state->active, i); + if (item->draw_col == -3) { + // No more inline virtual text before this non-inline virtual text item, + // so its position can be decided now. + decor_init_draw_col(wlv->off, selected, item); + } if (item->start_row != state->row || item->kind != kDecorKindVirtText || item->data.vt->pos != kVPosInline @@ -864,8 +818,8 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t if (wlv->n_extra == 0) { continue; } - wlv->c_extra = NUL; - wlv->c_final = NUL; + wlv->sc_extra = NUL; + wlv->sc_final = NUL; wlv->extra_attr = attr; wlv->n_attr = mb_charlen(text); // If the text didn't reach until the first window @@ -937,42 +891,46 @@ static colnr_T get_leadcol(win_T *wp, const char *ptr, const char *line) } /// Start a screen line at column zero. -static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra) +static void win_line_start(win_T *wp, winlinevars_T *wlv) { wlv->col = 0; wlv->off = 0; wlv->need_lbr = false; - - if (save_extra) { - // reset the drawing state for the start of a wrapped line - wlv->draw_state = WL_START; - wlv->saved_n_extra = wlv->n_extra; - wlv->saved_p_extra = wlv->p_extra; - wlv->saved_extra_for_extmark = wlv->extra_for_extmark; - wlv->saved_c_extra = wlv->c_extra; - wlv->saved_c_final = wlv->c_final; - wlv->need_lbr = true; - wlv->saved_char_attr = wlv->char_attr; - - wlv->n_extra = 0; + for (int i = 0; i < wp->w_grid.cols; i++) { + linebuf_char[i] = schar_from_ascii(' '); + linebuf_attr[i] = -1; + linebuf_vcol[i] = -1; } } -/// Called when wlv->draw_state is set to WL_LINE. -static void win_line_continue(winlinevars_T *wlv) +static void fix_for_boguscols(winlinevars_T *wlv) { - if (wlv->saved_n_extra > 0) { - // Continue item from end of wrapped line. - wlv->n_extra = wlv->saved_n_extra; - wlv->saved_n_extra = 0; - wlv->c_extra = wlv->saved_c_extra; - wlv->c_final = wlv->saved_c_final; - wlv->p_extra = wlv->saved_p_extra; - wlv->extra_for_extmark = wlv->saved_extra_for_extmark; - wlv->char_attr = wlv->saved_char_attr; - } else { - wlv->char_attr = 0; + wlv->n_extra += wlv->vcol_off; + wlv->vcol -= wlv->vcol_off; + wlv->vcol_off = 0; + wlv->col -= wlv->boguscols; + wlv->old_boguscols = wlv->boguscols; + wlv->boguscols = 0; +} + +static int get_rightmost_vcol(win_T *wp, const int *color_cols) +{ + int ret = 0; + + if (wp->w_p_cuc) { + ret = wp->w_virtcol; } + + if (color_cols) { + // determine rightmost colorcolumn to possibly draw + for (int i = 0; color_cols[i] >= 0; i++) { + if (ret < color_cols[i]) { + ret = color_cols[i]; + } + } + } + + return ret; } /// Display line "lnum" of window "wp" on the screen. @@ -981,7 +939,8 @@ static void win_line_continue(winlinevars_T *wlv) /// @param lnum line to display /// @param startrow first row relative to window grid /// @param endrow last grid row to be redrawn -/// @param number_only only update the number column +/// @param col_rows set to the height of the line when only updating the columns, +/// otherwise set to 0 /// @param spv 'spell' related variables kept between calls for "wp" /// @param foldinfo fold info for this line /// @param[in, out] providers decoration providers active this line @@ -989,19 +948,17 @@ static void win_line_continue(winlinevars_T *wlv) /// or explicitly return `false`. /// /// @return the number of last row the line occupies. -int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_only, spellvars_T *spv, - foldinfo_T foldinfo, DecorProviders *providers) +int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, spellvars_T *spv, + foldinfo_T foldinfo) { - winlinevars_T wlv; // variables passed between functions - colnr_T vcol_prev = -1; // "wlv.vcol" of previous character - char *line; // current line - char *ptr; // current position in "line" ScreenGrid *grid = &wp->w_grid; // grid specific to the window - static char *at_end_str = ""; // used for p_extra when displaying curwin->w_p_lcs_chars.eol - // at end-of-line const bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; + const bool has_foldtext = has_fold && *wp->w_p_fdt != NUL; + + const bool is_wrapped = wp->w_p_wrap + && !has_fold; // Never wrap folded lines int saved_attr2 = 0; // char_attr saved for n_attr int n_attr3 = 0; // chars with overruling special attr @@ -1010,8 +967,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl int fromcol_prev = -2; // start of inverting after cursor bool noinvcur = false; // don't invert the cursor bool lnum_in_visual_area = false; - pos_T pos; - ptrdiff_t v; bool attr_pri = false; // char_attr has priority bool area_highlighting = false; // Visual or incsearch highlighting in this line @@ -1022,10 +977,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl int decor_attr = 0; // attributes desired by syntax and extmarks bool has_syntax = false; // this buffer has syntax highl. int folded_attr = 0; // attributes for folded line - int save_did_emsg; int eol_hl_off = 0; // 1 if highlighted char after EOL - bool draw_color_col = false; // highlight colorcolumn - int *color_cols = NULL; // pointer to according columns array #define SPWORDLEN 150 char nextline[SPWORDLEN * 2]; // text with start of the next line int nextlinecol = 0; // column where nextline[] starts @@ -1034,17 +986,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl int spell_attr = 0; // attributes desired by spelling int word_end = 0; // last byte with same spell_attr int cur_checked_col = 0; // checked column for current line - int extra_check = 0; // has syntax or linebreak + bool extra_check = 0; // has syntax or linebreak int multi_attr = 0; // attributes desired by multibyte int mb_l = 1; // multi-byte byte length int mb_c = 0; // decoded multi-byte character - schar_T mb_schar; // complete screen char + schar_T mb_schar = 0; // complete screen char int change_start = MAXCOL; // first col of changed area int change_end = -1; // last col of changed area bool in_multispace = false; // in multiple consecutive spaces int multispace_pos = 0; // position in lcs-multispace string - int line_attr_save; - int line_attr_lowprio_save; bool search_attr_from_match = false; // if search_attr is from :match bool has_decor = false; // this buffer has decoration @@ -1076,43 +1026,35 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl int prev_syntax_id = 0; int conceal_attr = win_hl_attr(wp, HLF_CONCEAL); bool is_concealing = false; - int did_wcol = false; - int old_boguscols = 0; -#define VCOL_HLC (wlv.vcol - wlv.vcol_off) -#define FIX_FOR_BOGUSCOLS \ - { \ - wlv.n_extra += wlv.vcol_off; \ - wlv.vcol -= wlv.vcol_off; \ - wlv.vcol_off = 0; \ - wlv.col -= wlv.boguscols; \ - old_boguscols = wlv.boguscols; \ - wlv.boguscols = 0; \ - } + bool did_wcol = false; +#define vcol_hlc(wlv) ((wlv).vcol - (wlv).vcol_off) assert(startrow < endrow); - CLEAR_FIELD(wlv); - - wlv.lnum = lnum; - wlv.foldinfo = foldinfo; - wlv.startrow = startrow; - wlv.row = startrow; - wlv.fromcol = -10; - wlv.tocol = MAXCOL; - wlv.vcol_sbr = -1; + // variables passed between functions + winlinevars_T wlv = { + .lnum = lnum, + .foldinfo = foldinfo, + .startrow = startrow, + .row = startrow, + .fromcol = -10, + .tocol = MAXCOL, + .vcol_sbr = -1, + .old_boguscols = 0, + }; buf_T *buf = wp->w_buffer; const bool end_fill = (lnum == buf->b_ml.ml_line_count + 1); - if (!number_only) { + if (col_rows == 0) { // To speed up the loop below, set extra_check when there is linebreak, // trailing white space and/or syntax processing to be done. extra_check = wp->w_p_lbr; if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow - && !has_fold && !end_fill) { + && !has_foldtext && !end_fill) { // Prepare for syntax highlighting in this line. When there is an // error, stop syntax highlighting. - save_did_emsg = did_emsg; + int save_did_emsg = did_emsg; did_emsg = false; syntax_start(wp, lnum); if (did_emsg) { @@ -1128,17 +1070,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl has_decor = decor_redraw_line(wp, lnum - 1, &decor_state); - decor_providers_invoke_line(wp, providers, lnum - 1, &has_decor); + decor_providers_invoke_line(wp, lnum - 1, &has_decor); if (has_decor) { extra_check = true; } // Check for columns to display for 'colorcolumn'. - color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; - if (color_cols != NULL) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); - } + wlv.color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; + advance_color_col(&wlv, vcol_hlc(wlv)); // handle Visual active in this window if (VIsual_active && wp->w_buffer == curwin->w_buffer) { @@ -1182,7 +1122,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } else if (bot->col == MAXCOL) { wlv.tocol = MAXCOL; } else { - pos = *bot; + pos_T pos = *bot; if (*p_sel == 'e') { getvvcol(wp, &pos, (colnr_T *)&wlv.tocol, NULL, NULL); } else { @@ -1207,7 +1147,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // handle 'incsearch' and ":s///c" highlighting } else if (highlight_match && wp == curwin - && !has_fold + && !has_foldtext && lnum >= curwin->w_cursor.lnum && lnum <= curwin->w_cursor.lnum + search_match_lines) { if (lnum == curwin->w_cursor.lnum) { @@ -1217,8 +1157,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl wlv.fromcol = 0; } if (lnum == curwin->w_cursor.lnum + search_match_lines) { - pos.lnum = lnum; - pos.col = search_match_endcol; + pos_T pos = { + .lnum = lnum, + .col = search_match_endcol, + }; getvcol(curwin, &pos, (colnr_T *)&wlv.tocol, NULL, NULL); } // do at least one character; happens when past end of line @@ -1265,7 +1207,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Do not show the cursor line in the text when Visual mode is active, // because it's not clear what is selected then. && !(wp == curwin && VIsual_active)) { - cul_screenline = (wp->w_p_wrap && (wp->w_p_culopt_flags & CULOPT_SCRLINE)); + cul_screenline = (is_wrapped && (wp->w_p_culopt_flags & CULOPT_SCRLINE)); if (!cul_screenline) { apply_cursorline_highlight(wp, &wlv); } else { @@ -1286,7 +1228,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl statuscol.draw = true; statuscol.sattrs = wlv.sattrs; statuscol.foldinfo = foldinfo; - statuscol.width = win_col_off(wp) - (cmdwin_type != 0 && wp == curwin); + statuscol.width = win_col_off(wp) - (wp == cmdwin_win); statuscol.use_cul = use_cursor_line_highlight(wp, lnum); statuscol.sign_cul_id = statuscol.use_cul ? sign_cul_attr : 0; statuscol.num_attr = sign_num_attr > 0 ? syn_id2attr(sign_num_attr) : 0; @@ -1311,12 +1253,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl area_highlighting = true; } - if (cul_screenline) { - line_attr_save = wlv.line_attr; - line_attr_lowprio_save = wlv.line_attr_lowprio; - } + int line_attr_save = wlv.line_attr; + int line_attr_lowprio_save = wlv.line_attr_lowprio; - if (spv->spv_has_spell && !number_only) { + if (spv->spv_has_spell && col_rows == 0) { // Prepare for spell checking. extra_check = true; @@ -1339,14 +1279,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Trick: skip a few chars for C/shell/Vim comments nextline[SPWORDLEN] = NUL; if (lnum < wp->w_buffer->b_ml.ml_line_count) { - line = ml_get_buf(wp->w_buffer, lnum + 1); + char *line = ml_get_buf(wp->w_buffer, lnum + 1); spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN); } assert(!end_fill); - line = ml_get_buf(wp->w_buffer, lnum); + char *line = ml_get_buf(wp->w_buffer, lnum); // If current line is empty, check first word in next line for capital. - ptr = skipwhite(line); + char *ptr = skipwhite(line); if (*ptr == NUL) { spv->spv_cap_col = 0; spv->spv_capcol_lnum = lnum + 1; @@ -1361,33 +1301,36 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl nextlinecol = MAXCOL; nextline_idx = 0; } else { - v = (ptrdiff_t)strlen(line); - if (v < SPWORDLEN) { + const size_t line_len = strlen(line); + if (line_len < SPWORDLEN) { // Short line, use it completely and append the start of the // next line. nextlinecol = 0; - memmove(nextline, line, (size_t)v); - STRMOVE(nextline + v, nextline + SPWORDLEN); - nextline_idx = (int)v + 1; + memmove(nextline, line, line_len); + STRMOVE(nextline + line_len, nextline + SPWORDLEN); + nextline_idx = (int)line_len + 1; } else { // Long line, use only the last SPWORDLEN bytes. - nextlinecol = (int)v - SPWORDLEN; + nextlinecol = (int)line_len - SPWORDLEN; memmove(nextline, line + nextlinecol, SPWORDLEN); nextline_idx = SPWORDLEN + 1; } } } - line = end_fill ? "" : ml_get_buf(wp->w_buffer, lnum); - ptr = line; + // current line + char *line = end_fill ? "" : ml_get_buf(wp->w_buffer, lnum); + // current position in "line" + char *ptr = line; colnr_T trailcol = MAXCOL; // start of trailing spaces colnr_T leadcol = 0; // start of leading spaces - int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used - int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used + bool lcs_eol_todo = true; // need to keep track of this even if lcs_eol is NUL + const schar_T lcs_eol = wp->w_p_lcs_chars.eol; // 'eol' value + schar_T lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used, then NUL - if (wp->w_p_list && !has_fold && !end_fill) { + if (wp->w_p_list && !has_foldtext && !end_fill) { if (wp->w_p_lcs_chars.space || wp->w_p_lcs_chars.multispace != NULL || wp->w_p_lcs_chars.leadmultispace != NULL @@ -1402,37 +1345,36 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the // first character to be displayed. - if (wp->w_p_wrap) { - v = startrow == 0 ? wp->w_skipcol : 0; - } else { - v = wp->w_leftcol; - } - if (v > 0 && !number_only) { + const int start_col = wp->w_p_wrap + ? (startrow == 0 ? wp->w_skipcol : 0) + : wp->w_leftcol; + + if (start_col > 0 && col_rows == 0) { char *prev_ptr = ptr; - chartabsize_T cts; - int charsize = 0; - int head = 0; - - init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, ptr); - cts.cts_max_head_vcol = (int)v; - while (cts.cts_vcol < v && *cts.cts_ptr != NUL) { - head = 0; - charsize = win_lbr_chartabsize(&cts, &head); - cts.cts_vcol += charsize; - prev_ptr = cts.cts_ptr; - MB_PTR_ADV(cts.cts_ptr); + CharSize cs = { 0 }; + + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, wp, lnum, line); + csarg.max_head_vcol = start_col; + int vcol = wlv.vcol; + StrCharInfo ci = utf_ptr2StrCharInfo(ptr); + while (vcol < start_col && *ci.ptr != NUL) { + cs = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg); + vcol += cs.width; + prev_ptr = ci.ptr; + ci = utfc_next(ci); if (wp->w_p_list) { - in_multispace = *prev_ptr == ' ' && (*cts.cts_ptr == ' ' + in_multispace = *prev_ptr == ' ' && (*ci.ptr == ' ' || (prev_ptr > line && prev_ptr[-1] == ' ')); if (!in_multispace) { multispace_pos = 0; - } else if (cts.cts_ptr >= line + leadcol + } else if (ci.ptr >= line + leadcol && wp->w_p_lcs_chars.multispace != NULL) { multispace_pos++; if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) { multispace_pos = 0; } - } else if (cts.cts_ptr < line + leadcol + } else if (ci.ptr < line + leadcol && wp->w_p_lcs_chars.leadmultispace != NULL) { multispace_pos++; if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) { @@ -1441,9 +1383,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } } } - wlv.vcol = cts.cts_vcol; - ptr = cts.cts_ptr; - clear_chartabsize_arg(&cts); + wlv.vcol = vcol; + ptr = ci.ptr; + int charsize = cs.width; + int head = cs.head; // When: // - 'cuc' is set, or @@ -1451,22 +1394,22 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // - 'virtualedit' is set, or // - the visual mode is active, // the end of the line may be before the start of the displayed part. - if (wlv.vcol < v && (wp->w_p_cuc - || draw_color_col - || virtual_active() - || (VIsual_active && wp->w_buffer == curwin->w_buffer))) { - wlv.vcol = (colnr_T)v; + if (wlv.vcol < start_col && (wp->w_p_cuc + || wlv.color_cols + || virtual_active() + || (VIsual_active && wp->w_buffer == curwin->w_buffer))) { + wlv.vcol = start_col; } // Handle a character that's not completely on the screen: Put ptr at // that character but skip the first few screen characters. - if (wlv.vcol > v) { + if (wlv.vcol > start_col) { wlv.vcol -= charsize; ptr = prev_ptr; } - if (v > wlv.vcol) { - wlv.skip_cells = (int)v - wlv.vcol - head; + if (start_col > wlv.vcol) { + wlv.skip_cells = start_col - wlv.vcol - head; } // Adjust for when the inverted text is before the screen, @@ -1484,14 +1427,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // When spell checking a word we need to figure out the start of the // word and if it's badly spelled or not. if (spv->spv_has_spell) { - size_t len; colnr_T linecol = (colnr_T)(ptr - line); hlf_T spell_hlf = HLF_COUNT; - pos = wp->w_cursor; + pos_T pos = wp->w_cursor; wp->w_cursor.lnum = lnum; wp->w_cursor.col = linecol; - len = spell_move_to(wp, FORWARD, true, true, &spell_hlf); + size_t len = spell_move_to(wp, FORWARD, true, true, &spell_hlf); // spell_move_to() may call ml_get() and make "line" invalid line = ml_get_buf(wp->w_buffer, lnum); @@ -1540,15 +1482,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } } - if (!number_only && !has_fold && !end_fill) { - v = ptr - line; - area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v, + if (col_rows == 0 && !has_foldtext && !end_fill) { + const int v = (int)(ptr - line); + area_highlighting |= prepare_search_hl_line(wp, lnum, v, &line, &screen_search_hl, &search_attr, &search_attr_from_match); ptr = line + v; // "line" may have been updated } - win_line_start(wp, &wlv, false); + win_line_start(wp, &wlv); + bool draw_cols = true; + int leftcols_width = 0; // won't highlight after TERM_ATTRS_MAX columns int term_attrs[TERM_ATTRS_MAX] = { 0 }; @@ -1557,7 +1501,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl extra_check = true; } - int sign_idx = 0; + const bool may_have_inline_virt + = !has_foldtext && buf_meta_total(wp->w_buffer, kMTMetaInline) > 0; int virt_line_index; int virt_line_offset = -1; // Repeat for the whole displayed line. @@ -1568,129 +1513,116 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl bool did_decrement_ptr = false; // Skip this quickly when working on the text. - if (wlv.draw_state != WL_LINE) { + if (draw_cols) { if (cul_screenline) { wlv.cul_attr = 0; wlv.line_attr = line_attr_save; wlv.line_attr_lowprio = line_attr_lowprio_save; } - if (wlv.draw_state == WL_CMDLINE - 1 && wlv.n_extra == 0) { - wlv.draw_state = WL_CMDLINE; - if (cmdwin_type != 0 && wp == curwin) { - // Draw the cmdline character. - wlv.n_extra = 1; - wlv.c_extra = cmdwin_type; - wlv.c_final = NUL; - wlv.char_attr = win_hl_attr(wp, HLF_AT); - } + assert(wlv.off == 0); + + if (wp == cmdwin_win) { + // Draw the cmdline character. + draw_col_fill(&wlv, schar_from_ascii(cmdwin_type), 1, win_hl_attr(wp, HLF_AT)); } - if (wlv.draw_state == WL_FOLD - 1 && wlv.n_extra == 0) { - if (wlv.filler_todo > 0) { - int index = wlv.filler_todo - (wlv.filler_lines - wlv.n_virt_lines); - if (index > 0) { - virt_line_index = (int)kv_size(virt_lines) - index; - assert(virt_line_index >= 0); - virt_line_offset = kv_A(virt_lines, virt_line_index).left_col ? 0 : win_col_off(wp); - } - } - if (virt_line_offset == 0) { - // Skip the column states if there is a "virt_left_col" line. - wlv.draw_state = WL_BRI - 1; - } else if (statuscol.draw) { - // Skip fold, sign and number states if 'statuscolumn' is set. - wlv.draw_state = WL_STC - 1; + if (wlv.filler_todo > 0) { + int index = wlv.filler_todo - (wlv.filler_lines - wlv.n_virt_lines); + if (index > 0) { + virt_line_index = (int)kv_size(virt_lines) - index; + assert(virt_line_index >= 0); + virt_line_offset = kv_A(virt_lines, virt_line_index).left_col ? 0 : win_col_off(wp); } } - if (wlv.draw_state == WL_FOLD - 1 && wlv.n_extra == 0) { - wlv.draw_state = WL_FOLD; - handle_foldcolumn(wp, &wlv); - } + if (virt_line_offset == 0) { + // skip columns + } else if (statuscol.draw) { + // Draw 'statuscolumn' if it is set. + if (sign_num_attr == 0) { + statuscol.num_attr = get_line_number_attr(wp, &wlv); + } + const int v = (int)(ptr - line); + draw_statuscol(wp, &wlv, lnum, wlv.row - startrow - wlv.filler_lines, col_rows, &statuscol); + if (wp->w_redr_statuscol) { + break; + } + if (!end_fill) { + // Get the line again as evaluating 'statuscolumn' may free it. + line = ml_get_buf(wp->w_buffer, lnum); + ptr = line + v; + } + } else { + // draw builtin info columns: fold, sign, number + draw_foldcolumn(wp, &wlv); - // sign column, this is hit until sign_idx reaches count - if (wlv.draw_state == WL_SIGN - 1 && wlv.n_extra == 0) { - // Show the sign column when desired. - wlv.draw_state = WL_SIGN; - if (wp->w_scwidth > 0) { - get_sign_display_info(false, wp, &wlv, sign_idx, sign_cul_attr); - if (++sign_idx < wp->w_scwidth) { - wlv.draw_state = WL_SIGN - 1; - } else { - sign_idx = 0; - } + // wp->w_scwidth is zero if signcol=number is used + for (int sign_idx = 0; sign_idx < wp->w_scwidth; sign_idx++) { + draw_sign(false, wp, &wlv, sign_idx, sign_cul_attr); } - } - if (wlv.draw_state == WL_NR - 1 && wlv.n_extra == 0) { - // Show the line number, if desired. - wlv.draw_state = WL_NR; - handle_lnum_col(wp, &wlv, sign_num_attr, sign_cul_attr); + draw_lnum_col(wp, &wlv, sign_num_attr, sign_cul_attr); } - if (wlv.draw_state == WL_STC - 1 && wlv.n_extra == 0) { - wlv.draw_state = WL_STC; - // Draw the 'statuscolumn' if option is set. - if (statuscol.draw) { - if (sign_num_attr == 0) { - statuscol.num_attr = get_line_number_attr(wp, &wlv); + win_col_offset = wlv.off; + + // When only updating the columns and that's done, stop here. + if (col_rows > 0) { + win_put_linebuf(wp, wlv.row, 0, wlv.off, wlv.off, bg_attr, false); + // Need to update more screen lines if: + // - 'statuscolumn' needs to be drawn, or + // - LineNrAbove or LineNrBelow is used, or + // - still drawing filler lines. + if ((wlv.row + 1 - wlv.startrow < col_rows + && (statuscol.draw + || win_hl_attr(wp, HLF_LNA) != win_hl_attr(wp, HLF_N) + || win_hl_attr(wp, HLF_LNB) != win_hl_attr(wp, HLF_N))) + || wlv.filler_todo > 0) { + wlv.row++; + if (wlv.row == endrow) { + break; } - if (statuscol.textp == NULL) { - v = (ptr - line); - get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol); - if (!end_fill) { - // Get the line again as evaluating 'statuscolumn' may free it. - line = ml_get_buf(wp->w_buffer, lnum); - ptr = line + v; - } - if (wp->w_redr_statuscol) { - break; - } + wlv.filler_todo--; + if (wlv.filler_todo == 0 && (wp->w_botfill || end_fill)) { + break; } - get_statuscol_display_info(&statuscol, &wlv); + // win_line_start(wp, &wlv); + wlv.col = 0; + wlv.off = 0; + continue; + } else { + break; } } - if (wlv.draw_state == WL_STC && wlv.n_extra == 0) { - win_col_offset = wlv.off; - } - // Check if 'breakindent' applies and show it. - // May change wlv.draw_state to WL_BRI or WL_BRI - 1. - if (wlv.n_extra == 0) { + if (!wp->w_briopt_sbr) { handle_breakindent(wp, &wlv); } - - if (wlv.draw_state == WL_SBR - 1 && wlv.n_extra == 0) { - wlv.draw_state = WL_SBR; - handle_showbreak_and_filler(wp, &wlv); + handle_showbreak_and_filler(wp, &wlv); + if (wp->w_briopt_sbr) { + handle_breakindent(wp, &wlv); } - if (wlv.draw_state == WL_LINE - 1 && wlv.n_extra == 0) { - sign_idx = 0; - wlv.draw_state = WL_LINE; - if (has_decor && wlv.row == startrow + wlv.filler_lines) { - // hide virt_text on text hidden by 'nowrap' or 'smoothscroll' - decor_redraw_col(wp, (colnr_T)(ptr - line) - 1, wlv.off, true, &decor_state); - } - win_line_continue(&wlv); // use wlv.saved_ values + wlv.col = wlv.off; + draw_cols = false; + if (wlv.filler_todo <= 0) { + leftcols_width = wlv.off; + } + if (has_decor && wlv.row == startrow + wlv.filler_lines) { + // hide virt_text on text hidden by 'nowrap' or 'smoothscroll' + decor_redraw_col(wp, (colnr_T)(ptr - line) - 1, wlv.off, true, &decor_state); } } - if (cul_screenline && wlv.draw_state == WL_LINE - && wlv.vcol >= left_curline_col - && wlv.vcol < right_curline_col) { + if (cul_screenline && wlv.vcol >= left_curline_col && wlv.vcol < right_curline_col) { apply_cursorline_highlight(wp, &wlv); } - // When still displaying '$' of change command, stop at cursor - if (((dollar_vcol >= 0 - && wp == curwin - && lnum == wp->w_cursor.lnum - && wlv.vcol >= wp->w_virtcol) - || (number_only && wlv.draw_state > WL_STC)) - && wlv.filler_todo <= 0) { + // When still displaying '$' of change command, stop at cursor. + if (dollar_vcol >= 0 && wp == curwin + && lnum == wp->w_cursor.lnum && wlv.vcol >= wp->w_virtcol) { draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row); // don't clear anything after wlv.col win_put_linebuf(wp, wlv.row, 0, wlv.col, wlv.col, bg_attr, false); @@ -1704,15 +1636,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl break; } - const bool draw_folded = wlv.draw_state == WL_LINE && has_fold - && wlv.row == startrow + wlv.filler_lines; + const bool draw_folded = has_fold && wlv.row == startrow + wlv.filler_lines; if (draw_folded && wlv.n_extra == 0) { wlv.char_attr = folded_attr = win_hl_attr(wp, HLF_FL); + decor_attr = 0; } int extmark_attr = 0; - if (wlv.draw_state == WL_LINE - && (area_highlighting || spv->spv_has_spell || extra_check)) { + if (area_highlighting || spv->spv_has_spell || extra_check) { if (wlv.n_extra == 0 || !wlv.extra_for_extmark) { wlv.reset_extra_attr = false; } @@ -1735,14 +1666,21 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl bool selected = (area_active || (area_highlighting && noinvcur && wlv.vcol == wp->w_virtcol)); + // When there may be inline virtual text, position of non-inline virtual text + // can only be decided after drawing inline virtual text with lower priority. if (decor_need_recheck) { - decor_recheck_draw_col(wlv.off, selected, &decor_state); + if (!may_have_inline_virt) { + decor_recheck_draw_col(wlv.off, selected, &decor_state); + } decor_need_recheck = false; } - extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state); - - if (!has_fold && wp->w_buffer->b_virt_text_inline > 0) { - handle_inline_virtual_text(wp, &wlv, v); + if (wlv.filler_todo <= 0) { + extmark_attr = decor_redraw_col(wp, (colnr_T)(ptr - line), + may_have_inline_virt ? -3 : wlv.off, + selected, &decor_state); + } + if (may_have_inline_virt) { + handle_inline_virtual_text(wp, &wlv, ptr - line, selected); if (wlv.n_extra > 0 && wlv.virt_inline_hl_mode <= kHlModeReplace) { // restore search_attr and area_attr when n_extra is down to zero // TODO(bfredl): this is ugly as fuck. look if we can do this some other way. @@ -1758,9 +1696,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } } - int *area_attr_p - = wlv.extra_for_extmark && wlv.virt_inline_hl_mode <= kHlModeReplace - ? &saved_area_attr : &area_attr; + int *area_attr_p = wlv.extra_for_extmark && wlv.virt_inline_hl_mode <= kHlModeReplace + ? &saved_area_attr : &area_attr; // handle Visual or match highlighting in this line if (wlv.vcol == wlv.fromcol @@ -1780,13 +1717,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl area_active = false; } - if (!has_fold && wlv.n_extra == 0) { + if (!has_foldtext && wlv.n_extra == 0) { // Check for start/end of 'hlsearch' and other matches. // After end, check for start/end of next match. // When another match, have to check for start again. - v = (ptr - line); - search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl, - &has_match_conc, &match_conc, lcs_eol_one, + const int v = (int)(ptr - line); + search_attr = update_search_hl(wp, lnum, v, &line, &screen_search_hl, + &has_match_conc, &match_conc, lcs_eol_todo, &on_last_col, &search_attr_from_match); ptr = line + v; // "line" may have been changed @@ -1847,7 +1784,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } } - if (draw_folded && wlv.n_extra == 0 && wlv.col == win_col_offset) { + if (draw_folded && has_foldtext && wlv.n_extra == 0 && wlv.col == win_col_offset) { + const int v = (int)(ptr - line); linenr_T lnume = lnum + foldinfo.fi_lines - 1; memset(buf_fold, ' ', FOLD_TEXT_LEN); wlv.p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold, &fold_vt); @@ -1856,8 +1794,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl if (wlv.p_extra != buf_fold) { foldtext_free = wlv.p_extra; } - wlv.c_extra = NUL; - wlv.c_final = NUL; + wlv.sc_extra = NUL; + wlv.sc_final = NUL; wlv.p_extra[wlv.n_extra] = NUL; // Get the line again as evaluating 'foldtext' may free it. @@ -1865,10 +1803,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl ptr = line + v; } - if (draw_folded && wlv.n_extra == 0 && wlv.col < grid->cols) { + if (draw_folded && wlv.n_extra == 0 && wlv.col < grid->cols && (has_foldtext || *ptr == NUL)) { // Fill rest of line with 'fold'. - wlv.c_extra = wp->w_p_fcs_chars.fold; - wlv.c_final = NUL; + wlv.sc_extra = wp->w_p_fcs_chars.fold; + wlv.sc_final = NUL; wlv.n_extra = grid->cols - wlv.col; } @@ -1881,15 +1819,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // // The "p_extra" points to the extra stuff that is inserted to // represent special characters (non-printable stuff) and other - // things. When all characters are the same, c_extra is used. - // If c_final is set, it will compulsorily be used at the end. + // things. When all characters are the same, sc_extra is used. + // If sc_final is set, it will compulsorily be used at the end. // "p_extra" must end in a NUL to avoid utfc_ptr2len() reads past // "p_extra[n_extra]". // For the '$' of the 'list' option, n_extra == 1, p_extra == "". if (wlv.n_extra > 0) { - if (wlv.c_extra != NUL || (wlv.n_extra == 1 && wlv.c_final != NUL)) { - mb_c = (wlv.n_extra == 1 && wlv.c_final != NUL) ? wlv.c_final : wlv.c_extra; - mb_schar = schar_from_char(mb_c); + if (wlv.sc_extra != NUL || (wlv.n_extra == 1 && wlv.sc_final != NUL)) { + mb_schar = (wlv.n_extra == 1 && wlv.sc_final != NUL) ? wlv.sc_final : wlv.sc_extra; + mb_c = schar_get_first_codepoint(mb_schar); wlv.n_extra--; } else { assert(wlv.p_extra != NULL); @@ -1923,33 +1861,36 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Only restore search_attr and area_attr after "n_extra" in // the next screen line is also done. if (wlv.n_extra <= 0) { - if (wlv.saved_n_extra <= 0) { - if (search_attr == 0) { - search_attr = saved_search_attr; - saved_search_attr = 0; - } - if (area_attr == 0 && *ptr != NUL) { - area_attr = saved_area_attr; - saved_area_attr = 0; - } - if (decor_attr == 0) { - decor_attr = saved_decor_attr; - saved_decor_attr = 0; - } + if (search_attr == 0) { + search_attr = saved_search_attr; + saved_search_attr = 0; + } + if (area_attr == 0 && *ptr != NUL) { + area_attr = saved_area_attr; + saved_area_attr = 0; + } + if (decor_attr == 0) { + decor_attr = saved_decor_attr; + saved_decor_attr = 0; + } - if (wlv.extra_for_extmark) { - // wlv.extra_attr should be used at this position but not - // any further. - wlv.reset_extra_attr = true; - } + if (wlv.extra_for_extmark) { + // wlv.extra_attr should be used at this position but not + // any further. + wlv.reset_extra_attr = true; } wlv.extra_for_extmark = false; } - } else if (has_fold) { + } else if (wlv.filler_todo > 0) { + // Wait with reading text until filler lines are done. Still need to + // initialize these. + mb_c = ' '; + mb_schar = schar_from_ascii(' '); + } else if (has_foldtext || (has_fold && wlv.col >= grid->cols)) { // skip writing the buffer line itself - mb_c = NUL; + mb_schar = NUL; } else { - char *prev_ptr = ptr; + const char *prev_ptr = ptr; // first byte of next char int c0 = (uint8_t)(*ptr); @@ -1982,8 +1923,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl mb_c = mb_ptr2char_adv((const char **)&wlv.p_extra); mb_schar = schar_from_char(mb_c); wlv.n_extra = (int)strlen(wlv.p_extra); - wlv.c_extra = NUL; - wlv.c_final = NUL; + wlv.sc_extra = NUL; + wlv.sc_final = NUL; if (area_attr == 0 && search_attr == 0) { wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_8); @@ -1996,9 +1937,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // last column; the character is displayed at the start of the // next line. if (wlv.col >= grid->cols - 1 && utf_char2cells(mb_c) == 2) { + mb_schar = schar_from_ascii('>'); mb_c = '>'; mb_l = 1; - mb_schar = schar_from_ascii(mb_c); multi_attr = win_hl_attr(wp, HLF_AT); // Put pointer back so that the character will be // displayed at the start of the next line. @@ -2012,11 +1953,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // the first column. Don't do this for unprintable characters. if (wlv.skip_cells > 0 && mb_l > 1 && wlv.n_extra == 0) { wlv.n_extra = 1; - wlv.c_extra = MB_FILLER_CHAR; - wlv.c_final = NUL; + wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR); + wlv.sc_final = NUL; + mb_schar = schar_from_ascii(' '); mb_c = ' '; mb_l = 1; - mb_schar = schar_from_ascii(mb_c); if (area_attr == 0 && search_attr == 0) { wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_AT); @@ -2027,20 +1968,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl decor_attr = 0; if (extra_check) { - bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0; + const bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0; bool can_spell = !no_plain_buffer; // Get extmark and syntax attributes, unless still at the start of the line // (double-wide char that doesn't fit). - v = (ptr - line); + const int v = (int)(ptr - line); + const ptrdiff_t prev_v = prev_ptr - line; if (has_syntax && v > 0) { // Get the syntax attribute for the character. If there // is an error, disable syntax highlighting. - save_did_emsg = did_emsg; + int save_did_emsg = did_emsg; did_emsg = false; - decor_attr = get_syntax_attr((colnr_T)v - 1, - spv->spv_has_spell ? &can_spell : NULL, false); + decor_attr = get_syntax_attr(v - 1, spv->spv_has_spell ? &can_spell : NULL, false); if (did_emsg) { wp->w_s->b_syn_error = true; @@ -2057,10 +1998,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // have made it invalid. line = ml_get_buf(wp->w_buffer, lnum); ptr = line + v; + prev_ptr = line + prev_v; // no concealing past the end of the line, it interferes // with line highlighting. - syntax_flags = (mb_c == 0) ? 0 : get_syntax_info(&syntax_seqnr); + syntax_flags = (mb_schar == 0) ? 0 : get_syntax_info(&syntax_seqnr); } if (has_decor && v > 0) { @@ -2070,6 +2012,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl can_spell = TRISTATE_TO_BOOL(decor_state.spell, can_spell); } + if (folded_attr) { + decor_attr = hl_combine_attr(folded_attr, decor_attr); + } + if (decor_attr) { if (!attr_pri) { if (wlv.cul_attr) { @@ -2090,28 +2036,28 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Only do this when there is no syntax highlighting, the // @Spell cluster is not used or the current syntax item // contains the @Spell cluster. - v = (ptr - line); - if (spv->spv_has_spell && v >= word_end && v > cur_checked_col) { + int v1 = (int)(ptr - line); + if (spv->spv_has_spell && v1 >= word_end && v1 > cur_checked_col) { spell_attr = 0; // do not calculate cap_col at the end of the line or when // only white space is following - if (mb_c != 0 && (*skipwhite(prev_ptr) != NUL) && can_spell) { + if (mb_schar != 0 && (*skipwhite(prev_ptr) != NUL) && can_spell) { char *p; hlf_T spell_hlf = HLF_COUNT; - v -= mb_l - 1; + v1 -= mb_l - 1; // Use nextline[] if possible, it has the start of the // next line concatenated. if ((prev_ptr - line) - nextlinecol >= 0) { p = nextline + ((prev_ptr - line) - nextlinecol); } else { - p = prev_ptr; + p = (char *)prev_ptr; } spv->spv_cap_col -= (int)(prev_ptr - line); size_t tmplen = spell_check(wp, p, &spell_hlf, &spv->spv_cap_col, spv->spv_unchanged); assert(tmplen <= INT_MAX); int len = (int)tmplen; - word_end = (int)v + len; + word_end = v1 + len; // In Insert mode only highlight a word that // doesn't touch the cursor. @@ -2169,7 +2115,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // // So only allow to linebreak, once we have found chars not in // 'breakat' in the line. - if (wp->w_p_lbr && !wlv.need_lbr && mb_c != NUL + if (wp->w_p_lbr && !wlv.need_lbr && mb_schar != NUL && !vim_isbreak((uint8_t)(*ptr))) { wlv.need_lbr = true; } @@ -2178,13 +2124,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && vim_isbreak(mb_c) && !vim_isbreak((uint8_t)(*ptr))) { int mb_off = utf_head_off(line, ptr - 1); char *p = ptr - (mb_off + 1); - chartabsize_T cts; - init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, p); - // do not want virtual text to be counted here - cts.cts_has_virt_text = false; - wlv.n_extra = win_lbr_chartabsize(&cts, NULL) - 1; - clear_chartabsize_arg(&cts); + CharsizeArg csarg; + // lnum == 0, do not want virtual text to be counted here + CSType cstype = init_charsize_arg(&csarg, wp, 0, line); + wlv.n_extra = win_charsize(cstype, wlv.vcol, p, utf_ptr2CharInfo(p).value, + &csarg).width - 1; if (on_last_col && mb_c != TAB) { // Do not continue search/match highlighting over the @@ -2197,12 +2142,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl wlv.n_extra = tabstop_padding(wlv.vcol, wp->w_buffer->b_p_ts, wp->w_buffer->b_p_vts_array) - 1; } - wlv.c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; - wlv.c_final = NUL; + wlv.sc_extra = schar_from_ascii(mb_off > 0 ? MB_FILLER_CHAR : ' '); + wlv.sc_final = NUL; if (mb_c < 128 && ascii_iswhite(mb_c)) { if (mb_c == TAB) { // See "Tab alignment" below. - FIX_FOR_BOGUSCOLS; + fix_for_boguscols(&wlv); } if (!wp->w_p_list) { mb_c = ' '; @@ -2231,39 +2176,39 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && ptr - line >= leadcol && ptr - line <= trailcol))) { if (in_multispace && wp->w_p_lcs_chars.multispace != NULL) { - mb_c = wp->w_p_lcs_chars.multispace[multispace_pos++]; + mb_schar = wp->w_p_lcs_chars.multispace[multispace_pos++]; if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) { multispace_pos = 0; } } else { - mb_c = (mb_c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp; + mb_schar = (mb_c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp; } wlv.n_attr = 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr - mb_schar = schar_from_char(mb_c); + mb_c = schar_get_first_codepoint(mb_schar); } if (mb_c == ' ' && mb_l == 1 && ((trailcol != MAXCOL && ptr > line + trailcol) || (leadcol != 0 && ptr < line + leadcol))) { if (leadcol != 0 && in_multispace && ptr < line + leadcol && wp->w_p_lcs_chars.leadmultispace != NULL) { - mb_c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++]; + mb_schar = wp->w_p_lcs_chars.leadmultispace[multispace_pos++]; if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) { multispace_pos = 0; } } else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) { - mb_c = wp->w_p_lcs_chars.trail; + mb_schar = wp->w_p_lcs_chars.trail; } else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) { - mb_c = wp->w_p_lcs_chars.lead; + mb_schar = wp->w_p_lcs_chars.lead; } else if (leadcol != 0 && wp->w_p_lcs_chars.space) { - mb_c = wp->w_p_lcs_chars.space; + mb_schar = wp->w_p_lcs_chars.space; } wlv.n_attr = 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr - mb_schar = schar_from_char(mb_c); + mb_c = schar_get_first_codepoint(mb_schar); } } @@ -2289,15 +2234,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl if (!wp->w_p_lbr || !wp->w_p_list) { wlv.n_extra = tab_len; } else { - char *p; int saved_nextra = wlv.n_extra; if (wlv.vcol_off > 0) { // there are characters to conceal tab_len += wlv.vcol_off; } - // boguscols before FIX_FOR_BOGUSCOLS macro from above. - if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0 + // boguscols before fix_for_boguscols() from above. + if (wp->w_p_lcs_chars.tab1 && wlv.old_boguscols > 0 && wlv.n_extra > tab_len) { tab_len += wlv.n_extra - tab_len; } @@ -2306,17 +2250,18 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // If wlv.n_extra > 0, it gives the number of chars // to use for a tab, else we need to calculate the // width for a tab. - int tab2_len = utf_char2len(wp->w_p_lcs_chars.tab2); - int len = tab_len * tab2_len; + size_t tab2_len = schar_len(wp->w_p_lcs_chars.tab2); + size_t len = (size_t)tab_len * tab2_len; if (wp->w_p_lcs_chars.tab3) { - len += utf_char2len(wp->w_p_lcs_chars.tab3) - tab2_len; + len += schar_len(wp->w_p_lcs_chars.tab3) - tab2_len; } if (wlv.n_extra > 0) { - len += wlv.n_extra - tab_len; + len += (size_t)(wlv.n_extra - tab_len); } - mb_c = wp->w_p_lcs_chars.tab1; - p = get_extra_buf((size_t)len + 1); - memset(p, ' ', (size_t)len); + mb_schar = wp->w_p_lcs_chars.tab1; + mb_c = schar_get_first_codepoint(mb_schar); + char *p = get_extra_buf(len + 1); + memset(p, ' ', len); p[len] = NUL; wlv.p_extra = p; for (int i = 0; i < tab_len; i++) { @@ -2324,17 +2269,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl tab_len = i; break; } - int lcs = wp->w_p_lcs_chars.tab2; + schar_T lcs = wp->w_p_lcs_chars.tab2; // if tab3 is given, use it for the last char if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) { lcs = wp->w_p_lcs_chars.tab3; } - p += utf_char2bytes(lcs, p); - wlv.n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0); + size_t slen = schar_get_adv(&p, lcs); + wlv.n_extra += (int)slen - (saved_nextra > 0 ? 1 : 0); } - // n_extra will be increased by FIX_FOX_BOGUSCOLS + // n_extra will be increased by fix_for_boguscols() // macro below, so need to adjust for that here if (wlv.vcol_off > 0) { wlv.n_extra -= wlv.vcol_off; @@ -2351,7 +2296,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // vcol_off and boguscols accumulated so far in the // line. Note that the tab can be longer than // 'tabstop' when there are concealed characters. - FIX_FOR_BOGUSCOLS; + fix_for_boguscols(&wlv); // Make sure, the highlighting for the tab char will be // correctly set further below (effectively reverts the @@ -2363,24 +2308,24 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } if (wp->w_p_list) { - mb_c = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3) - ? wp->w_p_lcs_chars.tab3 : wp->w_p_lcs_chars.tab1; + mb_schar = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3) + ? wp->w_p_lcs_chars.tab3 : wp->w_p_lcs_chars.tab1; if (wp->w_p_lbr && wlv.p_extra != NULL && *wlv.p_extra != NUL) { - wlv.c_extra = NUL; // using p_extra from above + wlv.sc_extra = NUL; // using p_extra from above } else { - wlv.c_extra = wp->w_p_lcs_chars.tab2; + wlv.sc_extra = wp->w_p_lcs_chars.tab2; } - wlv.c_final = wp->w_p_lcs_chars.tab3; + wlv.sc_final = wp->w_p_lcs_chars.tab3; wlv.n_attr = tab_len + 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr } else { - wlv.c_final = NUL; - wlv.c_extra = ' '; - mb_c = ' '; + wlv.sc_final = NUL; + wlv.sc_extra = schar_from_ascii(' '); + mb_schar = schar_from_ascii(' '); } - mb_schar = schar_from_char(mb_c); - } else if (mb_c == NUL + mb_c = schar_get_first_codepoint(mb_schar); + } else if (mb_schar == NUL && (wp->w_p_list || ((wlv.fromcol >= 0 || fromcol_prev >= 0) && wlv.tocol > wlv.vcol @@ -2389,7 +2334,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && !(noinvcur && lnum == wp->w_cursor.lnum && wlv.vcol == wp->w_virtcol))) - && lcs_eol_one > 0) { + && lcs_eol_todo && lcs_eol != NUL) { // Display a '$' after the line or highlight an extra // character if the line break is included. // For a diff line the highlighting continues after the "$". @@ -2399,21 +2344,21 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // In virtualedit, visual selections may extend beyond end of line if (!(area_highlighting && virtual_active() && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol)) { - wlv.p_extra = at_end_str; + wlv.p_extra = ""; } wlv.n_extra = 0; } if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) { - mb_c = wp->w_p_lcs_chars.eol; + mb_schar = wp->w_p_lcs_chars.eol; } else { - mb_c = ' '; + mb_schar = schar_from_ascii(' '); } - lcs_eol_one = -1; + lcs_eol_todo = false; ptr--; // put it back at the NUL wlv.extra_attr = win_hl_attr(wp, HLF_AT); wlv.n_attr = 1; - mb_schar = schar_from_char(mb_c); - } else if (mb_c != NUL) { + mb_c = schar_get_first_codepoint(mb_schar); + } else if (mb_schar != NUL) { wlv.p_extra = transchar_buf(wp->w_buffer, mb_c); if (wlv.n_extra == 0) { wlv.n_extra = byte2cells(mb_c) - 1; @@ -2421,17 +2366,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl if ((dy_flags & DY_UHEX) && wp->w_p_rl) { rl_mirror_ascii(wlv.p_extra, NULL); // reverse "<12>" } - wlv.c_extra = NUL; - wlv.c_final = NUL; + wlv.sc_extra = NUL; + wlv.sc_final = NUL; if (wp->w_p_lbr) { - char *p; - mb_c = (uint8_t)(*wlv.p_extra); - p = get_extra_buf((size_t)wlv.n_extra + 1); + char *p = get_extra_buf((size_t)wlv.n_extra + 1); memset(p, ' ', (size_t)wlv.n_extra); - strncpy(p, // NOLINT(runtime/printf) - wlv.p_extra + 1, - (size_t)strlen(wlv.p_extra) - 1); + memcpy(p, wlv.p_extra + 1, strlen(wlv.p_extra) - 1); p[wlv.n_extra] = NUL; wlv.p_extra = p; } else { @@ -2459,7 +2400,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0) && !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { wlv.char_attr = conceal_attr; - bool is_conceal_char = false; if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0) || has_match_conc > 1 || decor_conceal > 1) && (syn_get_sub_char() != NUL @@ -2470,21 +2410,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // First time at this concealed item: display one // character. if (has_match_conc && match_conc) { - mb_c = match_conc; + mb_schar = schar_from_char(match_conc); } else if (decor_conceal && decor_state.conceal_char) { mb_schar = decor_state.conceal_char; - mb_c = schar_get_first_codepoint(mb_schar); - is_conceal_char = true; if (decor_state.conceal_attr) { wlv.char_attr = decor_state.conceal_attr; } } else if (syn_get_sub_char() != NUL) { - mb_c = syn_get_sub_char(); + mb_schar = schar_from_char(syn_get_sub_char()); } else if (wp->w_p_lcs_chars.conceal != NUL) { - mb_c = wp->w_p_lcs_chars.conceal; + mb_schar = wp->w_p_lcs_chars.conceal; } else { - mb_c = ' '; + mb_schar = schar_from_ascii(' '); } + mb_c = schar_get_first_codepoint(mb_schar); prev_syntax_id = syntax_seqnr; @@ -2492,7 +2431,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl wlv.vcol_off += wlv.n_extra; } wlv.vcol += wlv.n_extra; - if (wp->w_p_wrap && wlv.n_extra > 0) { + if (is_wrapped && wlv.n_extra > 0) { wlv.boguscols += wlv.n_extra; wlv.col += wlv.n_extra; } @@ -2502,9 +2441,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl is_concealing = true; wlv.skip_cells = 1; } - if (!is_conceal_char) { - mb_schar = schar_from_char(mb_c); - } } else { prev_syntax_id = 0; is_concealing = false; @@ -2518,7 +2454,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // In the cursor line and we may be concealing characters: correct // the cursor column when we reach its position. - if (!did_wcol && wlv.draw_state == WL_LINE + if (!did_wcol && wp == curwin && lnum == wp->w_cursor.lnum && conceal_cursor_line(wp) && (int)wp->w_virtcol <= wlv.vcol + wlv.skip_cells) { @@ -2529,7 +2465,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } // Don't override visual selection highlighting. - if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) { + if (wlv.n_attr > 0 && !search_attr_from_match) { wlv.char_attr = hl_combine_attr(wlv.char_attr, wlv.extra_attr); if (wlv.reset_extra_attr) { wlv.reset_extra_attr = false; @@ -2546,37 +2482,36 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && wp->w_p_list && (wp->w_p_wrap ? (wp->w_skipcol > 0 && wlv.row == 0) : wp->w_leftcol > 0) && wlv.filler_todo <= 0 - && wlv.draw_state > WL_STC - && mb_c != NUL) { - mb_c = wp->w_p_lcs_chars.prec; + && mb_schar != NUL) { + mb_schar = wp->w_p_lcs_chars.prec; lcs_prec_todo = NUL; if (utf_char2cells(mb_c) > 1) { // Double-width character being overwritten by the "precedes" // character, need to fill up half the character. - wlv.c_extra = MB_FILLER_CHAR; - wlv.c_final = NUL; + wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR); + wlv.sc_final = NUL; wlv.n_extra = 1; wlv.n_attr = 2; wlv.extra_attr = win_hl_attr(wp, HLF_AT); } - mb_schar = schar_from_char(mb_c); + mb_c = schar_get_first_codepoint(mb_schar); saved_attr3 = wlv.char_attr; // save current attr wlv.char_attr = win_hl_attr(wp, HLF_AT); // overwriting char_attr n_attr3 = 1; } // At end of the text line or just after the last character. - if (mb_c == NUL && eol_hl_off == 0) { + if (mb_schar == NUL && eol_hl_off == 0) { // flag to indicate whether prevcol equals startcol of search_hl or // one of the matches - bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &screen_search_hl, - (colnr_T)(ptr - line) - 1); + const bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &screen_search_hl, + (colnr_T)(ptr - line) - 1); // Invert at least one char, used for Visual and empty line or // highlight match at end of line. If it's beyond the last // char on the screen, just overwrite that one (tricky!) Not // needed when a '$' was displayed for 'list'. - if (wp->w_p_lcs_chars.eol == lcs_eol_one + if (lcs_eol_todo && ((area_attr != 0 && wlv.vcol == wlv.fromcol && (VIsual_mode != Ctrl_V || lnum == VIsual.lnum @@ -2597,7 +2532,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Add a blank character to highlight. linebuf_char[wlv.off] = schar_from_ascii(' '); } - if (area_attr == 0 && !has_fold) { + if (area_attr == 0 && !has_foldtext) { // Use attributes from match with highest priority among // 'search_hl' and the match list. get_search_match_hl(wp, @@ -2606,10 +2541,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl &wlv.char_attr); } - int eol_attr = wlv.char_attr; - if (wlv.cul_attr) { - eol_attr = hl_combine_attr(wlv.cul_attr, eol_attr); - } + const int eol_attr = wlv.cul_attr + ? hl_combine_attr(wlv.cul_attr, wlv.char_attr) + : wlv.char_attr; + linebuf_attr[wlv.off] = eol_attr; linebuf_vcol[wlv.off] = MAXCOL; wlv.col++; @@ -2620,70 +2555,48 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } // At end of the text line. - if (mb_c == NUL) { + if (mb_schar == NUL) { // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line. - if (wp->w_p_wrap) { - v = wlv.startrow == 0 ? wp->w_skipcol : 0; - } else { - v = wp->w_leftcol; - } // check if line ends before left margin - if (wlv.vcol < v + wlv.col - win_col_off(wp)) { - wlv.vcol = (colnr_T)v + wlv.col - win_col_off(wp); + if (wlv.vcol < start_col + wlv.col - win_col_off(wp)) { + wlv.vcol = start_col + wlv.col - win_col_off(wp); } // Get rid of the boguscols now, we want to draw until the right // edge for 'cursorcolumn'. wlv.col -= wlv.boguscols; wlv.boguscols = 0; - if (draw_color_col) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); - } + advance_color_col(&wlv, vcol_hlc(wlv)); bool has_virttext = false; // Make sure alignment is the same regardless // if listchars=eol:X is used or not. - int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0 - ? 1 : 0); + const int eol_skip = (lcs_eol_todo && eol_hl_off == 0 ? 1 : 0); if (has_decor) { has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip); } if (((wp->w_p_cuc - && wp->w_virtcol >= VCOL_HLC - eol_hl_off - && wp->w_virtcol < grid->cols * (ptrdiff_t)(wlv.row - startrow + 1) + v + && wp->w_virtcol >= vcol_hlc(wlv) - eol_hl_off + && wp->w_virtcol < grid->cols * (ptrdiff_t)(wlv.row - startrow + 1) + start_col && lnum != wp->w_cursor.lnum) - || draw_color_col || wlv.line_attr_lowprio || wlv.line_attr + || wlv.color_cols || wlv.line_attr_lowprio || wlv.line_attr || wlv.diff_hlf != 0 || has_virttext)) { - int rightmost_vcol = 0; - - if (wp->w_p_cuc) { - rightmost_vcol = wp->w_virtcol; - } - - if (draw_color_col) { - // determine rightmost colorcolumn to possibly draw - for (int i = 0; color_cols[i] >= 0; i++) { - if (rightmost_vcol < color_cols[i]) { - rightmost_vcol = color_cols[i]; - } - } - } + int rightmost_vcol = get_rightmost_vcol(wp, wlv.color_cols); + const int cuc_attr = win_hl_attr(wp, HLF_CUC); + const int mc_attr = win_hl_attr(wp, HLF_MC); - int cuc_attr = win_hl_attr(wp, HLF_CUC); - int mc_attr = win_hl_attr(wp, HLF_MC); - - int diff_attr = 0; if (wlv.diff_hlf == HLF_TXD) { wlv.diff_hlf = HLF_CHD; } - if (wlv.diff_hlf != 0) { - diff_attr = win_hl_attr(wp, (int)wlv.diff_hlf); - } - int base_attr = hl_combine_attr(wlv.line_attr_lowprio, diff_attr); + const int diff_attr = wlv.diff_hlf != 0 + ? win_hl_attr(wp, (int)wlv.diff_hlf) + : 0; + + const int base_attr = hl_combine_attr(wlv.line_attr_lowprio, diff_attr); if (base_attr || wlv.line_attr || has_virttext) { rightmost_vcol = INT_MAX; } @@ -2692,15 +2605,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl linebuf_char[wlv.off] = schar_from_ascii(' '); linebuf_vcol[wlv.off] = MAXCOL; wlv.col++; - if (draw_color_col) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); - } + advance_color_col(&wlv, vcol_hlc(wlv)); int col_attr = base_attr; - if (wp->w_p_cuc && VCOL_HLC == wp->w_virtcol) { + if (wp->w_p_cuc && vcol_hlc(wlv) == wp->w_virtcol) { col_attr = cuc_attr; - } else if (draw_color_col && VCOL_HLC == *color_cols) { + } else if (wlv.color_cols && vcol_hlc(wlv) == *wlv.color_cols) { col_attr = hl_combine_attr(wlv.line_attr_lowprio, mc_attr); } @@ -2709,7 +2620,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl linebuf_attr[wlv.off] = col_attr; wlv.off++; - if (VCOL_HLC >= rightmost_vcol) { + if (vcol_hlc(wlv) >= rightmost_vcol) { break; } @@ -2746,73 +2657,72 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW); conceal_cursor_used = conceal_cursor_line(curwin); } + + // When the window is too narrow draw all "@" lines. + if (leftcols_width >= wp->w_grid.cols && is_wrapped) { + win_draw_end(wp, schar_from_ascii('@'), true, wlv.row, wp->w_grid.rows, HLF_AT); + set_empty_rows(wp, wlv.row); + wlv.row = endrow; + } + break; } // Show "extends" character from 'listchars' if beyond the line end and // 'list' is set. + // Don't show this with 'wrap' as the line can't be scrolled horizontally. if (wp->w_p_lcs_chars.ext != NUL - && wlv.draw_state == WL_LINE && wp->w_p_list && !wp->w_p_wrap && wlv.filler_todo <= 0 && wlv.col == grid->cols - 1 - && !has_fold) { - if (has_decor && *ptr == NUL && lcs_eol_one == 0) { + && !has_foldtext) { + if (has_decor && *ptr == NUL && lcs_eol == 0 && lcs_eol_todo) { // Tricky: there might be a virtual text just _after_ the last char - decor_redraw_col(wp, (colnr_T)v, wlv.off, false, &decor_state); + decor_redraw_col(wp, (colnr_T)(ptr - line), -1, false, &decor_state); } if (*ptr != NUL - || lcs_eol_one > 0 - || (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)) - || has_more_inline_virt(&wlv, v)) { - mb_c = wp->w_p_lcs_chars.ext; + || (lcs_eol > 0 && lcs_eol_todo) + || (wlv.n_extra > 0 && (wlv.sc_extra != NUL || *wlv.p_extra != NUL)) + || (may_have_inline_virt && has_more_inline_virt(&wlv, ptr - line))) { + mb_schar = wp->w_p_lcs_chars.ext; wlv.char_attr = win_hl_attr(wp, HLF_AT); - mb_schar = schar_from_char(mb_c); + mb_c = schar_get_first_codepoint(mb_schar); } } - // advance to the next 'colorcolumn' - if (draw_color_col) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); - } + advance_color_col(&wlv, vcol_hlc(wlv)); // Highlight the cursor column if 'cursorcolumn' is set. But don't // highlight the cursor position itself. // Also highlight the 'colorcolumn' if it is different than // 'cursorcolumn' - // Also highlight the 'colorcolumn' if 'breakindent' and/or 'showbreak' - // options are set vcol_save_attr = -1; - if ((wlv.draw_state == WL_LINE - || wlv.draw_state == WL_BRI - || wlv.draw_state == WL_SBR) - && !lnum_in_visual_area + if (!lnum_in_visual_area && search_attr == 0 && area_attr == 0 && wlv.filler_todo <= 0) { - if (wp->w_p_cuc && VCOL_HLC == wp->w_virtcol + if (wp->w_p_cuc && vcol_hlc(wlv) == wp->w_virtcol && lnum != wp->w_cursor.lnum) { vcol_save_attr = wlv.char_attr; wlv.char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), wlv.char_attr); - } else if (draw_color_col && VCOL_HLC == *color_cols) { + } else if (wlv.color_cols && vcol_hlc(wlv) == *wlv.color_cols) { vcol_save_attr = wlv.char_attr; wlv.char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), wlv.char_attr); } } // Apply lowest-priority line attr now, so everything can override it. - if (wlv.draw_state == WL_LINE) { - wlv.char_attr = hl_combine_attr(wlv.line_attr_lowprio, wlv.char_attr); - } + wlv.char_attr = hl_combine_attr(wlv.line_attr_lowprio, wlv.char_attr); - if (wlv.draw_state == WL_LINE) { - vcol_prev = wlv.vcol; - } + vcol_prev = wlv.vcol; // Store character to be displayed. // Skip characters that are left of the screen for 'nowrap'. - if (wlv.draw_state < WL_LINE || wlv.skip_cells <= 0) { + if (wlv.filler_todo > 0) { + // TODO(bfredl): the main render loop should get called also with the virtual + // lines chunks, so we get line wrapping and other Nice Things. + } else if (wlv.skip_cells <= 0) { // Store the character. linebuf_char[wlv.off] = mb_schar; if (multi_attr) { @@ -2822,18 +2732,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl linebuf_attr[wlv.off] = wlv.char_attr; } - if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) { - linebuf_vcol[wlv.off] = wlv.vcol; - } else if (wlv.draw_state == WL_FOLD) { - if (wlv.n_closing > 0) { - linebuf_vcol[wlv.off] = -3; - wlv.n_closing--; - } else { - linebuf_vcol[wlv.off] = -2; - } - } else { - linebuf_vcol[wlv.off] = -1; - } + linebuf_vcol[wlv.off] = wlv.vcol; if (utf_char2cells(mb_c) > 1) { // Need to fill two screen columns. @@ -2843,11 +2742,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl linebuf_char[wlv.off] = 0; linebuf_attr[wlv.off] = linebuf_attr[wlv.off - 1]; - if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) { - linebuf_vcol[wlv.off] = ++wlv.vcol; - } else { - linebuf_vcol[wlv.off] = -1; - } + linebuf_vcol[wlv.off] = ++wlv.vcol; // When "wlv.tocol" is halfway through a character, set it to the end // of the character, otherwise highlighting won't stop. @@ -2863,7 +2758,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl if (wlv.n_extra > 0) { wlv.vcol_off += wlv.n_extra; } - if (wp->w_p_wrap) { + if (is_wrapped) { // Special voodoo required if 'wrap' is on. // // Advance the column indicator to force the line @@ -2903,15 +2798,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } // The skipped cells need to be accounted for in vcol. - if (wlv.draw_state > WL_STC && wlv.skipped_cells > 0) { + if (wlv.skipped_cells > 0) { wlv.vcol += wlv.skipped_cells; wlv.skipped_cells = 0; } // Only advance the "wlv.vcol" when after the 'number' or // 'relativenumber' column. - if (wlv.draw_state > WL_STC - && wlv.filler_todo <= 0) { + if (wlv.filler_todo <= 0) { wlv.vcol++; } @@ -2920,46 +2814,45 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } // restore attributes after "predeces" in 'listchars' - if (wlv.draw_state > WL_STC && n_attr3 > 0 && --n_attr3 == 0) { + if (n_attr3 > 0 && --n_attr3 == 0) { wlv.char_attr = saved_attr3; } // restore attributes after last 'listchars' or 'number' char - if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && --wlv.n_attr == 0) { + if (wlv.n_attr > 0 && --wlv.n_attr == 0) { wlv.char_attr = saved_attr2; } if (has_decor && wlv.filler_todo <= 0 && wlv.col >= grid->cols) { // At the end of screen line: might need to peek for decorations just after // this position. - if (!has_fold && wp->w_p_wrap && wlv.n_extra == 0) { - decor_redraw_col(wp, (int)(ptr - line), -3, false, &decor_state); + if (is_wrapped && wlv.n_extra == 0) { + decor_redraw_col(wp, (colnr_T)(ptr - line), -3, false, &decor_state); // Check position/hiding of virtual text again on next screen line. decor_need_recheck = true; - } else if (has_fold || !wp->w_p_wrap) { + } else if (!is_wrapped) { // Without wrapping, we might need to display right_align and win_col // virt_text for the entire text line. + decor_recheck_draw_col(-1, true, &decor_state); decor_redraw_col(wp, MAXCOL, -1, true, &decor_state); } } // At end of screen line and there is more to come: Display the line // so far. If there is no more to display it is caught above. - if (wlv.col >= grid->cols && (!has_fold || virt_line_offset >= 0) - && (wlv.draw_state != WL_LINE - || *ptr != NUL + if (wlv.col >= grid->cols && (!has_foldtext || virt_line_offset >= 0) + && (*ptr != NUL || wlv.filler_todo > 0 - || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL - && wlv.p_extra != at_end_str) - || (wlv.n_extra != 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)) - || has_more_inline_virt(&wlv, v))) { - bool wrap = wp->w_p_wrap // Wrapping enabled. - && wlv.filler_todo <= 0 // Not drawing diff filler lines. - && lcs_eol_one != -1 // Haven't printed the lcs_eol character. - && wlv.row != endrow - 1 // Not the last line being displayed. - && (grid->cols == Columns // Window spans the width of the screen, - || ui_has(kUIMultigrid)) // or has dedicated grid. - && !wp->w_p_rl; // Not right-to-left. + || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL && lcs_eol_todo) + || (wlv.n_extra != 0 && (wlv.sc_extra != NUL || *wlv.p_extra != NUL)) + || (may_have_inline_virt && has_more_inline_virt(&wlv, ptr - line)))) { + const bool wrap = is_wrapped // Wrapping enabled (not a folded line). + && wlv.filler_todo <= 0 // Not drawing diff filler lines. + && lcs_eol_todo // Haven't printed the lcs_eol character. + && wlv.row != endrow - 1 // Not the last line being displayed. + && (grid->cols == Columns // Window spans the width of the screen, + || ui_has(kUIMultigrid)) // or has dedicated grid. + && !wp->w_p_rl; // Not right-to-left. int draw_col = wlv.col - wlv.boguscols; if (virt_line_offset >= 0) { @@ -2984,15 +2877,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl wlv.vcol_off = 0; wlv.row++; - // When not wrapping and finished diff lines, or when displayed - // '$' and highlighting until last column, break here. - if ((!wp->w_p_wrap && wlv.filler_todo <= 0) || lcs_eol_one == -1) { + // When not wrapping and finished diff lines, break here. + if (!is_wrapped && wlv.filler_todo <= 0) { break; } // When the window is too narrow draw all "@" lines. - if (wlv.draw_state != WL_LINE && wlv.filler_todo <= 0) { - win_draw_end(wp, '@', ' ', true, wlv.row, wp->w_grid.rows, HLF_AT); + if (wlv.col <= leftcols_width) { + win_draw_end(wp, schar_from_ascii('@'), true, wlv.row, wp->w_grid.rows, HLF_AT); set_empty_rows(wp, wlv.row); wlv.row = endrow; } @@ -3003,18 +2895,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl break; } - win_line_start(wp, &wlv, true); + win_line_start(wp, &wlv); + draw_cols = true; lcs_prec_todo = wp->w_p_lcs_chars.prec; if (wlv.filler_todo <= 0) { wlv.need_showbreak = true; } - if (statuscol.draw) { - if (vim_strchr(p_cpo, CPO_NUMCOL) && wlv.row > startrow + wlv.filler_lines) { - statuscol.draw = false; // don't draw status column if "n" is in 'cpo' - } else { - statuscol.textp = NULL; // re-evaluate with new v:virtnum - } + if (statuscol.draw && vim_strchr(p_cpo, CPO_NUMCOL) + && wlv.row > startrow + wlv.filler_lines) { + statuscol.draw = false; // don't draw status column if "n" is in 'cpo' } wlv.filler_todo--; virt_line_offset = -1; @@ -3040,8 +2930,7 @@ static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clea int start_col = 0; if (wp->w_p_rl) { - linebuf_mirror(&start_col, &clear_width, grid->cols); - endcol = grid->cols - 1 - endcol; + linebuf_mirror(&start_col, &endcol, &clear_width, grid->cols); } // Take care of putting "<<<" on the first line for 'smoothscroll'. diff --git a/src/nvim/drawline.h b/src/nvim/drawline.h index 5a7f220a13..9112deddb3 100644 --- a/src/nvim/drawline.h +++ b/src/nvim/drawline.h @@ -4,14 +4,12 @@ #include <stdint.h> #include "klib/kvec.h" -#include "nvim/decoration_provider.h" -#include "nvim/fold_defs.h" +#include "nvim/fold_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" -// Maximum columns for terminal highlight attributes -#define TERM_ATTRS_MAX 1024 +enum { TERM_ATTRS_MAX = 1024, }; ///< Maximum columns for terminal highlight attributes typedef struct { NS ns_id; @@ -23,7 +21,7 @@ EXTERN kvec_t(WinExtmark) win_extmark_arr INIT( = KV_INITIAL_VALUE); EXTERN bool conceal_cursor_used INIT( = false); -// Spell checking variables passed from win_update() to win_line(). +/// Spell checking variables passed from win_update() to win_line(). typedef struct { bool spv_has_spell; ///< drawn window has spell checking bool spv_unchanged; ///< not updating for changed text diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 6cc623cb72..145229bacc 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -1,5 +1,5 @@ // drawscreen.c: Code for updating all the windows on the screen. -// This is the top level, drawline.c is the middle and grid.c/screen.c the lower level. +// This is the top level, drawline.c is the middle and grid.c the lower level. // update_screen() is the function that updates all windows and status lines. // It is called from the main loop when must_redraw is non-zero. It may be @@ -59,15 +59,16 @@ #include <stdlib.h> #include <string.h> -#include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cursor.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/digraph.h" @@ -76,31 +77,36 @@ #include "nvim/eval.h" #include "nvim/ex_getln.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" +#include "nvim/fold_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/insexpand.h" +#include "nvim/marktree_defs.h" #include "nvim/match.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/option.h" #include "nvim/option_vars.h" +#include "nvim/os/os_defs.h" #include "nvim/plines.h" #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/regexp.h" #include "nvim/search.h" -#include "nvim/sign_defs.h" #include "nvim/spell.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" @@ -108,10 +114,13 @@ #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" +#include "nvim/ui_defs.h" #include "nvim/version.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "klib/kvec.h" + /// corner value flags for hsep_connected and vsep_connected typedef enum { WC_TOP_LEFT = 0, @@ -151,8 +160,8 @@ void conceal_check_cursor_line(void) /// There may be some time between setting Rows and Columns and (re)allocating /// default_grid arrays. This happens when starting up and when /// (manually) changing the screen size. Always use default_grid.rows and -/// default_grid.Columns to access items in default_grid.chars[]. Use Rows -/// and Columns for positioning text etc. where the final size of the screen is +/// default_grid.cols to access items in default_grid.chars[]. Use Rows and +/// Columns for positioning text etc. where the final size of the screen is /// needed. /// /// @return whether resizing has been done @@ -171,12 +180,8 @@ bool default_grid_alloc(void) // Allocation of the screen buffers is done only when the size changes and // when Rows and Columns have been set and we have started doing full // screen stuff. - if ((default_grid.chars != NULL - && Rows == default_grid.rows - && Columns == default_grid.cols) - || Rows == 0 - || Columns == 0 - || (!full_screen && default_grid.chars == NULL)) { + if ((default_grid.chars != NULL && Rows == default_grid.rows && Columns == default_grid.cols) + || Rows == 0 || Columns == 0 || (!full_screen && default_grid.chars == NULL)) { resizing = false; return false; } @@ -194,8 +199,8 @@ bool default_grid_alloc(void) grid_alloc(&default_grid, Rows, Columns, true, true); stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size); - tab_page_click_defs = stl_alloc_click_defs(tab_page_click_defs, Columns, - &tab_page_click_defs_size); + tab_page_click_defs + = stl_alloc_click_defs(tab_page_click_defs, Columns, &tab_page_click_defs_size); default_grid.comp_height = Rows; default_grid.comp_width = Columns; @@ -218,8 +223,7 @@ void screenclear(void) // blank out the default grid for (int i = 0; i < default_grid.rows; i++) { - grid_clear_line(&default_grid, default_grid.line_offset[i], - default_grid.cols, true); + grid_clear_line(&default_grid, default_grid.line_offset[i], default_grid.cols, true); } ui_call_grid_clear(1); // clear the display @@ -266,7 +270,7 @@ void screen_resize(int width, int height) return; } - if (width < 0 || height < 0) { // just checking... + if (width < 0 || height < 0) { // just checking... return; } @@ -307,9 +311,9 @@ void screen_resize(int width, int height) RedrawingDisabled++; - win_new_screensize(); // fit the windows in the new sized screen + win_new_screensize(); // fit the windows in the new sized screen - comp_col(); // recompute columns for shown command and ruler + comp_col(); // recompute columns for shown command and ruler RedrawingDisabled--; @@ -403,8 +407,7 @@ void check_screensize(void) /// Return true if redrawing should currently be done. bool redrawing(void) { - return !RedrawingDisabled - && !(p_lz && char_avail() && !KeyTyped && !do_redraw); + return !RedrawingDisabled && !(p_lz && char_avail() && !KeyTyped && !do_redraw); } /// Redraw the parts of the screen that is marked for redraw. @@ -443,7 +446,7 @@ int update_screen(void) // will be redrawn later or in win_update(). must_redraw = 0; - updating_screen = 1; + updating_screen = true; display_tick++; // let syntax code know we're in a next round of // display updating @@ -475,8 +478,7 @@ int update_screen(void) if (msg_grid.chars) { // non-displayed part of msg_grid is considered invalid. for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) { - grid_clear_line(&msg_grid, msg_grid.line_offset[i], - msg_grid.cols, i < p_ch); + grid_clear_line(&msg_grid, msg_grid.line_offset[i], msg_grid.cols, i < p_ch); } } msg_grid.throttled = false; @@ -486,8 +488,7 @@ int update_screen(void) if (type == UPD_NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) { was_invalidated = ui_comp_set_screen_valid(false); for (int i = valid; i < Rows - p_ch; i++) { - grid_clear_line(&default_grid, default_grid.line_offset[i], - Columns, false); + grid_clear_line(&default_grid, default_grid.line_offset[i], Columns, false); } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_floating) { @@ -533,10 +534,10 @@ int update_screen(void) hl_changed = true; } - if (type == UPD_CLEAR) { // first clear screen - screenclear(); // will reset clear_cmdline - // and set UPD_NOT_VALID for each window - cmdline_screen_cleared(); // clear external cmdline state + if (type == UPD_CLEAR) { // first clear screen + screenclear(); // will reset clear_cmdline + // and set UPD_NOT_VALID for each window + cmdline_screen_cleared(); // clear external cmdline state type = UPD_NOT_VALID; // must_redraw may be set indirectly, avoid another redraw later must_redraw = 0; @@ -547,13 +548,12 @@ int update_screen(void) // might need to clear space on default_grid for the message area. if (type == UPD_NOT_VALID && clear_cmdline && !ui_has(kUIMessages)) { - grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0); + grid_clear(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, 0); } ui_comp_set_screen_valid(true); - DecorProviders providers; - decor_providers_start(&providers); + decor_providers_start(); // "start" callback could have changed highlights for global elements if (win_check_ns_hl(NULL)) { @@ -561,7 +561,7 @@ int update_screen(void) redraw_tabline = true; } - if (clear_cmdline) { // going to clear cmdline (done below) + if (clear_cmdline) { // going to clear cmdline (done below) msg_check_for_delay(false); } @@ -570,8 +570,9 @@ int update_screen(void) // TODO(bfredl): special casing curwin here is SÅ JÄVLA BULL. // Either this should be done for all windows or not at all. if (curwin->w_redr_type < UPD_NOT_VALID - && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu || *curwin->w_p_stc) - ? number_width(curwin) : 0)) { + && curwin->w_nrwidth + != ((curwin->w_p_nu || curwin->w_p_rnu || *curwin->w_p_stc) ? number_width(curwin) + : 0)) { curwin->w_redr_type = UPD_NOT_VALID; } @@ -593,26 +594,16 @@ int update_screen(void) buf_T *buf = wp->w_buffer; if (buf->b_mod_set) { - if (buf->b_mod_tick_syn < display_tick - && syntax_present(wp)) { + if (buf->b_mod_tick_syn < display_tick && syntax_present(wp)) { syn_stack_apply_changes(buf); buf->b_mod_tick_syn = display_tick; } if (buf->b_mod_tick_decor < display_tick) { - decor_providers_invoke_buf(buf, &providers); + decor_providers_invoke_buf(buf); buf->b_mod_tick_decor = display_tick; } } - - // Reset 'statuscolumn' if there is no dedicated signcolumn but it is invalid. - if (*wp->w_p_stc != NUL && wp->w_minscwidth <= SCL_NO - && (wp->w_buffer->b_signcols.invalid_bot || !wp->w_buffer->b_signcols.sentinel)) { - wp->w_nrwidth_line_count = 0; - wp->w_valid &= ~VALID_WCOL; - wp->w_redr_type = UPD_NOT_VALID; - wp->w_buffer->b_signcols.invalid_bot = 0; - } } // Go from top to bottom through the windows, redrawing the ones that need it. @@ -639,7 +630,7 @@ int update_screen(void) did_one = true; start_search_hl(); } - win_update(wp, &providers); + win_update(wp); } // redraw status line and window bar after the window to minimize cursor movement @@ -659,13 +650,14 @@ int update_screen(void) win_check_ns_hl(NULL); - // Reset b_mod_set flags. Going through all windows is probably faster - // than going through all buffers (there could be many buffers). + // Reset b_mod_set and b_signcols.resized flags. Going through all windows is + // probably faster than going through all buffers (there could be many buffers). FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { wp->w_buffer->b_mod_set = false; + wp->w_buffer->b_signcols.resized = false; } - updating_screen = 0; + updating_screen = false; // Clear or redraw the command line. Done last, because scrolling may // mess up the command line. @@ -679,8 +671,7 @@ int update_screen(void) } did_intro = true; - decor_providers_invoke_end(&providers); - kvi_destroy(providers); + decor_providers_invoke_end(); // either cmdline is cleared, not drawn or mode is last drawn cmdline_was_last_drawn = false; @@ -740,7 +731,7 @@ int win_get_bordertext_col(int total_col, int text_width, AlignTextPos align) static void win_redr_border(win_T *wp) { wp->w_redr_border = false; - if (!(wp->w_floating && wp->w_float_config.border)) { + if (!(wp->w_floating && wp->w_config.border)) { return; } @@ -748,9 +739,9 @@ static void win_redr_border(win_T *wp) schar_T chars[8]; for (int i = 0; i < 8; i++) { - chars[i] = schar_from_str(wp->w_float_config.border_chars[i]); + chars[i] = schar_from_str(wp->w_config.border_chars[i]); } - int *attrs = wp->w_float_config.border_attr; + int *attrs = wp->w_config.border_attr; int *adj = wp->w_border_adj; int irow = wp->w_height_inner + wp->w_winbar_height; @@ -766,10 +757,10 @@ static void win_redr_border(win_T *wp) grid_line_put_schar(i + adj[3], chars[1], attrs[1]); } - if (wp->w_float_config.title) { - int title_col = win_get_bordertext_col(icol, wp->w_float_config.title_width, - wp->w_float_config.title_pos); - win_redr_bordertext(wp, wp->w_float_config.title_chunks, title_col); + if (wp->w_config.title) { + int title_col + = win_get_bordertext_col(icol, wp->w_config.title_width, wp->w_config.title_pos); + win_redr_bordertext(wp, wp->w_config.title_chunks, title_col); } if (adj[1]) { grid_line_put_schar(icol + adj[3], chars[2], attrs[2]); @@ -802,10 +793,10 @@ static void win_redr_border(win_T *wp) grid_line_put_schar(i + adj[3], chars[ic], attrs[ic]); } - if (wp->w_float_config.footer) { - int footer_col = win_get_bordertext_col(icol, wp->w_float_config.footer_width, - wp->w_float_config.footer_pos); - win_redr_bordertext(wp, wp->w_float_config.footer_chunks, footer_col); + if (wp->w_config.footer) { + int footer_col + = win_get_bordertext_col(icol, wp->w_config.footer_width, wp->w_config.footer_pos); + win_redr_bordertext(wp, wp->w_config.footer_chunks, footer_col); } if (adj[1]) { grid_line_put_schar(icol + adj[3], chars[4], attrs[4]); @@ -834,8 +825,7 @@ void setcursor_mayforce(bool force) // With 'rightleft' set and the cursor on a double-wide character, // position it on the leftmost column. col = curwin->w_width_inner - curwin->w_wcol - - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 - && vim_isprintc(gchar_cursor())) ? 2 : 1); + - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 && vim_isprintc(gchar_cursor())) ? 2 : 1); } grid_adjust(&grid, &row, &col); @@ -849,22 +839,19 @@ void setcursor_mayforce(bool force) void show_cursor_info_later(bool force) { int state = get_real_state(); - int empty_line = (State & MODE_INSERT) == 0 - && *ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum) == NUL; + int empty_line + = (State & MODE_INSERT) == 0 && *ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum) == NUL; // Only draw when something changed. validate_virtcol_win(curwin); - if (force - || curwin->w_cursor.lnum != curwin->w_stl_cursor.lnum + if (force || curwin->w_cursor.lnum != curwin->w_stl_cursor.lnum || curwin->w_cursor.col != curwin->w_stl_cursor.col || curwin->w_virtcol != curwin->w_stl_virtcol || curwin->w_cursor.coladd != curwin->w_stl_cursor.coladd || curwin->w_topline != curwin->w_stl_topline || curwin->w_buffer->b_ml.ml_line_count != curwin->w_stl_line_count - || curwin->w_topfill != curwin->w_stl_topfill - || empty_line != curwin->w_stl_empty - || reg_recording != curwin->w_stl_recording - || state != curwin->w_stl_state + || curwin->w_topfill != curwin->w_stl_topfill || empty_line != curwin->w_stl_empty + || reg_recording != curwin->w_stl_recording || state != curwin->w_stl_state || (VIsual_active && VIsual_mode != curwin->w_stl_visual_mode)) { if (curwin->w_status_height || global_stl_height()) { curwin->w_redr_status = true; @@ -876,8 +863,7 @@ void show_cursor_info_later(bool force) curwin->w_redr_status = true; } - if ((p_icon && (stl_syntax & STL_IN_ICON)) - || (p_title && (stl_syntax & STL_IN_TITLE))) { + if ((p_icon && (stl_syntax & STL_IN_ICON)) || (p_title && (stl_syntax & STL_IN_TITLE))) { need_maketitle = true; } } @@ -928,15 +914,12 @@ int showmode(void) msg_grid_validate(); - int do_mode = ((p_smd && msg_silent == 0) - && ((State & MODE_TERMINAL) - || (State & MODE_INSERT) - || restart_edit != NUL - || VIsual_active)); + bool do_mode = ((p_smd && msg_silent == 0) + && ((State & MODE_TERMINAL) || (State & MODE_INSERT) || restart_edit != NUL + || VIsual_active)); bool can_show_mode = (p_ch != 0 || ui_has(kUIMessages)); if ((do_mode || reg_recording != 0) && can_show_mode) { - int sub_attr; if (skip_showmode()) { return 0; // show mode later } @@ -954,7 +937,7 @@ int showmode(void) // Position on the last line in the window, column 0 msg_pos_mode(); - int attr = HL_ATTR(HLF_CM); // Highlight mode + int attr = HL_ATTR(HLF_CM); // Highlight mode // When the screen is too narrow to show the entire mode message, // avoid scrolling and truncate instead. @@ -989,7 +972,8 @@ int showmode(void) } if (edit_submode_extra != NULL) { msg_puts_attr(" ", attr); // Add a space in between. - if ((int)edit_submode_highl < HLF_COUNT) { + int sub_attr; + if (edit_submode_highl < HLF_COUNT) { sub_attr = win_hl_attr(curwin, (int)edit_submode_highl); } else { sub_attr = attr; @@ -1009,8 +993,8 @@ int showmode(void) msg_puts_attr(_(" REVERSE"), attr); } msg_puts_attr(_(" INSERT"), attr); - } else if (restart_edit == 'I' || restart_edit == 'i' - || restart_edit == 'a' || restart_edit == 'A') { + } else if (restart_edit == 'I' || restart_edit == 'i' || restart_edit == 'a' + || restart_edit == 'A') { if (curbuf->terminal) { msg_puts_attr(_(" (terminal)"), attr); } else { @@ -1024,8 +1008,7 @@ int showmode(void) if (State & MODE_LANGMAP) { if (curwin->w_p_arab) { msg_puts_attr(_(" Arabic"), attr); - } else if (get_keymap_str(curwin, " (%s)", - NameBuff, MAXPATHL)) { + } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL)) { msg_puts_attr(NameBuff, attr); } } @@ -1038,21 +1021,25 @@ int showmode(void) // Don't concatenate separate words to avoid translation // problems. - switch ((VIsual_select ? 4 : 0) - + (VIsual_mode == Ctrl_V) * 2 - + (VIsual_mode == 'V')) { + switch ((VIsual_select ? 4 : 0) + (VIsual_mode == Ctrl_V) * 2 + (VIsual_mode == 'V')) { case 0: - p = N_(" VISUAL"); break; + p = N_(" VISUAL"); + break; case 1: - p = N_(" VISUAL LINE"); break; + p = N_(" VISUAL LINE"); + break; case 2: - p = N_(" VISUAL BLOCK"); break; + p = N_(" VISUAL BLOCK"); + break; case 4: - p = N_(" SELECT"); break; + p = N_(" SELECT"); + break; case 5: - p = N_(" SELECT LINE"); break; + p = N_(" SELECT LINE"); + break; default: - p = N_(" SELECT BLOCK"); break; + p = N_(" SELECT BLOCK"); + break; } msg_puts_attr(_(p), attr); } @@ -1061,9 +1048,8 @@ int showmode(void) need_clear = true; } - if (reg_recording != 0 - && edit_submode == NULL // otherwise it gets too long - ) { + if (reg_recording != 0 && edit_submode == NULL // otherwise it gets too long + ) { recording_mode(attr); need_clear = true; } @@ -1072,12 +1058,12 @@ int showmode(void) if (need_clear || clear_cmdline || redraw_mode) { msg_clr_eos(); } - msg_didout = false; // overwrite this message + msg_didout = false; // overwrite this message length = msg_col; msg_col = 0; msg_no_more = false; lines_left = save_lines_left; - need_wait_return = nwr_save; // never ask for hit-return for this + need_wait_return = nwr_save; // never ask for hit-return for this } else if (clear_cmdline && msg_silent == 0) { // Clear the whole command line. Will reset "clear_cmdline". msg_clr_cmdline(); @@ -1151,17 +1137,19 @@ void clearmode(void) static void recording_mode(int attr) { - msg_puts_attr(_("recording"), attr); if (shortmess(SHM_RECORDING)) { return; } - char s[4]; - snprintf(s, ARRAY_SIZE(s), " @%c", reg_recording); + msg_puts_attr(_("recording"), attr); + char reg_str[8]; + reg_str[utf_char2bytes(reg_recording, reg_str)] = 0; + char s[16]; + snprintf(s, ARRAY_SIZE(s), " @%s", reg_str); msg_puts_attr(s, attr); } -#define COL_RULER 17 // columns needed by standard ruler +#define COL_RULER 17 // columns needed by standard ruler /// Compute columns for ruler and shown command. 'sc_col' is also used to /// decide what the maximum length of a message on the status line can be. @@ -1180,19 +1168,17 @@ void comp_col(void) sc_col = ru_col; } } - if (p_sc) { + if (p_sc && *p_sloc == 'l') { sc_col += SHOWCMD_COLS; - if (!p_ru || last_has_status) { // no need for separating space + if (!p_ru || last_has_status) { // no need for separating space sc_col++; } } - assert(sc_col >= 0 - && INT_MIN + sc_col <= Columns); + assert(sc_col >= 0 && INT_MIN + sc_col <= Columns); sc_col = Columns - sc_col; - assert(ru_col >= 0 - && INT_MIN + ru_col <= Columns); + assert(ru_col >= 0 && INT_MIN + ru_col <= Columns); ru_col = Columns - ru_col; - if (sc_col <= 0) { // screen too narrow, will become a mess + if (sc_col <= 0) { // screen too narrow, will become a mess sc_col = 1; } if (ru_col <= 0) { @@ -1201,17 +1187,34 @@ void comp_col(void) set_vim_var_nr(VV_ECHOSPACE, sc_col - 1); } -static void redraw_win_signcol(win_T *wp) +/// Redraw entire window "wp" if "auto" 'signcolumn' width has changed. +static bool win_redraw_signcols(win_T *wp) { - // If we can compute a change in the automatic sizing of the sign column - // under 'signcolumn=auto:X' and signs currently placed in the buffer, better - // figuring it out here so we can redraw the entire screen for it. - int scwidth = wp->w_scwidth; - wp->w_scwidth = win_signcol_count(wp); - if (wp->w_scwidth != scwidth) { - changed_line_abv_curs_win(wp); - redraw_later(wp, UPD_NOT_VALID); + buf_T *buf = wp->w_buffer; + + if (!buf->b_signcols.autom + && (*wp->w_p_stc != NUL || (wp->w_maxscwidth > 1 && wp->w_minscwidth != wp->w_maxscwidth))) { + buf->b_signcols.autom = true; + buf_signcols_count_range(buf, 0, buf->b_ml.ml_line_count, MAXLNUM, kFalse); } + + while (buf->b_signcols.max > 0 && buf->b_signcols.count[buf->b_signcols.max - 1] == 0) { + buf->b_signcols.resized = true; + buf->b_signcols.max--; + } + + int width = MIN(wp->w_maxscwidth, buf->b_signcols.max); + bool rebuild_stc = buf->b_signcols.resized && *wp->w_p_stc != NUL; + + if (rebuild_stc) { + wp->w_nrwidth_line_count = 0; + } else if (wp->w_minscwidth == 0 && wp->w_maxscwidth == 1) { + width = buf_meta_total(buf, kMTMetaSignText) > 0; + } + + int scwidth = wp->w_scwidth; + wp->w_scwidth = MAX(MAX(0, wp->w_minscwidth), width); + return (wp->w_scwidth != scwidth || rebuild_stc); } /// Check if horizontal separator of window "wp" at specified window corner is connected to the @@ -1220,8 +1223,7 @@ static void redraw_win_signcol(win_T *wp) static bool hsep_connected(win_T *wp, WindowCorner corner) { bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT); - int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) - ? wp->w_winrow - 1 : W_ENDROW(wp); + int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) ? wp->w_winrow - 1 : W_ENDROW(wp); frame_T *fr = wp->w_frame; while (fr->fr_parent != NULL) { @@ -1255,8 +1257,8 @@ static bool hsep_connected(win_T *wp, WindowCorner corner) static bool vsep_connected(win_T *wp, WindowCorner corner) { bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT); - int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) - ? wp->w_wincol - 1 : W_ENDCOL(wp); + int sep_col + = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) ? wp->w_wincol - 1 : W_ENDCOL(wp); frame_T *fr = wp->w_frame; while (fr->fr_parent != NULL) { @@ -1293,10 +1295,11 @@ static void draw_vsep_win(win_T *wp) } // draw the vertical separator right of this window - int hl = win_hl_attr(wp, HLF_C); - int c = wp->w_p_fcs_chars.vert; - grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp), - W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); + for (int row = wp->w_winrow; row < W_ENDROW(wp); row++) { + grid_line_start(&default_grid, row); + grid_line_put_schar(W_ENDCOL(wp), wp->w_p_fcs_chars.vert, win_hl_attr(wp, HLF_C)); + grid_line_flush(); + } } /// Draw the horizontal separator below window "wp" @@ -1307,10 +1310,9 @@ static void draw_hsep_win(win_T *wp) } // draw the horizontal separator below this window - int hl = win_hl_attr(wp, HLF_C); - int c = wp->w_p_fcs_chars.horiz; - grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1, - wp->w_wincol, W_ENDCOL(wp), c, c, hl); + grid_line_start(&default_grid, W_ENDROW(wp)); + grid_line_fill(wp->w_wincol, W_ENDCOL(wp), wp->w_p_fcs_chars.horiz, win_hl_attr(wp, HLF_C)); + grid_line_flush(); } /// Get the separator connector for specified window corner of window "wp" @@ -1318,21 +1320,19 @@ static schar_T get_corner_sep_connector(win_T *wp, WindowCorner corner) { // It's impossible for windows to be connected neither vertically nor horizontally // So if they're not vertically connected, assume they're horizontally connected - int c; if (vsep_connected(wp, corner)) { if (hsep_connected(wp, corner)) { - c = wp->w_p_fcs_chars.verthoriz; + return wp->w_p_fcs_chars.verthoriz; } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) { - c = wp->w_p_fcs_chars.vertright; + return wp->w_p_fcs_chars.vertright; } else { - c = wp->w_p_fcs_chars.vertleft; + return wp->w_p_fcs_chars.vertleft; } } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) { - c = wp->w_p_fcs_chars.horizdown; + return wp->w_p_fcs_chars.horizdown; } else { - c = wp->w_p_fcs_chars.horizup; + return wp->w_p_fcs_chars.horizup; } - return schar_from_char(c); } /// Draw separator connecting characters on the corners of window "wp" @@ -1414,28 +1414,28 @@ static void draw_sep_connectors_win(win_T *wp) /// - if wp->w_buffer->b_mod_set set, update lines between /// b_mod_top and b_mod_bot. /// - if wp->w_redraw_top non-zero, redraw lines between -/// wp->w_redraw_top and wp->w_redr_bot. +/// wp->w_redraw_top and wp->w_redraw_bot. /// - continue redrawing when syntax status is invalid. /// 4. if scrolled up, update lines at the bottom. /// This results in three areas that may need updating: /// top: from first row to top_end (when scrolled down) /// mid: from mid_start to mid_end (update inversion or changed text) /// bot: from bot_start to last row (when scrolled up) -static void win_update(win_T *wp, DecorProviders *providers) +static void win_update(win_T *wp) { - int top_end = 0; // Below last row of the top area that needs - // updating. 0 when no top area updating. - int mid_start = 999; // first row of the mid area that needs - // updating. 999 when no mid area updating. - int mid_end = 0; // Below last row of the mid area that needs - // updating. 0 when no mid area updating. - int bot_start = 999; // first row of the bot area that needs - // updating. 999 when no bot area updating - bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit - bool top_to_mod = false; // redraw above mod_top - - int bot_scroll_start = 999; // first line that needs to be redrawn due to - // scrolling. only used for EOB + int top_end = 0; // Below last row of the top area that needs + // updating. 0 when no top area updating. + int mid_start = 999; // first row of the mid area that needs + // updating. 999 when no mid area updating. + int mid_end = 0; // Below last row of the mid area that needs + // updating. 0 when no mid area updating. + int bot_start = 999; // first row of the bot area that needs + // updating. 999 when no bot area updating + bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit + bool top_to_mod = false; // redraw above mod_top + + int bot_scroll_start = 999; // first line that needs to be redrawn due to + // scrolling. only used for EOB static bool recursive = false; // being called recursively @@ -1444,9 +1444,10 @@ static void win_update(win_T *wp, DecorProviders *providers) DID_NONE = 1, // didn't update a line DID_LINE = 2, // updated a normal line DID_FOLD = 3, // updated a folded line - } did_update = DID_NONE; + } did_update + = DID_NONE; - linenr_T syntax_last_parsed = 0; // last parsed text line + linenr_T syntax_last_parsed = 0; // last parsed text line linenr_T mod_top = 0; linenr_T mod_bot = 0; @@ -1488,10 +1489,13 @@ static void win_update(win_T *wp, DecorProviders *providers) decor_redraw_reset(wp, &decor_state); - DecorProviders line_providers; - decor_providers_invoke_win(wp, providers, &line_providers); + decor_providers_invoke_win(wp); - redraw_win_signcol(wp); + if (win_redraw_signcols(wp)) { + wp->w_lines_valid = 0; + wp->w_redr_type = UPD_NOT_VALID; + changed_line_abv_curs_win(wp); + } init_search_hl(wp, &screen_search_hl); @@ -1521,13 +1525,6 @@ static void win_update(win_T *wp, DecorProviders *providers) if (wp->w_nrwidth != nrwidth_new) { type = UPD_NOT_VALID; wp->w_nrwidth = nrwidth_new; - } else if (buf->b_mod_set - && buf->b_mod_xlines != 0 - && wp->w_redraw_top != 0) { - // When there are both inserted/deleted lines and specific lines to be - // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw - // everything (only happens when redrawing is off for while). - type = UPD_NOT_VALID; } else { // Set mod_top to the first line that needs displaying because of // changes. Set mod_bot to the first line after the changes. @@ -1558,14 +1555,12 @@ static void win_update(win_T *wp, DecorProviders *providers) // previous line invalid. Simple solution: redraw all visible // lines above the change. // Same for a match pattern. - if (screen_search_hl.rm.regprog != NULL - && re_multiline(screen_search_hl.rm.regprog)) { + if (screen_search_hl.rm.regprog != NULL && re_multiline(screen_search_hl.rm.regprog)) { top_to_mod = true; } else { const matchitem_T *cur = wp->w_match_head; while (cur != NULL) { - if (cur->mit_match.regprog != NULL - && re_multiline(cur->mit_match.regprog)) { + if (cur->mit_match.regprog != NULL && re_multiline(cur->mit_match.regprog)) { top_to_mod = true; break; } @@ -1602,14 +1597,14 @@ static void win_update(win_T *wp, DecorProviders *providers) } } - (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL); + hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL); if (mod_top > lnumt) { mod_top = lnumt; } // Now do the same for the bottom line (one above mod_bot). mod_bot--; - (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL); + hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL); mod_bot++; if (mod_bot < lnumb) { mod_bot = lnumb; @@ -1627,12 +1622,6 @@ static void win_update(win_T *wp, DecorProviders *providers) top_end = 1; } } - - // When line numbers are displayed need to redraw all lines below - // inserted/deleted lines. - if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) { - mod_bot = MAXLNUM; - } } wp->w_redraw_top = 0; // reset for next time @@ -1664,13 +1653,11 @@ static void win_update(win_T *wp, DecorProviders *providers) // 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up // 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in // w_lines[] that needs updating. - if ((type == UPD_VALID || type == UPD_SOME_VALID - || type == UPD_INVERTED || type == UPD_INVERTED_ALL) + if ((type == UPD_VALID || type == UPD_SOME_VALID || type == UPD_INVERTED + || type == UPD_INVERTED_ALL) && !wp->w_botfill && !wp->w_old_botfill) { - if (mod_top != 0 - && wp->w_topline == mod_top - && (!wp->w_lines[0].wl_valid - || wp->w_topline == wp->w_lines[0].wl_lnum)) { + if (mod_top != 0 && wp->w_topline == mod_top + && (!wp->w_lines[0].wl_valid || wp->w_topline == wp->w_lines[0].wl_lnum)) { // w_topline is the first changed line and window is not scrolled, // the scrolling from changed lines will be done further down. } else if (wp->w_lines[0].wl_valid @@ -1680,22 +1667,20 @@ static void win_update(win_T *wp, DecorProviders *providers) // New topline is above old topline: May scroll down. int j; if (hasAnyFolding(wp)) { - linenr_T ln; - // count the number of lines we are off, counting a sequence // of folded lines as one j = 0; - for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) { + for (linenr_T ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) { j++; if (j >= wp->w_grid.rows - 2) { break; } - (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL); + hasFoldingWin(wp, ln, NULL, &ln, true, NULL); } } else { j = wp->w_lines[0].wl_lnum - wp->w_topline; } - if (j < wp->w_grid.rows - 2) { // not too far off + if (j < wp->w_grid.rows - 2) { // not too far off int i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1, true); // insert extra lines for previously invisible filler lines if (wp->w_lines[0].wl_lnum != wp->w_topline) { @@ -1741,8 +1726,7 @@ static void win_update(win_T *wp, DecorProviders *providers) int j = -1; int row = 0; for (int i = 0; i < wp->w_lines_valid; i++) { - if (wp->w_lines[i].wl_valid - && wp->w_lines[i].wl_lnum == wp->w_topline) { + if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lnum == wp->w_topline) { j = i; break; } @@ -1781,8 +1765,7 @@ static void win_update(win_T *wp, DecorProviders *providers) wp->w_lines[idx] = wp->w_lines[j]; // stop at line that didn't fit, unless it is still // valid (no lines deleted) - if (row > 0 && bot_start + row - + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { + if (row > 0 && bot_start + row + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { wp->w_lines_valid = idx + 1; break; } @@ -1798,8 +1781,8 @@ static void win_update(win_T *wp, DecorProviders *providers) // Correct the first entry for filler lines at the top // when it won't get updated below. if (win_may_fill(wp) && bot_start > 0) { - wp->w_lines[0].wl_size = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true) - + wp->w_topfill); + wp->w_lines[0].wl_size + = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true) + wp->w_topfill); } } } @@ -1862,15 +1845,13 @@ static void win_update(win_T *wp, DecorProviders *providers) } else { from = wp->w_old_cursor_lnum; to = curwin->w_cursor.lnum; - if (from == 0) { // Visual mode just started + if (from == 0) { // Visual mode just started from = to; } } - if (VIsual.lnum != wp->w_old_visual_lnum - || VIsual.col != wp->w_old_visual_col) { - if (wp->w_old_visual_lnum < from - && wp->w_old_visual_lnum != 0) { + if (VIsual.lnum != wp->w_old_visual_lnum || VIsual.col != wp->w_old_visual_col) { + if (wp->w_old_visual_lnum < from && wp->w_old_visual_lnum != 0) { from = wp->w_old_visual_lnum; } if (wp->w_old_visual_lnum > to) { @@ -1926,8 +1907,7 @@ static void win_update(win_T *wp, DecorProviders *providers) } } - if (fromc != wp->w_old_cursor_fcol - || toc != wp->w_old_cursor_lcol) { + if (fromc != wp->w_old_cursor_fcol || toc != wp->w_old_cursor_lcol) { if (from > VIsual.lnum) { from = VIsual.lnum; } @@ -1981,7 +1961,7 @@ static void win_update(win_T *wp, DecorProviders *providers) } else { mid_start = 0; } - while (lnum < from && idx < wp->w_lines_valid) { // find start + while (lnum < from && idx < wp->w_lines_valid) { // find start if (wp->w_lines[idx].wl_valid) { mid_start += wp->w_lines[idx].wl_size; } else if (!scrolled_down) { @@ -1996,9 +1976,8 @@ static void win_update(win_T *wp, DecorProviders *providers) } srow += mid_start; mid_end = wp->w_grid.rows; - for (; idx < wp->w_lines_valid; idx++) { // find end - if (wp->w_lines[idx].wl_valid - && wp->w_lines[idx].wl_lnum >= to + 1) { + for (; idx < wp->w_lines_valid; idx++) { // find end + if (wp->w_lines[idx].wl_valid && wp->w_lines[idx].wl_lnum >= to + 1) { // Only update until first row of this line mid_end = srow; break; @@ -2042,12 +2021,12 @@ static void win_update(win_T *wp, DecorProviders *providers) } // Update all the window rows. - int idx = 0; // first entry in w_lines[].wl_size - int row = 0; // current window row to display - int srow = 0; // starting row of the current line + int idx = 0; // first entry in w_lines[].wl_size + int row = 0; // current window row to display + int srow = 0; // starting row of the current line - bool eof = false; // if true, we hit the end of the file - bool didline = false; // if true, we finished the last line + bool eof = false; // if true, we hit the end of the file + bool didline = false; // if true, we finished the last line while (true) { // stop updating when reached the end of the window (check for _past_ // the end of the window is at the end of the loop) @@ -2073,27 +2052,19 @@ static void win_update(win_T *wp, DecorProviders *providers) // When syntax folding is being used, the saved syntax states will // already have been updated, we can't see where the syntax state is // the same again, just update until the end of the window. - if (row < top_end - || (row >= mid_start && row < mid_end) - || top_to_mod - || idx >= wp->w_lines_valid - || (row + wp->w_lines[idx].wl_size > bot_start) + if (row < top_end || (row >= mid_start && row < mid_end) || top_to_mod + || idx >= wp->w_lines_valid || (row + wp->w_lines[idx].wl_size > bot_start) || (mod_top != 0 && (lnum == mod_top || (lnum >= mod_top - && (lnum < mod_bot - || did_update == DID_FOLD - || (did_update == DID_LINE - && syntax_present(wp) - && ((foldmethodIsSyntax(wp) - && hasAnyFolding(wp)) + && (lnum < mod_bot || did_update == DID_FOLD + || (did_update == DID_LINE && syntax_present(wp) + && ((foldmethodIsSyntax(wp) && hasAnyFolding(wp)) || syntax_check_changed(lnum))) // match in fixed position might need redraw // if lines were inserted or deleted - || (wp->w_match_head != NULL - && buf->b_mod_xlines != 0))))) - || lnum == wp->w_cursorline - || lnum == wp->w_last_cursorline) { + || (wp->w_match_head != NULL && buf->b_mod_xlines != 0))))) + || lnum == wp->w_cursorline || lnum == wp->w_last_cursorline) { if (lnum == mod_top) { top_to_mod = false; } @@ -2103,9 +2074,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // Don't do this when the change continues until the end. // Don't scroll when dollar_vcol >= 0, keep the "$". // Don't scroll when redrawing the top, scrolled already above. - if (lnum == mod_top - && mod_bot != MAXLNUM - && !(dollar_vcol >= 0 && mod_bot == mod_top + 1) + if (lnum == mod_top && mod_bot != MAXLNUM && !(dollar_vcol >= 0 && mod_bot == mod_top + 1) && row >= top_end) { int old_rows = 0; linenr_T l; @@ -2117,18 +2086,15 @@ static void win_update(win_T *wp, DecorProviders *providers) for (i = idx; i < wp->w_lines_valid; i++) { // Only valid lines have a meaningful wl_lnum. Invalid // lines are part of the changed area. - if (wp->w_lines[i].wl_valid - && wp->w_lines[i].wl_lnum == mod_bot) { + if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lnum == mod_bot) { break; } old_rows += wp->w_lines[i].wl_size; - if (wp->w_lines[i].wl_valid - && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) { + if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) { // Must have found the last valid entry above mod_bot. // Add following invalid entries. i++; - while (i < wp->w_lines_valid - && !wp->w_lines[i].wl_valid) { + while (i < wp->w_lines_valid && !wp->w_lines[i].wl_valid) { old_rows += wp->w_lines[i++].wl_size; } break; @@ -2211,8 +2177,7 @@ static void win_update(win_T *wp, DecorProviders *providers) } wp->w_lines[j] = wp->w_lines[i]; // stop at a line that won't fit - if (x + (int)wp->w_lines[j].wl_size - > wp->w_grid.rows) { + if (x + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { wp->w_lines_valid = j + 1; break; } @@ -2222,8 +2187,8 @@ static void win_update(win_T *wp, DecorProviders *providers) if (bot_start > x) { bot_start = x; } - } else { // j > i - // move entries in w_lines[] downwards + } else { // j > i + // move entries in w_lines[] downwards j -= i; wp->w_lines_valid += (linenr_T)j; if (wp->w_lines_valid > wp->w_grid.rows) { @@ -2248,52 +2213,52 @@ static void win_update(win_T *wp, DecorProviders *providers) // When lines are folded, display one line for all of them. // Otherwise, display normally (can be several display lines when // 'wrap' is on). - foldinfo_T foldinfo = wp->w_p_cul && lnum == wp->w_cursor.lnum - ? cursorline_fi : fold_info(wp, lnum); - - if (foldinfo.fi_lines == 0 - && idx < wp->w_lines_valid - && wp->w_lines[idx].wl_valid - && wp->w_lines[idx].wl_lnum == lnum - && lnum > wp->w_topline + foldinfo_T foldinfo + = wp->w_p_cul && lnum == wp->w_cursor.lnum ? cursorline_fi : fold_info(wp, lnum); + + if (foldinfo.fi_lines == 0 && idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid + && wp->w_lines[idx].wl_lnum == lnum && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) - && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows - && win_get_fill(wp, lnum) == 0) { + && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows && win_get_fill(wp, lnum) == 0) { // This line is not going to fit. Don't draw anything here, // will draw "@ " lines below. row = wp->w_grid.rows + 1; } else { prepare_search_hl(wp, &screen_search_hl, lnum); // Let the syntax stuff know we skipped a few lines. - if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum - && syntax_present(wp)) { + if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum && syntax_present(wp)) { syntax_end_parsing(wp, syntax_last_parsed + 1); } + bool display_buf_line = (foldinfo.fi_lines == 0 || *wp->w_p_fdt == NUL); + // Display one line spellvars_T zero_spv = { 0 }; - row = win_line(wp, lnum, srow, wp->w_grid.rows, false, - foldinfo.fi_lines > 0 ? &zero_spv : &spv, - foldinfo, &line_providers); + row = win_line(wp, lnum, srow, wp->w_grid.rows, 0, display_buf_line ? &spv : &zero_spv, + foldinfo); + + if (display_buf_line) { + syntax_last_parsed = lnum; + } else { + spv.spv_capcol_lnum = 0; + } if (foldinfo.fi_lines == 0) { wp->w_lines[idx].wl_folded = false; wp->w_lines[idx].wl_lastlnum = lnum; did_update = DID_LINE; - syntax_last_parsed = lnum; } else { foldinfo.fi_lines--; wp->w_lines[idx].wl_folded = true; wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines; did_update = DID_FOLD; - spv.spv_capcol_lnum = 0; } } wp->w_lines[idx].wl_lnum = lnum; wp->w_lines[idx].wl_valid = true; - if (row > wp->w_grid.rows) { // past end of grid + if (row > wp->w_grid.rows) { // past end of grid // we may need the size of that too long line later on if (dollar_vcol == -1) { wp->w_lines[idx].wl_size = (uint16_t)plines_win(wp, lnum, true); @@ -2307,12 +2272,15 @@ static void win_update(win_T *wp, DecorProviders *providers) idx++; lnum += foldinfo.fi_lines + 1; } else { - if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum) { - // 'relativenumber' set and cursor moved vertically: The - // text doesn't need to be drawn, but the number column does. - foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum - ? cursorline_fi : fold_info(wp, lnum); - (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, &spv, info, &line_providers); + // If: + // - 'number' is set and below inserted/deleted lines, or + // - 'relativenumber' is set and cursor moved vertically, + // the text doesn't need to be redrawn, but the number column does. + if ((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot && buf->b_mod_xlines != 0) + || (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum)) { + foldinfo_T info + = wp->w_p_cul && lnum == wp->w_cursor.lnum ? cursorline_fi : fold_info(wp, lnum); + win_line(wp, lnum, srow, wp->w_grid.rows, wp->w_lines[idx].wl_size, &spv, info); } // This line does not need to be drawn, advance to the next one. @@ -2327,6 +2295,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // 'statuscolumn' width has changed or errored, start from the top. if (wp->w_redr_statuscol) { + redr_statuscol: wp->w_redr_statuscol = false; idx = 0; row = 0; @@ -2334,7 +2303,7 @@ static void win_update(win_T *wp, DecorProviders *providers) wp->w_lines_valid = 0; wp->w_valid &= ~VALID_WCOL; decor_redraw_reset(wp, &decor_state); - decor_providers_invoke_win(wp, providers, &line_providers); + decor_providers_invoke_win(wp); continue; } @@ -2376,26 +2345,26 @@ static void win_update(win_T *wp, DecorProviders *providers) // Window ends in filler lines. wp->w_botline = lnum; wp->w_filler_rows = wp->w_grid.rows - srow; - } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" + } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" // Last line isn't finished: Display "@@@" in the last screen line. grid_line_start(&wp->w_grid, wp->w_grid.rows - 1); grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr); - grid_line_fill(3, wp->w_grid.cols, ' ', at_attr); + grid_line_fill(3, wp->w_grid.cols, schar_from_ascii(' '), at_attr); grid_line_flush(); set_empty_rows(wp, srow); wp->w_botline = lnum; - } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" + } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" // Last line isn't finished: Display "@@@" at the end. // If this would split a doublewidth char in two, we need to display "@@@@" instead grid_line_start(&wp->w_grid, wp->w_grid.rows - 1); int width = grid_line_getchar(MAX(wp->w_grid.cols - 3, 0), NULL) == NUL ? 4 : 3; - grid_line_fill(MAX(wp->w_grid.cols - width, 0), wp->w_grid.cols, - wp->w_p_fcs_chars.lastline, at_attr); + grid_line_fill(MAX(wp->w_grid.cols - width, 0), wp->w_grid.cols, wp->w_p_fcs_chars.lastline, + at_attr); grid_line_flush(); set_empty_rows(wp, srow); wp->w_botline = lnum; } else { - win_draw_end(wp, wp->w_p_fcs_chars.lastline, ' ', true, srow, wp->w_grid.rows, HLF_AT); + win_draw_end(wp, wp->w_p_fcs_chars.lastline, true, srow, wp->w_grid.rows, HLF_AT); set_empty_rows(wp, srow); wp->w_botline = lnum; } @@ -2408,8 +2377,11 @@ static void win_update(win_T *wp, DecorProviders *providers) // for ml_line_count+1 and only draw filler lines spellvars_T zero_spv = { 0 }; foldinfo_T zero_foldinfo = { 0 }; - row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, false, &zero_spv, - zero_foldinfo, &line_providers); + row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, 0, &zero_spv, zero_foldinfo); + if (wp->w_redr_statuscol) { + eof = false; + goto redr_statuscol; + } } } else if (dollar_vcol == -1) { wp->w_botline = lnum; @@ -2428,13 +2400,10 @@ static void win_update(win_T *wp, DecorProviders *providers) lastline = 0; } - win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, MAX(lastline, row), wp->w_grid.rows, - HLF_EOB); + win_draw_end(wp, wp->w_p_fcs_chars.eob, false, MAX(lastline, row), wp->w_grid.rows, HLF_EOB); set_empty_rows(wp, row); } - kvi_destroy(line_providers); - if (wp->w_redr_type >= UPD_REDRAW_TOP) { draw_vsep_win(wp); draw_hsep_win(wp); @@ -2449,9 +2418,9 @@ static void win_update(win_T *wp, DecorProviders *providers) // Send win_extmarks if needed for (size_t n = 0; n < kv_size(win_extmark_arr); n++) { - ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle, - kv_A(win_extmark_arr, n).ns_id, (Integer)kv_A(win_extmark_arr, n).mark_id, - kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col); + ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle, kv_A(win_extmark_arr, n).ns_id, + (Integer)kv_A(win_extmark_arr, n).mark_id, kv_A(win_extmark_arr, n).win_row, + kv_A(win_extmark_arr, n).win_col); } if (dollar_vcol == -1) { @@ -2475,7 +2444,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // Don't update for changes in buffer again. int mod_set = curbuf->b_mod_set; curbuf->b_mod_set = false; - win_update(curwin, providers); + win_update(curwin); must_redraw = 0; curbuf->b_mod_set = mod_set; } @@ -2509,71 +2478,49 @@ void win_scroll_lines(win_T *wp, int row, int line_count) } if (line_count < 0) { - grid_del_lines(&wp->w_grid, row, -line_count, - wp->w_grid.rows, 0, wp->w_grid.cols); + grid_del_lines(&wp->w_grid, row, -line_count, wp->w_grid.rows, 0, wp->w_grid.cols); } else { - grid_ins_lines(&wp->w_grid, row, line_count, - wp->w_grid.rows, 0, wp->w_grid.cols); + grid_ins_lines(&wp->w_grid, row, line_count, wp->w_grid.rows, 0, wp->w_grid.cols); } } -/// Call grid_fill() with columns adjusted for 'rightleft' if needed. -/// Return the new offset. -static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, int endrow, - int attr) -{ - int nn = off + width; - const int endcol = wp->w_grid.cols; - - if (nn > endcol) { - nn = endcol; - } - - if (wp->w_p_rl) { - grid_fill(&wp->w_grid, row, endrow, endcol - nn, endcol - off, c1, c2, attr); - } else { - grid_fill(&wp->w_grid, row, endrow, off, nn, c1, c2, attr); - } - - return nn; -} - /// Clear lines near the end of the window and mark the unused lines with "c1". -/// Use "c2" as filler character. /// When "draw_margin" is true, then draw the sign/fold/number columns. -void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl) +void win_draw_end(win_T *wp, schar_T c1, bool draw_margin, int startrow, int endrow, hlf_T hl) { assert(hl >= 0 && hl < HLF_COUNT); - int n = 0; - - if (draw_margin) { - // draw the fold column - int fdc = compute_foldcolumn(wp, 0); - if (fdc > 0) { - n = win_fill_end(wp, ' ', ' ', n, fdc, row, endrow, - win_hl_attr(wp, HLF_FC)); - } - // draw the sign column - int count = wp->w_scwidth; - if (count > 0) { - n = win_fill_end(wp, ' ', ' ', n, SIGN_WIDTH * count, row, - endrow, win_hl_attr(wp, HLF_SC)); + for (int row = startrow; row < endrow; row++) { + grid_line_start(&wp->w_grid, row); + + int n = 0; + if (draw_margin) { + // draw the fold column + int fdc = MAX(0, compute_foldcolumn(wp, 0)); + n = grid_line_fill(n, n + fdc, schar_from_ascii(' '), win_hl_attr(wp, HLF_FC)); + + // draw the sign column + n = grid_line_fill(n, n + wp->w_scwidth, schar_from_ascii(' '), win_hl_attr(wp, HLF_FC)); + + // draw the number column + if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) { + int width = number_width(wp) + 1; + n = grid_line_fill(n, n + width, schar_from_ascii(' '), win_hl_attr(wp, HLF_N)); + } } - // draw the number column - if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) { - n = win_fill_end(wp, ' ', ' ', n, number_width(wp) + 1, row, endrow, - win_hl_attr(wp, HLF_N)); + + int attr = hl_combine_attr(win_bg_attr(wp), win_hl_attr(wp, (int)hl)); + + if (n < wp->w_grid.cols) { + grid_line_put_schar(n, c1, 0); // base attr is inherited from clear + n++; } - } - int attr = hl_combine_attr(win_bg_attr(wp), win_hl_attr(wp, (int)hl)); + grid_line_clear_end(n, wp->w_grid.cols, attr); - const int endcol = wp->w_grid.cols; - if (wp->w_p_rl) { - grid_fill(&wp->w_grid, row, endrow, 0, endcol - 1 - n, c2, c2, attr); - grid_fill(&wp->w_grid, row, endrow, endcol - 1 - n, endcol - n, c1, c2, attr); - } else { - grid_fill(&wp->w_grid, row, endrow, n, endcol, c1, c2, attr); + if (wp->w_p_rl) { + grid_line_mirror(); + } + grid_line_flush(); } } @@ -2631,7 +2578,7 @@ int number_width(win_T *wp) // If 'signcolumn' is set to 'number' and there is a sign to display, then // the minimal width for the number column is 2. - if (n < 2 && wp->w_buffer->b_signs_with_text && wp->w_minscwidth == SCL_NUM) { + if (n < 2 && buf_meta_total(wp->w_buffer, kMTMetaSignText) && wp->w_minscwidth == SCL_NUM) { n = 2; } @@ -2644,14 +2591,15 @@ int number_width(win_T *wp) /// Set must_redraw only if not already set to a higher value. /// e.g. if must_redraw is UPD_CLEAR, type UPD_NOT_VALID will do nothing. void redraw_later(win_T *wp, int type) - FUNC_ATTR_NONNULL_ALL { - if (!exiting && wp->w_redr_type < type) { + // curwin may have been set to NULL when exiting + assert(wp != NULL || exiting); + if (!exiting && !redraw_not_allowed && wp->w_redr_type < type) { wp->w_redr_type = type; if (type >= UPD_NOT_VALID) { wp->w_lines_valid = 0; } - if (must_redraw < type) { // must_redraw is the maximum of all windows + if (must_redraw < type) { // must_redraw is the maximum of all windows must_redraw = type; } } @@ -2664,7 +2612,14 @@ void redraw_all_later(int type) redraw_later(wp, type); } // This may be needed when switching tabs. - if (must_redraw < type) { + set_must_redraw(type); +} + +/// Set "must_redraw" to "type" unless it already has a higher value +/// or it is currently not allowed. +void set_must_redraw(int type) +{ + if (!redraw_not_allowed && must_redraw < type) { must_redraw = type; } } @@ -2704,11 +2659,10 @@ void redraw_buf_line_later(buf_T *buf, linenr_T line, bool force) } } -void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) +void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf - && lastline >= wp->w_topline && firstline < wp->w_botline) { + if (wp->w_buffer == buf && lastline >= wp->w_topline && firstline < wp->w_botline) { if (wp->w_redraw_top == 0 || wp->w_redraw_top > firstline) { wp->w_redraw_top = firstline; } @@ -2725,13 +2679,9 @@ void redraw_buf_status_later(buf_T *buf) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer == buf - && (wp->w_status_height - || (wp == curwin && global_stl_height()) - || wp->w_winbar_height)) { + && (wp->w_status_height || (wp == curwin && global_stl_height()) || wp->w_winbar_height)) { wp->w_redr_status = true; - if (must_redraw < UPD_VALID) { - must_redraw = UPD_VALID; - } + set_must_redraw(UPD_VALID); } } } @@ -2742,8 +2692,7 @@ void status_redraw_all(void) bool is_stl_global = global_stl_height() != 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if ((!is_stl_global && wp->w_status_height) || wp == curwin - || wp->w_winbar_height) { + if ((!is_stl_global && wp->w_status_height) || wp == curwin || wp->w_winbar_height) { wp->w_redr_status = true; redraw_later(wp, UPD_VALID); } @@ -2762,8 +2711,9 @@ void status_redraw_buf(buf_T *buf) bool is_stl_global = global_stl_height() != 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf && ((!is_stl_global && wp->w_status_height) - || (is_stl_global && wp == curwin) || wp->w_winbar_height)) { + if (wp->w_buffer == buf + && ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin) + || wp->w_winbar_height)) { wp->w_redr_status = true; redraw_later(wp, UPD_VALID); } @@ -2793,8 +2743,7 @@ void redraw_statuslines(void) } /// Redraw all status lines at the bottom of frame "frp". -void win_redraw_last_status(const frame_T *frp) - FUNC_ATTR_NONNULL_ARG(1) +void win_redraw_last_status(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) { if (frp->fr_layout == FR_LEAF) { frp->fr_win->w_redr_status = true; @@ -2818,11 +2767,9 @@ void win_redraw_last_status(const frame_T *frp) /// Used to remove the "$" from a change command. /// Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot /// may become invalid and the whole window will have to be redrawn. -void redrawWinline(win_T *wp, linenr_T lnum) - FUNC_ATTR_NONNULL_ALL +void redrawWinline(win_T *wp, linenr_T lnum) FUNC_ATTR_NONNULL_ALL { - if (lnum >= wp->w_topline - && lnum < wp->w_botline) { + if (lnum >= wp->w_topline && lnum < wp->w_botline) { if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { wp->w_redraw_top = lnum; } @@ -2835,8 +2782,7 @@ void redrawWinline(win_T *wp, linenr_T lnum) /// Return true if the cursor line in window "wp" may be concealed, according /// to the 'concealcursor' option. -bool conceal_cursor_line(const win_T *wp) - FUNC_ATTR_NONNULL_ALL +bool conceal_cursor_line(const win_T *wp) FUNC_ATTR_NONNULL_ALL { int c; @@ -2860,20 +2806,7 @@ bool conceal_cursor_line(const win_T *wp) /// Whether cursorline is drawn in a special way /// /// If true, both old and new cursorline will need to be redrawn when moving cursor within windows. -bool win_cursorline_standout(const win_T *wp) - FUNC_ATTR_NONNULL_ALL +bool win_cursorline_standout(const win_T *wp) FUNC_ATTR_NONNULL_ALL { return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp)); } - -/// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set. -/// Also when concealing is on and 'concealcursor' is not active. -void redraw_for_cursorline(win_T *wp) - FUNC_ATTR_NONNULL_ALL -{ - if ((wp->w_valid & VALID_CROW) == 0 && !pum_visible() - && (wp->w_p_rnu || win_cursorline_standout(wp))) { - // win_line() will redraw the number column and cursorline only. - redraw_later(wp, UPD_VALID); - } -} diff --git a/src/nvim/drawscreen.h b/src/nvim/drawscreen.h index 565b01bcd1..f804345bca 100644 --- a/src/nvim/drawscreen.h +++ b/src/nvim/drawscreen.h @@ -3,7 +3,6 @@ #include <stdbool.h> #include "nvim/buffer_defs.h" -#include "nvim/drawline.h" #include "nvim/macros_defs.h" /// flags for update_screen() @@ -20,9 +19,13 @@ enum { /// While redrawing the screen this flag is set. It means the screen size /// ('lines' and 'rows') must not be changed. -EXTERN bool updating_screen INIT( = 0); +EXTERN bool updating_screen INIT( = false); -EXTERN match_T screen_search_hl INIT( = { 0 }); // used for 'hlsearch' highlight matching +/// While computing a statusline and the like we do not want any w_redr_type or +/// must_redraw to be set. +EXTERN bool redraw_not_allowed INIT( = false); + +EXTERN match_T screen_search_hl INIT( = { 0 }); ///< used for 'hlsearch' highlight matching #define W_ENDCOL(wp) ((wp)->w_wincol + (wp)->w_width) #define W_ENDROW(wp) ((wp)->w_winrow + (wp)->w_height) diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 71a12ea1b0..b7b32883c2 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -5,12 +5,13 @@ #include <inttypes.h> #include <stdbool.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -22,14 +23,15 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" @@ -38,13 +40,17 @@ #include "nvim/macros_defs.h" #include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" +#include "nvim/marktree_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/option_vars.h" @@ -54,6 +60,7 @@ #include "nvim/pos_defs.h" #include "nvim/search.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/terminal.h" @@ -61,11 +68,12 @@ #include "nvim/textobject.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" -typedef struct insert_state { +typedef struct { VimState state; cmdarg_T *ca; int mincol; @@ -113,7 +121,7 @@ static int did_restart_edit; // "restart_edit" when calling edit() static bool can_cindent; // may do cindenting on this line -static int revins_on; // reverse insert mode on +static bool revins_on; // reverse insert mode on static int revins_chars; // how much to skip after edit static int revins_legal; // was the last char 'legal'? static int revins_scol; // start column of revins session @@ -233,7 +241,7 @@ static void insert_enter(InsertState *s) // need to position cursor again when on a TAB and // when on a char with inline virtual text - if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) { + if (gchar_cursor() == TAB || buf_meta_total(curbuf, kMTMetaInline) > 0) { curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); } @@ -355,7 +363,13 @@ static void insert_enter(InsertState *s) ins_apply_autocmds(EVENT_INSERTLEAVE); } did_cursorhold = false; - curbuf->b_last_changedtick = buf_get_changedtick(curbuf); + + // ins_redraw() triggers TextChangedI only when no characters + // are in the typeahead buffer, so only reset curbuf->b_last_changedtick + // if the TextChangedI was not blocked by char_avail() (e.g. using :norm!) + if (!char_avail()) { + curbuf->b_last_changedtick = buf_get_changedtick(curbuf); + } } static int insert_check(VimState *state) @@ -395,7 +409,7 @@ static int insert_check(VimState *state) Insstart_orig = Insstart; } - if (curbuf->terminal) { + if (curbuf->terminal && !stop_insert_mode) { // Exit Insert mode and go to Terminal mode. stop_insert_mode = true; restart_edit = 'I'; @@ -445,8 +459,11 @@ static int insert_check(VimState *state) // is detected when the cursor column is smaller after inserting something. // Don't do this when the topline changed already, it has already been // adjusted (by insertchar() calling open_line())). + // Also don't do this when 'smoothscroll' is set, as the window should then + // be scrolled by screen lines. if (curbuf->b_mod_set && curwin->w_p_wrap + && !curwin->w_p_sms && !s->did_backspace && curwin->w_topline == s->old_topline && curwin->w_topfill == s->old_topfill) { @@ -456,8 +473,7 @@ static int insert_check(VimState *state) if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(), curbuf->b_p_ts, curbuf->b_p_vts_array) - && curwin->w_wrow == curwin->w_winrow - + curwin->w_height_inner - 1 - get_scrolloff_value(curwin) + && curwin->w_wrow == curwin->w_height_inner - 1 - get_scrolloff_value(curwin) && (curwin->w_cursor.lnum != curwin->w_topline || curwin->w_topfill > 0)) { if (curwin->w_topfill > 0) { @@ -1429,50 +1445,52 @@ static int pc_col; void edit_putchar(int c, bool highlight) { - if (curwin->w_grid_alloc.chars != NULL || default_grid.chars != NULL) { - int attr; - update_topline(curwin); // just in case w_topline isn't valid - validate_cursor(); - if (highlight) { - attr = HL_ATTR(HLF_8); - } else { - attr = 0; - } - pc_row = curwin->w_wrow; - pc_status = PC_STATUS_UNSET; - grid_line_start(&curwin->w_grid, pc_row); - if (curwin->w_p_rl) { - pc_col = curwin->w_grid.cols - 1 - curwin->w_wcol; - - if (grid_line_getchar(pc_col, NULL) == NUL) { - grid_line_put_schar(pc_col - 1, schar_from_ascii(' '), attr); - curwin->w_wcol--; - pc_status = PC_STATUS_RIGHT; - } - } else { - pc_col = curwin->w_wcol; + if (curwin->w_grid_alloc.chars == NULL && default_grid.chars == NULL) { + return; + } - if (grid_line_getchar(pc_col + 1, NULL) == NUL) { - // pc_col is the left half of a double-width char - pc_status = PC_STATUS_LEFT; - } + int attr; + update_topline(curwin); // just in case w_topline isn't valid + validate_cursor(); + if (highlight) { + attr = HL_ATTR(HLF_8); + } else { + attr = 0; + } + pc_row = curwin->w_wrow; + pc_status = PC_STATUS_UNSET; + grid_line_start(&curwin->w_grid, pc_row); + if (curwin->w_p_rl) { + pc_col = curwin->w_grid.cols - 1 - curwin->w_wcol; + + if (grid_line_getchar(pc_col, NULL) == NUL) { + grid_line_put_schar(pc_col - 1, schar_from_ascii(' '), attr); + curwin->w_wcol--; + pc_status = PC_STATUS_RIGHT; } + } else { + pc_col = curwin->w_wcol; - // save the character to be able to put it back - if (pc_status == PC_STATUS_UNSET) { - pc_schar = grid_line_getchar(pc_col, &pc_attr); - pc_status = PC_STATUS_SET; + if (grid_line_getchar(pc_col + 1, NULL) == NUL) { + // pc_col is the left half of a double-width char + pc_status = PC_STATUS_LEFT; } + } - char buf[MB_MAXCHAR + 1]; - grid_line_puts(pc_col, buf, utf_char2bytes(c, buf), attr); - grid_line_flush(); + // save the character to be able to put it back + if (pc_status == PC_STATUS_UNSET) { + pc_schar = grid_line_getchar(pc_col, &pc_attr); + pc_status = PC_STATUS_SET; } + + char buf[MB_MAXCHAR + 1]; + grid_line_puts(pc_col, buf, utf_char2bytes(c, buf), attr); + grid_line_flush(); } /// @return the effective prompt for the specified buffer. char *buf_prompt_text(const buf_T *const buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { if (buf->b_prompt_text == NULL) { return "% "; @@ -1581,10 +1599,12 @@ void display_dollar(colnr_T col_arg) // in insert mode. void undisplay_dollar(void) { - if (dollar_vcol >= 0) { - dollar_vcol = -1; - redrawWinline(curwin, curwin->w_cursor.lnum); + if (dollar_vcol < 0) { + return; } + + dollar_vcol = -1; + redrawWinline(curwin, curwin->w_cursor.lnum); } /// Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D). @@ -1596,7 +1616,7 @@ void undisplay_dollar(void) /// @param round if true, round the indent to 'shiftwidth' (only with _INC and _Dec). /// @param replaced replaced character, put on replace stack /// @param call_changed_bytes call changed_bytes() -void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes) +void change_indent(int type, int amount, int round, int replaced, bool call_changed_bytes) { int insstart_less; // reduction for Insstart.col colnr_T orig_col = 0; // init for GCC @@ -1638,7 +1658,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang // Set the new indent. The cursor will be put on the first non-blank. if (type == INDENT_SET) { - (void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0); + set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0); } else { int save_State = State; @@ -1670,33 +1690,37 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang } else { // Compute the screen column where the cursor should be. vcol = get_indent() - vcol; - curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); + int const end_vcol = (colnr_T)((vcol < 0) ? 0 : vcol); + curwin->w_virtcol = end_vcol; // Advance the cursor until we reach the right screen column. - int last_vcol = 0; - char *ptr = get_cursor_line_ptr(); - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, 0, 0, ptr, ptr); - while (cts.cts_vcol <= (int)curwin->w_virtcol) { - last_vcol = cts.cts_vcol; - if (cts.cts_vcol > 0) { - MB_PTR_ADV(cts.cts_ptr); - } - if (*cts.cts_ptr == NUL) { - break; + new_cursor_col = 0; + char *const line = get_cursor_line_ptr(); + vcol = 0; + if (*line != NUL) { + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, 0, line); + StrCharInfo ci = utf_ptr2StrCharInfo(line); + while (true) { + int next_vcol = vcol + win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + if (next_vcol > end_vcol) { + break; + } + vcol = next_vcol; + ci = utfc_next(ci); + if (*ci.ptr == NUL) { + break; + } } - cts.cts_vcol += lbr_chartabsize(&cts); + new_cursor_col = (int)(ci.ptr - line); } - vcol = last_vcol; - new_cursor_col = (int)(cts.cts_ptr - cts.cts_line); - clear_chartabsize_arg(&cts); // May need to insert spaces to be able to position the cursor on // the right screen column. if (vcol != (int)curwin->w_virtcol) { curwin->w_cursor.col = (colnr_T)new_cursor_col; size_t i = (size_t)(curwin->w_virtcol - vcol); - ptr = xmallocz(i); + char *ptr = xmallocz(i); memset(ptr, ' ', i); new_cursor_col += (int)i; ins_str(ptr); @@ -2136,11 +2160,10 @@ void insertchar(int c, int flags, int second_indent) && !p_ri) { #define INPUT_BUFLEN 100 char buf[INPUT_BUFLEN + 1]; - int i; colnr_T virtcol = 0; buf[0] = (char)c; - i = 1; + int i = 1; if (textwidth > 0) { virtcol = get_nolist_virtcol(); } @@ -2454,9 +2477,7 @@ void beginline(int flags) curwin->w_cursor.coladd = 0; if (flags & (BL_WHITE | BL_SOL)) { - char *ptr; - - for (ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr) + for (char *ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr) && !((flags & BL_FIX) && ptr[1] == NUL); ptr++) { curwin->w_cursor.col++; } @@ -2569,7 +2590,7 @@ void cursor_up_inner(win_T *wp, linenr_T n) // Count each sequence of folded lines as one logical line. // go to the start of the current fold - (void)hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); while (n--) { // move up one line @@ -2581,7 +2602,7 @@ void cursor_up_inner(win_T *wp, linenr_T n) // Insert mode or when 'foldopen' contains "all": it will open // in a moment. if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & FDO_ALL))) { - (void)hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); } } if (lnum < 1) { @@ -2595,7 +2616,7 @@ void cursor_up_inner(win_T *wp, linenr_T n) } /// @param upd_topline When true: update topline -int cursor_up(linenr_T n, int upd_topline) +int cursor_up(linenr_T n, bool upd_topline) { // This fails if the cursor is already in the first line. if (n > 0 && curwin->w_cursor.lnum <= 1) { @@ -2648,7 +2669,7 @@ void cursor_down_inner(win_T *wp, int n) } /// @param upd_topline When true: update topline -int cursor_down(int n, int upd_topline) +int cursor_down(int n, bool upd_topline) { // This fails if the cursor is already in the last line. if (n > 0 && curwin->w_cursor.lnum >= curwin->w_buffer->b_ml.ml_line_count) { @@ -2945,7 +2966,7 @@ static void replace_do_bs(int limit_col) getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL); orig_vcols = win_chartabsize(curwin, get_cursor_pos_ptr(), start_vcol); } - (void)del_char_after_col(limit_col); + del_char_after_col(limit_col); if (l_State & VREPLACE_FLAG) { orig_len = (int)strlen(get_cursor_pos_ptr()); } @@ -2976,7 +2997,7 @@ static void replace_do_bs(int limit_col) // mark the buffer as changed and prepare for displaying changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); } else if (cc == 0) { - (void)del_char_after_col(limit_col); + del_char_after_col(limit_col); } } @@ -3003,10 +3024,9 @@ bool cindent_on(void) bool in_cinkeys(int keytyped, int when, bool line_is_empty) { char *look; - int try_match; - int try_match_word; + bool try_match; + bool try_match_word; char *p; - char *line; bool icase; if (keytyped == NUL) { @@ -3148,7 +3168,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) // Just completed a word, check if it starts with "look". // search back for the start of a word. - line = get_cursor_line_ptr(); + char *line = get_cursor_line_ptr(); for (s = line + curwin->w_cursor.col; s > line; s = n) { n = mb_prevptr(line, s); if (!vim_iswordp(n)) { @@ -3165,9 +3185,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) } else { // TODO(@brammool): multi-byte if (keytyped == (int)(uint8_t)p[-1] - || (icase && keytyped < 256 + || (icase && keytyped < 256 && keytyped >= 0 && TOLOWER_LOC(keytyped) == TOLOWER_LOC((uint8_t)p[-1]))) { - line = get_cursor_pos_ptr(); + char *line = get_cursor_pos_ptr(); assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX); if ((curwin->w_cursor.col == (colnr_T)(p - look) || !vim_iswordc((uint8_t)line[-(p - look) - 1])) @@ -3411,7 +3431,7 @@ static bool ins_esc(int *count, int cmdchar, bool nomove) State &= ~REPLACE_FLAG; } - (void)start_redo_ins(); + start_redo_ins(); if (cmdchar == 'r' || cmdchar == 'v') { stuffRedoReadbuff(ESC_STR); // No ESC in redo buffer } @@ -3463,7 +3483,7 @@ static bool ins_esc(int *count, int cmdchar, bool nomove) may_trigger_modechanged(); // need to position cursor again when on a TAB and // when on a char with inline virtual text - if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) { + if (gchar_cursor() == TAB || buf_meta_total(curbuf, kMTMetaInline) > 0) { curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); } @@ -3601,7 +3621,7 @@ static void ins_shift(int c, int lastc) if (c == Ctrl_D && (lastc == '0' || lastc == '^') && curwin->w_cursor.col > 0) { curwin->w_cursor.col--; - (void)del_char(false); // delete the '^' or '0' + del_char(false); // delete the '^' or '0' // In Replace mode, restore the characters that '^' or '0' replaced. if (State & REPLACE_FLAG) { replace_pop_ins(); @@ -3666,7 +3686,7 @@ static void ins_bs_one(colnr_T *vcolp) replace_do_bs(-1); } } else { - (void)del_char(false); + del_char(false); } } @@ -3707,7 +3727,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) if (stop_arrow() == FAIL) { return false; } - int in_indent = inindent(0); + bool in_indent = inindent(0); if (in_indent) { can_cindent = false; } @@ -3765,9 +3785,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) if (has_format_option(FO_AUTO) && has_format_option(FO_WHITE_PAR)) { char *ptr = ml_get_buf_mut(curbuf, curwin->w_cursor.lnum); - int len; - - len = (int)strlen(ptr); + int len = (int)strlen(ptr); if (len > 0 && ptr[len - 1] == ' ') { ptr[len - 1] = NUL; } @@ -3917,7 +3935,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) char *p0 = get_cursor_pos_ptr(); has_composing = utf_composinglike(p0, p0 + utf_ptr2len(p0)); } - (void)del_char(false); + del_char(false); // If there are combining characters and 'delcombine' is set // move the cursor back. Don't back up before the base character. if (has_composing) { @@ -4061,7 +4079,7 @@ static void ins_s_left(void) if (!end_change) { AppendCharToRedobuff(K_S_LEFT); } - (void)bck_word(1, false, false); + bck_word(1, false, false); curwin->w_set_curswant = true; } else { vim_beep(BO_CRSR); @@ -4120,7 +4138,7 @@ static void ins_s_right(void) if (!end_change) { AppendCharToRedobuff(K_S_RIGHT); } - (void)fwd_word(1, false, 0); + fwd_word(1, false, 0); curwin->w_set_curswant = true; } else { vim_beep(BO_CRSR); @@ -4233,7 +4251,7 @@ static bool ins_tab(void) return false; } - int ind = inindent(0); + bool ind = inindent(0); if (ind) { can_cindent = false; } @@ -4344,14 +4362,16 @@ static bool ins_tab(void) getvcol(curwin, cursor, &want_vcol, NULL, NULL); char *tab = "\t"; - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, 0, vcol, tab, tab); + int32_t tab_v = (uint8_t)(*tab); + + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, 0, tab); // Use as many TABs as possible. Beware of 'breakindent', 'showbreak' // and 'linebreak' adding extra virtual columns. while (ascii_iswhite(*ptr)) { - int i = lbr_chartabsize(&cts); - if (cts.cts_vcol + i > want_vcol) { + int i = win_charsize(cstype, vcol, tab, tab_v, &csarg).width; + if (vcol + i > want_vcol) { break; } if (*ptr != TAB) { @@ -4366,23 +4386,18 @@ static bool ins_tab(void) } fpos.col++; ptr++; - cts.cts_vcol += i; + vcol += i; } - vcol = cts.cts_vcol; - clear_chartabsize_arg(&cts); if (change_col >= 0) { int repl_off = 0; // Skip over the spaces we need. - init_chartabsize_arg(&cts, curwin, 0, vcol, ptr, ptr); - while (cts.cts_vcol < want_vcol && *cts.cts_ptr == ' ') { - cts.cts_vcol += lbr_chartabsize(&cts); - cts.cts_ptr++; + cstype = init_charsize_arg(&csarg, curwin, 0, ptr); + while (vcol < want_vcol && *ptr == ' ') { + vcol += win_charsize(cstype, vcol, ptr, (uint8_t)(' '), &csarg).width; + ptr++; repl_off++; } - ptr = cts.cts_ptr; - vcol = cts.cts_vcol; - clear_chartabsize_arg(&cts); if (vcol > want_vcol) { // Must have a char with 'showbreak' just before it. @@ -4553,8 +4568,6 @@ static int ins_digraph(void) // Returns the char to be inserted, or NUL if none found. int ins_copychar(linenr_T lnum) { - char *ptr; - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { vim_beep(BO_COPY); return NUL; @@ -4562,24 +4575,22 @@ int ins_copychar(linenr_T lnum) // try to advance to the cursor column validate_virtcol(); + int const end_vcol = curwin->w_virtcol; char *line = ml_get(lnum); - char *prev_ptr = line; - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, lnum, 0, line, line); - while (cts.cts_vcol < curwin->w_virtcol && *cts.cts_ptr != NUL) { - prev_ptr = cts.cts_ptr; - cts.cts_vcol += lbr_chartabsize_adv(&cts); - } - - if (cts.cts_vcol > curwin->w_virtcol) { - ptr = prev_ptr; - } else { - ptr = cts.cts_ptr; + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, lnum, line); + StrCharInfo ci = utf_ptr2StrCharInfo(line); + int vcol = 0; + while (vcol < end_vcol && *ci.ptr != NUL) { + vcol += win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + if (vcol > end_vcol) { + break; + } + ci = utfc_next(ci); } - clear_chartabsize_arg(&cts); - int c = utf_ptr2char(ptr); + int c = ci.chr.value < 0 ? (uint8_t)(*ci.ptr) : ci.chr.value; if (c == NUL) { vim_beep(BO_COPY); } @@ -4656,7 +4667,7 @@ static void ins_try_si(int c) if (State & VREPLACE_FLAG) { change_indent(INDENT_SET, i, false, NUL, true); } else { - (void)set_indent(i, SIN_CHANGED); + set_indent(i, SIN_CHANGED); } } else if (curwin->w_cursor.col > 0) { // when inserting '{' after "O" reduce indent, but not @@ -4688,7 +4699,7 @@ static void ins_try_si(int c) if (curwin->w_cursor.col > 0 && can_si && c == '#' && inindent(0)) { // remember current indent for next line old_indent = get_indent(); - (void)set_indent(0, SIN_CHANGED); + set_indent(0, SIN_CHANGED); } // Adjust ai_col, the char at this position can be deleted. @@ -4722,6 +4733,10 @@ static char *do_insert_char_pre(int c) char buf[MB_MAXBYTES + 1]; const int save_State = State; + if (c == Ctrl_RSB) { + return NULL; + } + // Return quickly when there is nothing to do. if (!has_event(EVENT_INSERTCHARPRE)) { return NULL; diff --git a/src/nvim/edit.h b/src/nvim/edit.h index 434b653f7b..2f15e737be 100644 --- a/src/nvim/edit.h +++ b/src/nvim/edit.h @@ -1,8 +1,8 @@ #pragma once #include "nvim/autocmd_defs.h" // IWYU pragma: keep -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep /// Values for in_cinkeys() enum { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f4479d06a6..7e3060100c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7,13 +7,14 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> -#include <sys/types.h> +#include <uv.h> #include "auto/config.h" #include "nvim/api/private/converter.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/channel.h" @@ -29,29 +30,31 @@ #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" +#include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/process.h" +#include "nvim/event/time.h" #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" -#include "nvim/ex_session.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid_defs.h" #include "nvim/hashtab.h" #include "nvim/highlight_group.h" #include "nvim/insexpand.h" #include "nvim/keycodes.h" -#include "nvim/lib/queue.h" +#include "nvim/lib/queue_defs.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/map_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -64,8 +67,10 @@ #include "nvim/optionstr.h" #include "nvim/os/fileio.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/lang.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/shell.h" #include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" @@ -73,13 +78,16 @@ #include "nvim/profile.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/tag.h" #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" +#include "nvim/ui_defs.h" #include "nvim/usercmd.h" #include "nvim/version.h" #include "nvim/vim_defs.h" @@ -89,8 +97,6 @@ #define DICT_MAXNEST 100 // maximum nesting of lists and dicts -#define MAX_CALLBACK_DEPTH 20 - static const char *e_missbrac = N_("E111: Missing ']'"); static const char *e_list_end = N_("E697: Missing end of List ']': %s"); static const char e_cannot_slice_dictionary[] @@ -188,6 +194,7 @@ static struct vimvar { VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT + VV_RO), VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), + VV(VV_TERMREQUEST, "termrequest", VAR_STRING, VV_RO), VV(VV_FNAME, "fname", VAR_STRING, VV_RO), VV(VV_LANG, "lang", VAR_STRING, VV_RO), VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), @@ -301,11 +308,12 @@ static partial_T *vvlua_partial; /// v: hashtab #define vimvarht vimvardict.dv_hashtab -/// Enum used by filter(), map() and mapnew() +/// Enum used by filter(), map(), mapnew() and foreach() typedef enum { FILTERMAP_FILTER, FILTERMAP_MAP, FILTERMAP_MAPNEW, + FILTERMAP_FOREACH, } filtermap_T; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -500,6 +508,10 @@ static void evalvars_clear(void) p->vv_list = NULL; } } + + partial_unref(vvlua_partial); + vimvars[VV_LUA].vv_partial = vvlua_partial = NULL; + hash_clear(&vimvarht); hash_init(&vimvarht); // garbage_collect() will access it hash_clear(&compat_hashtab); @@ -528,7 +540,7 @@ void eval_clear(void) free_autoload_scriptnames(); // unreferenced lists and dicts - (void)garbage_collect(false); + garbage_collect(false); // functions not garbage collected free_all_functions(); @@ -559,7 +571,7 @@ static char *redir_varname = NULL; /// @param append append to an existing variable /// /// @return OK if successfully completed the setup. FAIL otherwise. -int var_redir_start(char *name, int append) +int var_redir_start(char *name, bool append) { // Catch a bad name early. if (!eval_isnamec1(*name)) { @@ -677,7 +689,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to, set_vim_var_string(VV_CC_TO, enc_to, -1); set_vim_var_string(VV_FNAME_IN, fname_from, -1); set_vim_var_string(VV_FNAME_OUT, fname_to, -1); - sctx_T *ctx = get_option_sctx("charconvert"); + sctx_T *ctx = get_option_sctx(kOptCharconvert); if (ctx != NULL) { current_sctx = *ctx; } @@ -706,7 +718,7 @@ void eval_diff(const char *const origfile, const char *const newfile, const char set_vim_var_string(VV_FNAME_NEW, newfile, -1); set_vim_var_string(VV_FNAME_OUT, outfile, -1); - sctx_T *ctx = get_option_sctx("diffexpr"); + sctx_T *ctx = get_option_sctx(kOptDiffexpr); if (ctx != NULL) { current_sctx = *ctx; } @@ -728,7 +740,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch set_vim_var_string(VV_FNAME_DIFF, difffile, -1); set_vim_var_string(VV_FNAME_OUT, outfile, -1); - sctx_T *ctx = get_option_sctx("patchexpr"); + sctx_T *ctx = get_option_sctx(kOptPatchexpr); if (ctx != NULL) { current_sctx = *ctx; } @@ -746,11 +758,14 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) { *evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE }; - if (eap != NULL) { - if (getline_equal(eap->getline, eap->cookie, getsourceline)) { - evalarg->eval_getline = eap->getline; - evalarg->eval_cookie = eap->cookie; - } + + if (eap == NULL) { + return; + } + + if (getline_equal(eap->getline, eap->cookie, getsourceline)) { + evalarg->eval_getline = eap->getline; + evalarg->eval_cookie = eap->cookie; } } @@ -760,7 +775,7 @@ void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) /// @param skip only parse, don't execute /// /// @return true or false. -int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip) +bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip) { typval_T tv; bool retval = false; @@ -1084,17 +1099,19 @@ bool is_compatht(const hashtab_T *ht) } /// Prepare v: variable "idx" to be used. -/// Save the current typeval in "save_tv". +/// Save the current typeval in "save_tv" and clear it. /// When not used yet add the variable to the v: hashtable. void prepare_vimvar(int idx, typval_T *save_tv) { *save_tv = vimvars[idx].vv_tv; + vimvars[idx].vv_str = NULL; // don't free it now if (vimvars[idx].vv_type == VAR_UNKNOWN) { hash_add(&vimvarht, vimvars[idx].vv_di.di_key); } } /// Restore v: variable "idx" to typeval "save_tv". +/// Note that the v: variable must have been cleared already. /// When no longer defined, remove the variable from the v: hashtable. void restore_vimvar(int idx, typval_T *save_tv) { @@ -1130,7 +1147,7 @@ list_T *eval_spell_expr(char *badword, char *expr) if (p_verbose == 0) { emsg_off++; } - sctx_T *ctx = get_option_sctx("spellsuggest"); + sctx_T *ctx = get_option_sctx(kOptSpellsuggest); if (ctx != NULL) { current_sctx = *ctx; } @@ -1274,7 +1291,7 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv) int eval_foldexpr(win_T *wp, int *cp) { const sctx_T saved_sctx = current_sctx; - const bool use_sandbox = was_set_insecurely(wp, "foldexpr", OPT_LOCAL); + const bool use_sandbox = was_set_insecurely(wp, kOptFoldexpr, OPT_LOCAL); char *arg = wp->w_p_fde; current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx; @@ -1322,7 +1339,7 @@ int eval_foldexpr(win_T *wp, int *cp) /// Evaluate 'foldtext', returning an Array or a String (NULL_STRING on failure). Object eval_foldtext(win_T *wp) { - const bool use_sandbox = was_set_insecurely(wp, "foldtext", OPT_LOCAL); + const bool use_sandbox = was_set_insecurely(wp, kOptFoldtext, OPT_LOCAL); char *arg = wp->w_p_fdt; funccal_entry_T funccal_entry; @@ -1338,7 +1355,7 @@ Object eval_foldtext(win_T *wp) retval = STRING_OBJ(NULL_STRING); } else { if (tv.v_type == VAR_LIST) { - retval = vim_to_object(&tv); + retval = vim_to_object(&tv, NULL, false); } else { retval = STRING_OBJ(cstr_to_string(tv_get_string(&tv))); } @@ -1725,7 +1742,7 @@ void clear_lval(lval_T *lp) /// @param endp points to just after the parsed name. /// @param op NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", /// "%" for "%=", "." for ".=" or "=" for "=". -void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool is_const, +void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, bool copy, const bool is_const, const char *op) { int cc; @@ -1794,8 +1811,8 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool return; } - (void)tv_list_assign_range(lp->ll_list, rettv->vval.v_list, - lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name); + tv_list_assign_range(lp->ll_list, rettv->vval.v_list, + lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name); } else { typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; @@ -2312,20 +2329,22 @@ static int eval_func(char **const arg, evalarg_T *const evalarg, char *const nam /// After using "evalarg" filled from "eap": free the memory. void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) { - if (evalarg != NULL) { - if (evalarg->eval_tofree != NULL) { - if (eap != NULL) { - // We may need to keep the original command line, e.g. for - // ":let" it has the variable names. But we may also need the - // new one, "nextcmd" points into it. Keep both. - xfree(eap->cmdline_tofree); - eap->cmdline_tofree = *eap->cmdlinep; - *eap->cmdlinep = evalarg->eval_tofree; - } else { - xfree(evalarg->eval_tofree); - } - evalarg->eval_tofree = NULL; + if (evalarg == NULL) { + return; + } + + if (evalarg->eval_tofree != NULL) { + if (eap != NULL) { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + xfree(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + } else { + xfree(evalarg->eval_tofree); } + evalarg->eval_tofree = NULL; } } @@ -2858,7 +2877,8 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) } else { bool error = false; varnumber_T n1, n2; - float_T f1 = 0, f2 = 0; + float_T f1 = 0; + float_T f2 = 0; if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; @@ -2949,7 +2969,8 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan } varnumber_T n1, n2; - float_T f1 = 0, f2 = 0; + float_T f1 = 0; + float_T f2 = 0; bool error = false; const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); if (evaluate) { @@ -3165,12 +3186,10 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // Register contents: @r. case '@': (*arg)++; + int regname = mb_cptr2char_adv((const char**) arg); if (evaluate) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc); - } - if (**arg != NUL) { - (*arg)++; + rettv->vval.v_string = get_reg_contents(regname, kGRegExprSrc); } break; @@ -3217,6 +3236,13 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan } else { // skip the name check_vars(s, (size_t)len); + // If evaluate is false rettv->v_type was not set, but it's needed + // in handle_subscript() to parse v:lua, so set it here. + if (rettv->v_type == VAR_UNKNOWN && !evaluate && strnequal(s, "v:lua.", 6)) { + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = vvlua_partial; + rettv->vval.v_partial->pt_refcount++; + } ret = OK; } } @@ -3421,7 +3447,7 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const int len; char *name = *arg; char *lua_funcname = NULL; - if (strncmp(name, "v:lua.", 6) == 0) { + if (strnequal(name, "v:lua.", 6)) { lua_funcname = name + 6; *arg = (char *)skip_luafunc_name(lua_funcname); *arg = skipwhite(*arg); // to detect trailing whitespace later @@ -3610,12 +3636,14 @@ static int check_can_index(typval_T *rettv, bool evaluate, bool verbose) /// slice() function void f_slice(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - if (check_can_index(argvars, true, false) == OK) { - tv_copy(argvars, rettv); - eval_index_inner(rettv, true, argvars + 1, - argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2, - true, NULL, 0, false); + if (check_can_index(argvars, true, false) != OK) { + return; } + + tv_copy(argvars, rettv); + eval_index_inner(rettv, true, argvars + 1, + argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2, + true, NULL, 0, false); } /// Apply index or range to "rettv". @@ -3731,7 +3759,11 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, keylen); if (item == NULL && verbose) { - semsg(_(e_dictkey), key); + if (keylen > 0) { + semsg(_(e_dictkey_len), keylen, key); + } else { + semsg(_(e_dictkey), key); + } } if (item == NULL || tv_is_luafunc(&item->di_tv)) { return FAIL; @@ -3759,10 +3791,12 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua FUNC_ATTR_NONNULL_ARG(1) { const bool working = (**arg == '+'); // has("+option") + OptIndex opt_idx; int scope; // Isolate the option name and find its value. - char *option_end = (char *)find_option_end(arg, &scope); + char *const option_end = (char *)find_option_var_end(arg, &opt_idx, &scope); + if (option_end == NULL) { if (rettv != NULL) { semsg(_("E112: Option name missing: %s"), *arg); @@ -3775,38 +3809,26 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua return OK; } - int ret = OK; - bool hidden; char c = *option_end; *option_end = NUL; - OptVal value = get_option_value(*arg, NULL, scope, &hidden); - if (rettv != NULL) { - switch (value.type) { - case kOptValTypeNil: + int ret = OK; + bool is_tty_opt = is_tty_option(*arg); + + if (opt_idx == kOptInvalid && !is_tty_opt) { + // Only give error if result is going to be used. + if (rettv != NULL) { semsg(_("E113: Unknown option: %s"), *arg); - ret = FAIL; - break; - case kOptValTypeBoolean: - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = value.data.boolean; - break; - case kOptValTypeNumber: - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = value.data.number; - break; - case kOptValTypeString: - rettv->v_type = VAR_STRING; - rettv->vval.v_string = value.data.string.data; - break; } - } else { - // Value isn't being used, free it. - optval_free(value); - if (value.type == kOptValTypeNil || (working && hidden)) { - ret = FAIL; - } + ret = FAIL; + } else if (rettv != NULL) { + OptVal value = is_tty_opt ? get_tty_option(*arg) : get_option_value(opt_idx, scope); + assert(value.type != kOptValTypeNil); + + *rettv = optval_as_tv(value, true); + } else if (working && !is_tty_opt && is_option_hidden(opt_idx)) { + ret = FAIL; } *option_end = c; // put back for error messages @@ -4238,7 +4260,11 @@ static void partial_free(partial_T *pt) /// becomes zero. void partial_unref(partial_T *pt) { - if (pt != NULL && --pt->pt_refcount <= 0) { + if (pt == NULL) { + return; + } + + if (--pt->pt_refcount <= 0) { partial_free(pt); } } @@ -4483,7 +4509,7 @@ bool garbage_collect(bool testing) // registers (ShaDa additional data) { - const void *reg_iter = NULL; + iter_register_T reg_iter = ITER_REGISTER_NULL; do { yankreg_T reg; char name = NUL; @@ -4492,7 +4518,7 @@ bool garbage_collect(bool testing) if (name != NUL) { ABORTING(set_ref_dict)(reg.additional_data, copyID); } - } while (reg_iter != NULL); + } while (reg_iter != ITER_REGISTER_NULL); } // global marks (ShaDa additional data) @@ -4548,7 +4574,7 @@ bool garbage_collect(bool testing) // history items (ShaDa additional elements) if (p_hi) { - for (HistoryType i = 0; i < HIST_COUNT; i++) { + for (int i = 0; i < HIST_COUNT; i++) { const void *iter = NULL; do { histentry_T hist; @@ -4707,7 +4733,7 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack) /// @param ht_stack Used to add hashtabs to be marked. Can be NULL. /// /// @returns true if setting references failed somehow. -bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack) +bool set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack) FUNC_ATTR_WARN_UNUSED_RESULT { bool abort = false; @@ -4784,7 +4810,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack // Didn't see this list yet. ll->lv_copyID = copyID; if (list_stack == NULL) { - abort = set_ref_in_list(ll, copyID, ht_stack); + abort = set_ref_in_list_items(ll, copyID, ht_stack); } else { list_stack_T *const newitem = xmalloc(sizeof(list_stack_T)); newitem->list = ll; @@ -5026,7 +5052,7 @@ size_t string2float(const char *const text, float_T *const ret_value) return 3; } if (STRNICMP(text, "-inf", 3) == 0) { - *ret_value = (float_T) - INFINITY; + *ret_value = (float_T)(-INFINITY); return 4; } if (STRNICMP(text, "nan", 3) == 0) { @@ -5089,7 +5115,8 @@ void assert_error(garray_T *gap) tv_list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, (ptrdiff_t)gap->ga_len); } -/// Implementation of map() and filter() for a Dict. +/// Implementation of map(), filter(), foreach() for a Dict. Apply "expr" to +/// every item in Dict "d" and return the result in "rettv". static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_name, const char *arg_errmsg, typval_T *expr, typval_T *rettv) { @@ -5157,7 +5184,7 @@ static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_n d->dv_lock = prev_lock; } -/// Implementation of map() and filter() for a Blob. +/// Implementation of map(), filter(), foreach() for a Blob. static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, const char *arg_errmsg, typval_T *rettv) { @@ -5200,20 +5227,22 @@ static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *e || did_emsg) { break; } - if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) { - tv_clear(&newtv); - emsg(_(e_invalblob)); - break; - } - if (filtermap != FILTERMAP_FILTER) { - if (newtv.vval.v_number != val) { - tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number); + if (filtermap != FILTERMAP_FOREACH) { + if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) { + tv_clear(&newtv); + emsg(_(e_invalblob)); + break; + } + if (filtermap != FILTERMAP_FILTER) { + if (newtv.vval.v_number != val) { + tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number); + } + } else if (rem) { + char *const p = (char *)blob_arg->bv_ga.ga_data; + memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1)); + b->bv_ga.ga_len--; + i--; } - } else if (rem) { - char *const p = (char *)blob_arg->bv_ga.ga_data; - memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1)); - b->bv_ga.ga_len--; - i--; } idx++; } @@ -5221,7 +5250,7 @@ static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *e b->bv_lock = prev_lock; } -/// Implementation of map() and filter() for a String. +/// Implementation of map(), filter(), foreach() for a String. static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *expr, typval_T *rettv) { @@ -5250,7 +5279,8 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T * tv_clear(&newtv); tv_clear(&tv); break; - } else if (filtermap != FILTERMAP_FILTER) { + } + if (filtermap == FILTERMAP_MAP || filtermap == FILTERMAP_MAPNEW) { if (newtv.v_type != VAR_STRING) { tv_clear(&newtv); tv_clear(&tv); @@ -5259,7 +5289,7 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T * } else { ga_concat(&ga, newtv.vval.v_string); } - } else if (!rem) { + } else if (filtermap == FILTERMAP_FOREACH || !rem) { ga_concat(&ga, tv.vval.v_string); } @@ -5272,7 +5302,8 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T * rettv->vval.v_string = ga.ga_data; } -/// Implementation of map() and filter() for a List. +/// Implementation of map(), filter(), foreach() for a List. Apply "expr" to +/// every item in List "l" and return the result in "rettv". static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_name, const char *arg_errmsg, typval_T *expr, typval_T *rettv) { @@ -5336,21 +5367,25 @@ static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_n tv_list_set_lock(l, prev_lock); } -/// Implementation of map() and filter(). +/// Implementation of map(), filter() and foreach(). static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) { const char *const func_name = (filtermap == FILTERMAP_MAP ? "map()" : (filtermap == FILTERMAP_MAPNEW ? "mapnew()" - : "filter()")); + : (filtermap == FILTERMAP_FILTER + ? "filter()" + : "foreach()"))); const char *const arg_errmsg = (filtermap == FILTERMAP_MAP ? N_("map() argument") : (filtermap == FILTERMAP_MAPNEW ? N_("mapnew() argument") - : N_("filter() argument"))); + : (filtermap == FILTERMAP_FILTER + ? N_("filter() argument") + : N_("foreach() argument")))); - // map() and filter() return the first argument, also on failure. + // map(), filter(), foreach() return the first argument, also on failure. if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING) { tv_copy(&argvars[0], rettv); } @@ -5367,38 +5402,40 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap // On type errors, the preceding call has already displayed an error // message. Avoid a misleading error message for an empty string that // was not passed as argument. - if (expr->v_type != VAR_UNKNOWN) { - typval_T save_val; - prepare_vimvar(VV_VAL, &save_val); - - // We reset "did_emsg" to be able to detect whether an error - // occurred during evaluation of the expression. - int save_did_emsg = did_emsg; - did_emsg = false; - - typval_T save_key; - prepare_vimvar(VV_KEY, &save_key); - if (argvars[0].v_type == VAR_DICT) { - filter_map_dict(argvars[0].vval.v_dict, filtermap, func_name, - arg_errmsg, expr, rettv); - } else if (argvars[0].v_type == VAR_BLOB) { - filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, arg_errmsg, rettv); - } else if (argvars[0].v_type == VAR_STRING) { - filter_map_string(tv_get_string(&argvars[0]), filtermap, expr, rettv); - } else { - assert(argvars[0].v_type == VAR_LIST); - filter_map_list(argvars[0].vval.v_list, filtermap, func_name, - arg_errmsg, expr, rettv); - } + if (expr->v_type == VAR_UNKNOWN) { + return; + } - restore_vimvar(VV_KEY, &save_key); - restore_vimvar(VV_VAL, &save_val); + typval_T save_val; + prepare_vimvar(VV_VAL, &save_val); + + // We reset "did_emsg" to be able to detect whether an error + // occurred during evaluation of the expression. + int save_did_emsg = did_emsg; + did_emsg = false; - did_emsg |= save_did_emsg; + typval_T save_key; + prepare_vimvar(VV_KEY, &save_key); + if (argvars[0].v_type == VAR_DICT) { + filter_map_dict(argvars[0].vval.v_dict, filtermap, func_name, + arg_errmsg, expr, rettv); + } else if (argvars[0].v_type == VAR_BLOB) { + filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, arg_errmsg, rettv); + } else if (argvars[0].v_type == VAR_STRING) { + filter_map_string(tv_get_string(&argvars[0]), filtermap, expr, rettv); + } else { + assert(argvars[0].v_type == VAR_LIST); + filter_map_list(argvars[0].vval.v_list, filtermap, func_name, + arg_errmsg, expr, rettv); } + + restore_vimvar(VV_KEY, &save_key); + restore_vimvar(VV_VAL, &save_val); + + did_emsg |= save_did_emsg; } -/// Handle one item for map() and filter(). +/// Handle one item for map(), filter(), foreach(). /// Sets v:val to "tv". Caller must set v:key. /// /// @param tv original value @@ -5413,6 +5450,17 @@ static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filter int retval = FAIL; tv_copy(tv, &vimvars[VV_VAL].vv_tv); + + newtv->v_type = VAR_UNKNOWN; + if (filtermap == FILTERMAP_FOREACH && expr->v_type == VAR_STRING) { + // foreach() is not limited to an expression + do_cmdline_cmd(expr->vval.v_string); + if (!did_emsg) { + retval = OK; + } + goto theend; + } + argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; if (eval_expr_typval(expr, false, argv, 2, newtv) == FAIL) { @@ -5429,6 +5477,8 @@ static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filter if (error) { goto theend; } + } else if (filtermap == FILTERMAP_FOREACH) { + tv_clear(newtv); } retval = OK; theend: @@ -5454,6 +5504,12 @@ void f_mapnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) filter_map(argvars, rettv, FILTERMAP_MAPNEW); } +/// "foreach()" function +void f_foreach(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + filter_map(argvars, rettv, FILTERMAP_FOREACH); +} + /// "function()" function /// "funcref()" function void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) @@ -6061,7 +6117,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co typval_T *const rettv) FUNC_ATTR_NONNULL_ALL { - if (callback_depth > MAX_CALLBACK_DEPTH) { + if (callback_depth > p_mfd) { emsg(_(e_command_too_recursive)); return false; } @@ -6092,8 +6148,8 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co break; case kCallbackLua: - rv = nlua_call_ref(callback->data.luaref, NULL, args, false, NULL); - return (rv.type == kObjectTypeBoolean && rv.data.boolean == true); + rv = nlua_call_ref(callback->data.luaref, NULL, args, kRetNilBool, NULL, NULL); + return LUARET_TRUTHY(rv); case kCallbackNone: return false; @@ -7563,6 +7619,10 @@ int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const e const char *lua_funcname = NULL; if (tv_is_luafunc(rettv)) { + if (!evaluate) { + tv_clear(rettv); + } + if (**arg != '.') { tv_clear(rettv); ret = FAIL; @@ -8135,13 +8195,14 @@ void ex_execute(exarg_T *eap) eap->nextcmd = check_nextcmd(arg); } -/// Skip over the name of an option: "&option", "&g:option" or "&l:option". +/// Skip over the name of an option variable: "&option", "&g:option" or "&l:option". /// -/// @param arg points to the "&" or '+' when called, to "option" when returning. +/// @param[in,out] arg Points to the "&" or '+' when called, to "option" when returning. +/// @param[out] opt_idxp Set to option index in options[] table. +/// @param[out] scope Set to option scope. /// -/// @return NULL when no option name found. Otherwise pointer to the char -/// after the option name. -const char *find_option_end(const char **const arg, int *const scope) +/// @return NULL when no option name found. Otherwise pointer to the char after the option name. +const char *find_option_var_end(const char **const arg, OptIndex *const opt_idxp, int *const scope) { const char *p = *arg; @@ -8156,22 +8217,12 @@ const char *find_option_end(const char **const arg, int *const scope) *scope = 0; } - if (!ASCII_ISALPHA(*p)) { - return NULL; - } - *arg = p; - - if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) { - p += 4; // t_xx/termcap option - } else { - while (ASCII_ISALPHA(*p)) { - p++; - } - } - return p; + const char *end = find_option_end(p, opt_idxp); + *arg = end == NULL ? *arg : p; + return end; } -static var_flavour_T var_flavour(char *varname) +var_flavour_T var_flavour(char *varname) FUNC_ATTR_PURE { char *p = varname; @@ -8187,48 +8238,6 @@ static var_flavour_T var_flavour(char *varname) return VAR_FLAVOUR_DEFAULT; } -/// Iterate over global variables -/// -/// @warning No modifications to global variable dictionary must be performed -/// while iteration is in progress. -/// -/// @param[in] iter Iterator. Pass NULL to start iteration. -/// @param[out] name Variable name. -/// @param[out] rettv Variable value. -/// -/// @return Pointer that needs to be passed to next `var_shada_iter` invocation -/// or NULL to indicate that iteration is over. -const void *var_shada_iter(const void *const iter, const char **const name, typval_T *rettv, - var_flavour_T flavour) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2, 3) -{ - const hashitem_T *hi; - const hashitem_T *hifirst = globvarht.ht_array; - const size_t hinum = (size_t)globvarht.ht_mask + 1; - *name = NULL; - if (iter == NULL) { - hi = globvarht.ht_array; - while ((size_t)(hi - hifirst) < hinum - && (HASHITEM_EMPTY(hi) - || !(var_flavour(hi->hi_key) & flavour))) { - hi++; - } - if ((size_t)(hi - hifirst) == hinum) { - return NULL; - } - } else { - hi = (const hashitem_T *)iter; - } - *name = TV_DICT_HI2DI(hi)->di_key; - tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); - while ((size_t)(++hi - hifirst) < hinum) { - if (!HASHITEM_EMPTY(hi) && (var_flavour(hi->hi_key) & flavour)) { - return hi; - } - } - return NULL; -} - void var_set_global(const char *const name, typval_T vartv) { funccal_entry_T funccall_entry; @@ -8238,50 +8247,6 @@ void var_set_global(const char *const name, typval_T vartv) restore_funccal(); } -int store_session_globals(FILE *fd) -{ - TV_DICT_ITER(&globvardict, this_var, { - if ((this_var->di_tv.v_type == VAR_NUMBER - || this_var->di_tv.v_type == VAR_STRING) - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - // Escape special characters with a backslash. Turn a LF and - // CR into \n and \r. - char *const p = vim_strsave_escaped(tv_get_string(&this_var->di_tv), "\\\"\n\r"); - for (char *t = p; *t != NUL; t++) { - if (*t == '\n') { - *t = 'n'; - } else if (*t == '\r') { - *t = 'r'; - } - } - if ((fprintf(fd, "let %s = %c%s%c", - this_var->di_key, - ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' '), - p, - ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' ')) < 0) - || put_eol(fd) == FAIL) { - xfree(p); - return FAIL; - } - xfree(p); - } else if (this_var->di_tv.v_type == VAR_FLOAT - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - float_T f = this_var->di_tv.vval.v_float; - int sign = ' '; - - if (f < 0) { - f = -f; - sign = '-'; - } - if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0) - || put_eol(fd) == FAIL) { - return FAIL; - } - } - }); - return OK; -} - /// Display script name where an item was last set. /// Should only be invoked when 'verbose' is non-zero. void last_set_msg(sctx_T script_ctx) @@ -8298,21 +8263,24 @@ void last_set_msg(sctx_T script_ctx) /// Should only be invoked when 'verbose' is non-zero. void option_last_set_msg(LastSet last_set) { - if (last_set.script_ctx.sc_sid != 0) { - bool should_free; - char *p = get_scriptname(last_set, &should_free); - verbose_enter(); - msg_puts(_("\n\tLast set from ")); - msg_puts(p); - if (last_set.script_ctx.sc_lnum > 0) { - msg_puts(_(line_msg)); - msg_outnum(last_set.script_ctx.sc_lnum); - } - if (should_free) { - xfree(p); - } - verbose_leave(); + if (last_set.script_ctx.sc_sid == 0) { + return; } + + bool should_free; + char *p = get_scriptname(last_set, &should_free); + + verbose_enter(); + msg_puts(_("\n\tLast set from ")); + msg_puts(p); + if (last_set.script_ctx.sc_lnum > 0) { + msg_puts(_(line_msg)); + msg_outnum(last_set.script_ctx.sc_lnum); + } + if (should_free) { + xfree(p); + } + verbose_leave(); } // reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal, @@ -8683,9 +8651,9 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char int i = (int)(regmatch.startp[0] - tail); memmove((char *)ga.ga_data + ga.ga_len, tail, (size_t)i); // add the substituted text - (void)vim_regsub(®match, sub, expr, - (char *)ga.ga_data + ga.ga_len + i, sublen, - REGSUB_COPY | REGSUB_MAGIC); + vim_regsub(®match, sub, expr, + (char *)ga.ga_data + ga.ga_len + i, sublen, + REGSUB_COPY | REGSUB_MAGIC); ga.ga_len += i + sublen - 1; tail = regmatch.endp[0]; if (*tail == NUL) { @@ -8712,7 +8680,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char // If it's still empty it was changed and restored, need to restore in // the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); + set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0); } free_string_option(save_cpo); } @@ -8825,7 +8793,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo funcexe.fe_firstline = curwin->w_cursor.lnum; funcexe.fe_lastline = curwin->w_cursor.lnum; funcexe.fe_evaluate = true; - (void)call_func(func, name_len, &rettv, 2, argvars, &funcexe); + call_func(func, name_len, &rettv, 2, argvars, &funcexe); tv_list_unref(arguments); // Restore caller scope information @@ -8908,32 +8876,6 @@ void eval_fmt_source_name_line(char *buf, size_t bufsize) } } -/// ":checkhealth [plugins]" -void ex_checkhealth(exarg_T *eap) -{ - Error err = ERROR_INIT; - MAXSIZE_TEMP_ARRAY(args, 1); - ADD_C(args, CSTR_AS_OBJ(eap->arg)); - NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err); - if (!ERROR_SET(&err)) { - return; - } - - const char *vimruntime_env = os_getenv("VIMRUNTIME"); - if (vimruntime_env == NULL) { - emsg(_("E5009: $VIMRUNTIME is empty or unset")); - } else { - bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env); - if (rtp_ok) { - semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); - } else { - emsg(_("E5009: Invalid 'runtimepath'")); - } - } - semsg_multiline(err.msg); - api_clear_error(&err); -} - void invoke_prompt_callback(void) { typval_T rettv; diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 1fc2891917..d83af70ef7 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -4,17 +4,22 @@ #include <stddef.h> #include <stdint.h> -#include "nvim/buffer_defs.h" -#include "nvim/channel.h" -#include "nvim/cmdexpand_defs.h" +#include "nvim/channel_defs.h" // IWYU pragma: keep +#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" -#include "nvim/event/time.h" -#include "nvim/ex_cmds_defs.h" -#include "nvim/globals.h" +#include "nvim/eval_defs.h" // IWYU pragma: keep +#include "nvim/event/defs.h" +#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep +#include "nvim/grid_defs.h" // IWYU pragma: keep #include "nvim/hashtab_defs.h" #include "nvim/macros_defs.h" -#include "nvim/os/fileio.h" -#include "nvim/os/stdpaths_defs.h" +#include "nvim/mbyte_defs.h" // IWYU pragma: keep +#include "nvim/msgpack_rpc/channel_defs.h" // IWYU pragma: keep +#include "nvim/option_defs.h" // IWYU pragma: keep +#include "nvim/os/fileio_defs.h" // IWYU pragma: keep +#include "nvim/os/stdpaths_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep +#include "nvim/vim_defs.h" // IWYU pragma: keep #define COPYID_INC 2 #define COPYID_MASK (~0x1) @@ -44,7 +49,7 @@ // "exp_name" NULL or non-NULL, to be freed later. // "tv" points to the Dictionary typval_T // "newkey" is the key for the new item. -typedef struct lval_S { +typedef struct { const char *ll_name; ///< Start of variable name (can be NULL). size_t ll_name_len; ///< Length of the .ll_name. char *ll_exp_name; ///< NULL or expanded name in allocated memory. @@ -81,6 +86,7 @@ typedef enum { VV_THIS_SESSION, VV_VERSION, VV_LNUM, + VV_TERMREQUEST, VV_TERMRESPONSE, VV_FNAME, VV_LANG, @@ -173,24 +179,8 @@ typedef enum { VV_VIRTNUM, } VimVarIndex; -/// All recognized msgpack types -typedef enum { - kMPNil, - kMPBoolean, - kMPInteger, - kMPFloat, - kMPString, - kMPBinary, - kMPArray, - kMPMap, - kMPExt, -} MessagePackType; -#define LAST_MSGPACK_TYPE kMPExt - /// Array mapping values from MessagePackType to corresponding list pointers -extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1]; - -#undef LAST_MSGPACK_TYPE +extern const list_T *eval_msgpack_type_lists[NUM_MSGPACK_TYPES]; // Struct passed to get_v_event() and restore_v_event(). typedef struct { @@ -254,32 +244,17 @@ typedef enum { kDictListItems, ///< List dictionary contents: [keys, values]. } DictListType; -typedef int (*ex_unletlock_callback)(lval_T *, char *, exarg_T *, int); - // Used for checking if local variables or arguments used in a lambda. extern bool *eval_lavars_used; -/// Struct passed through eval() functions. -/// See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE. -typedef struct { - int eval_flags; ///< EVAL_ flag values below - - /// copied from exarg_T when "getline" is "getsourceline". Can be NULL. - LineGetter eval_getline; - void *eval_cookie; ///< argument for eval_getline() - - /// pointer to the last line obtained with getsourceline() - char *eval_tofree; -} evalarg_T; +// Character used as separated in autoload function/variable names. +#define AUTOLOAD_CHAR '#' /// Flag for expression evaluation. enum { EVAL_EVALUATE = 1, ///< when missing don't actually evaluate }; -// Character used as separated in autoload function/variable names. -#define AUTOLOAD_CHAR '#' - /// Passed to an eval() function to enable evaluation. EXTERN evalarg_T EVALARG_EVALUATE INIT( = { EVAL_EVALUATE, NULL, NULL, NULL }); diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 55f4721c3a..b7120d5dd5 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -17,7 +17,7 @@ --- @field deprecated? true --- @field returns? string|false --- @field returns_desc? string ---- @field signature string +--- @field signature? string --- @field desc? string --- @field params {[1]:string, [2]:string, [3]:string}[] --- @field lua? false Do not render type information @@ -809,7 +809,7 @@ M.funcs = { < ]=], name = 'bufname', - params = { { 'buf', 'any' } }, + params = { { 'buf', 'integer|string' } }, returns = 'string', signature = 'bufname([{buf}])', }, @@ -832,7 +832,7 @@ M.funcs = { ]=], name = 'bufnr', - params = { { 'buf', 'any' }, { 'create', 'any' } }, + params = { { 'buf', 'integer|string' }, { 'create', 'any' } }, returns = 'integer', signature = 'bufnr([{buf} [, {create}]])', }, @@ -1373,6 +1373,8 @@ M.funcs = { no item is selected when using the <Up> or <Down> keys) inserted Inserted string. [NOT IMPLEMENTED YET] + preview_winid Info floating preview window id. + preview_bufnr Info floating preview buffer id. *complete_info_mode* mode values are: @@ -2114,7 +2116,7 @@ M.funcs = { name = 'execute', params = { { 'command', 'string|string[]' }, - { 'silent', "''|'silent'|'silent!'" } + { 'silent', "''|'silent'|'silent!'" }, }, returns = 'string', signature = 'execute({command} [, {silent}])', @@ -2200,6 +2202,7 @@ M.funcs = { echo exists("*strftime") echo exists("*s:MyFunc") echo exists("*MyFunc") + echo exists("*v:lua.Func") echo exists("bufcount") echo exists(":Make") echo exists("#CursorHold") @@ -2346,9 +2349,20 @@ M.funcs = { ]=], name = 'expand', - params = { { 'string', 'string' }, { 'nosuf', 'boolean' }, { 'list', 'any' } }, - returns = 'string|string[]', + params = { { 'string', 'string' }, { 'nosuf', 'boolean' }, { 'list', 'nil|false' } }, signature = 'expand({string} [, {nosuf} [, {list}]])', + returns = 'string', + }, + expand__1 = { + args = { 3 }, + base = 1, + name = 'expand', + params = { + { 'string', 'string' }, + { 'nosuf', 'boolean' }, + { 'list', 'true|number|string|table' }, + }, + returns = 'string|string[]', }, expandcmd = { args = { 1, 2 }, @@ -2910,10 +2924,51 @@ M.funcs = { returns = 'string', signature = 'foldtextresult({lnum})', }, + foreach = { + args = 2, + base = 1, + desc = [=[ + {expr1} must be a |List|, |String|, |Blob| or |Dictionary|. + For each item in {expr1} execute {expr2}. {expr1} is not + modified; its values may be, as with |:lockvar| 1. |E741| + See |map()| and |filter()| to modify {expr1}. + + {expr2} must be a |string| or |Funcref|. + + If {expr2} is a |string|, inside {expr2} |v:val| has the value + of the current item. For a |Dictionary| |v:key| has the key + of the current item and for a |List| |v:key| has the index of + the current item. For a |Blob| |v:key| has the index of the + current byte. For a |String| |v:key| has the index of the + current character. + Examples: >vim + call foreach(mylist, 'let used[v:val] = v:true') + <This records the items that are in the {expr1} list. + + Note that {expr2} is the result of expression and is then used + as a command. Often it is good to use a |literal-string| to + avoid having to double backslashes. + + If {expr2} is a |Funcref| it must take two arguments: + 1. the key or the index of the current item. + 2. the value of the current item. + With a lambda you don't get an error if it only accepts one + argument. + If the function returns a value, it is ignored. + + Returns {expr1} in all cases. + When an error is encountered while executing {expr2} no + further items in {expr1} are processed. + When {expr2} is a Funcref errors inside a function are ignored, + unless it was defined with the "abort" flag. + ]=], + name = 'foreach', + params = { { 'expr1', 'any' }, { 'expr2', 'any' } }, + signature = 'foreach({expr1}, {expr2})', + }, foreground = { args = 0, params = {}, - signature = '', lua = false, }, fullcommand = { @@ -3164,6 +3219,8 @@ M.funcs = { bufnr Buffer number. changed TRUE if the buffer is modified. changedtick Number of changes made to the buffer. + command TRUE if the buffer belongs to the + command-line window |cmdwin|. hidden TRUE if the buffer is hidden. lastused Timestamp in seconds, like |localtime()|, when the buffer was @@ -3406,7 +3463,7 @@ M.funcs = { 32 mouse double click 64 mouse triple click 96 mouse quadruple click (== 32 + 64) - 128 command (Macintosh only) + 128 command (Mac) or super Only the modifiers that have not been included in the character itself are obtained. Thus Shift-a results in "A" without a modifier. Returns 0 if no modifiers are used. @@ -3607,6 +3664,7 @@ M.funcs = { help help subjects highlight highlight groups history |:history| suboptions + keymap keyboard mappings locale locale names (as output of locale -a) mapclear buffer argument mapping mapping name @@ -3915,9 +3973,16 @@ M.funcs = { |getbufoneline()| ]=], name = 'getline', - params = { { 'lnum', 'integer' }, { 'end', 'any' } }, - returns = 'string|string[]', + params = { { 'lnum', 'integer|string' }, { 'end', 'nil|false' } }, signature = 'getline({lnum} [, {end}])', + returns = 'string', + }, + getline__1 = { + args = { 2 }, + base = 1, + name = 'getline', + params = { { 'lnum', 'integer' }, { 'end', 'true|number|string|table' } }, + returns = 'string|string[]', }, getloclist = { args = { 1, 2 }, @@ -4246,9 +4311,16 @@ M.funcs = { ]=], name = 'getreg', - params = { { 'regname', 'string' }, { 'list', 'any' } }, - returns = 'string|string[]', + params = { { 'regname', 'string' }, { 'list', 'nil|false' } }, signature = 'getreg([{regname} [, 1 [, {list}]]])', + returns = 'string', + }, + getreg__1 = { + args = { 3 }, + base = 1, + name = 'getreg', + params = { { 'regname', 'string' }, { 'list', 'true|number|string|table' } }, + returns = 'string|string[]', }, getreginfo = { args = { 0, 1 }, @@ -4283,6 +4355,65 @@ M.funcs = { returns = 'table', signature = 'getreginfo([{regname}])', }, + getregion = { + args = { 2, 3 }, + base = 1, + desc = [=[ + Returns the list of strings from {pos1} to {pos2} from a + buffer. + + {pos1} and {pos2} must both be |List|s with four numbers. + See |getpos()| for the format of the list. It's possible + to specify positions from a different buffer, but please + note the limitations at |getregion-notes|. + + The optional argument {opts} is a Dict and supports the + following items: + + type Specify the region's selection type + (default: "v"): + "v" for |charwise| mode + "V" for |linewise| mode + "<CTRL-V>" for |blockwise-visual| mode + + exclusive If |TRUE|, use exclusive selection + for the end position + (default: follow 'selection') + + You can get the last selection type by |visualmode()|. + If Visual mode is active, use |mode()| to get the Visual mode + (e.g., in a |:vmap|). + This function is useful to get text starting and ending in + different columns, such as a |charwise-visual| selection. + + *getregion-notes* + Note that: + - Order of {pos1} and {pos2} doesn't matter, it will always + return content from the upper left position to the lower + right position. + - If 'virtualedit' is enabled and the region is past the end + of the lines, resulting lines are padded with spaces. + - If the region is blockwise and it starts or ends in the + middle of a multi-cell character, it is not included but + its selected part is substituted with spaces. + - If {pos1} and {pos2} are not in the same buffer, an empty + list is returned. + - {pos1} and {pos2} must belong to a |bufloaded()| buffer. + - It is evaluated in current window context, which makes a + difference if the buffer is displayed in a window with + different 'virtualedit' or 'list' values. + + Examples: > + :xnoremap <CR> + \ <Cmd>echom getregion( + \ getpos('v'), getpos('.'), #{ type: mode() })<CR> + < + ]=], + name = 'getregion', + params = { { 'pos1', 'table' }, { 'pos2', 'table' }, { 'opts', 'table' } }, + returns = 'string[]', + signature = 'getregion({pos1}, {pos2} [, {opts}])', + }, getregtype = { args = { 0, 1 }, base = 1, @@ -4519,7 +4650,7 @@ M.funcs = { name = 'getwininfo', params = { { 'winid', 'integer' } }, signature = 'getwininfo([{winid}])', - returns = 'vim.fn.getwininfo.ret.item[]' + returns = 'vim.fn.getwininfo.ret.item[]', }, getwinpos = { args = { 0, 1 }, @@ -5095,7 +5226,7 @@ M.funcs = { ]=], name = 'indent', - params = { { 'lnum', 'integer' } }, + params = { { 'lnum', 'integer|string' } }, returns = 'integer', signature = 'indent({lnum})', }, @@ -5735,8 +5866,7 @@ M.funcs = { Vim value. In the following cases it will output |msgpack-special-dict|: 1. Dictionary contains duplicate key. - 2. Dictionary contains empty key. - 3. String contains NUL byte. Two special dictionaries: for + 2. String contains NUL byte. Two special dictionaries: for dictionary and for string will be emitted in case string with NUL byte was a dictionary key. @@ -6216,10 +6346,22 @@ M.funcs = { { 'name', 'string' }, { 'mode', 'string' }, { 'abbr', 'boolean' }, - { 'dict', 'boolean' }, + { 'dict', 'false' }, }, - returns = 'string|table<string,any>', signature = 'maparg({name} [, {mode} [, {abbr} [, {dict}]]])', + returns = 'string', + }, + maparg__1 = { + args = { 4 }, + base = 1, + name = 'maparg', + params = { + { 'name', 'string' }, + { 'mode', 'string' }, + { 'abbr', 'boolean' }, + { 'dict', 'true' }, + }, + returns = 'string|table<string,any>', }, mapcheck = { args = { 1, 3 }, @@ -6297,7 +6439,7 @@ M.funcs = { ]], name = 'maplist', params = {}, - signature = 'maplist([{abbr}])' + signature = 'maplist([{abbr}])', }, mapnew = { args = 2, @@ -6315,6 +6457,13 @@ M.funcs = { mapset = { args = { 1, 3 }, base = 1, + name = 'mapset', + params = { { 'mode', 'string' }, { 'abbr', 'any' }, { 'dict', 'any' } }, + signature = 'mapset({mode}, {abbr}, {dict})', + }, + mapset__1 = { + args = { 1, 3 }, + base = 1, desc = [=[ Restore a mapping from a dictionary, possibly returned by |maparg()| or |maplist()|. A buffer mapping, when dict.buffer @@ -6352,8 +6501,8 @@ M.funcs = { endfor ]=], name = 'mapset', - params = { { 'mode', 'string' }, { 'abbr', 'any' }, { 'dict', 'any' } }, - signature = 'mapset({mode}, {abbr}, {dict})', + params = { { 'dict', 'any' } }, + signature = 'mapset({dict})', }, match = { args = { 2, 4 }, @@ -6411,6 +6560,7 @@ M.funcs = { Note that when {count} is added the way {start} works changes, see above. + *match-pattern* See |pattern| for the patterns that are accepted. The 'ignorecase' option is used to set the ignore-caseness of the pattern. 'smartcase' is NOT used. The matching is always @@ -6569,6 +6719,63 @@ M.funcs = { params = { { 'nr', 'integer' } }, signature = 'matcharg({nr})', }, + matchbufline = { + args = { 4, 5 }, + base = 1, + desc = [=[ + Returns the |List| of matches in lines from {lnum} to {end} in + buffer {buf} where {pat} matches. + + {lnum} and {end} can either be a line number or the string "$" + to refer to the last line in {buf}. + + The {dict} argument supports following items: + submatches include submatch information (|/\(|) + + For each match, a |Dict| with the following items is returned: + byteidx starting byte index of the match + lnum line number where there is a match + text matched string + Note that there can be multiple matches in a single line. + + This function works only for loaded buffers. First call + |bufload()| if needed. + + See |match-pattern| for information about the effect of some + option settings on the pattern. + + When {buf} is not a valid buffer, the buffer is not loaded or + {lnum} or {end} is not valid then an error is given and an + empty |List| is returned. + + Examples: >vim + " Assuming line 3 in buffer 5 contains "a" + :echo matchbufline(5, '\<\k\+\>', 3, 3) + [{'lnum': 3, 'byteidx': 0, 'text': 'a'}] + " Assuming line 4 in buffer 10 contains "tik tok" + :echo matchbufline(10, '\<\k\+\>', 1, 4) + [{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}] + < + If {submatch} is present and is v:true, then submatches like + "\1", "\2", etc. are also returned. Example: >vim + " Assuming line 2 in buffer 2 contains "acd" + :echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2 + \ {'submatches': v:true}) + [{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}] + <The "submatches" List always contains 9 items. If a submatch + is not found, then an empty string is returned for that + submatch. + ]=], + name = 'matchbufline', + params = { + { 'buf', 'string|integer' }, + { 'pat', 'string' }, + { 'lnum', 'string|integer' }, + { 'end', 'string|integer' }, + { 'dict', 'table' }, + }, + signature = 'matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict}])', + }, matchdelete = { args = { 1, 2 }, base = 1, @@ -6753,6 +6960,46 @@ M.funcs = { params = { { 'expr', 'any' }, { 'pat', 'any' }, { 'start', 'any' }, { 'count', 'any' } }, signature = 'matchstr({expr}, {pat} [, {start} [, {count}]])', }, + matchstrlist = { + args = { 2, 3 }, + base = 1, + desc = [=[ + Returns the |List| of matches in {list} where {pat} matches. + {list} is a |List| of strings. {pat} is matched against each + string in {list}. + + The {dict} argument supports following items: + submatches include submatch information (|/\(|) + + For each match, a |Dict| with the following items is returned: + byteidx starting byte index of the match. + idx index in {list} of the match. + text matched string + submatches a List of submatches. Present only if + "submatches" is set to v:true in {dict}. + + See |match-pattern| for information about the effect of some + option settings on the pattern. + + Example: >vim + :echo matchstrlist(['tik tok'], '\<\k\+\>') + [{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}] + :echo matchstrlist(['a', 'b'], '\<\k\+\>') + [{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}] + < + If "submatches" is present and is v:true, then submatches like + "\1", "\2", etc. are also returned. Example: >vim + :echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)', + \ #{submatches: v:true}) + [{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}] + <The "submatches" List always contains 9 items. If a submatch + is not found, then an empty string is returned for that + submatch. + ]=], + name = 'matchstrlist', + params = { { 'list', 'string[]' }, { 'pat', 'string' }, { 'dict', 'table' } }, + signature = 'matchstrlist({list}, {pat} [, {dict}])', + }, matchstrpos = { args = { 2, 4 }, base = 1, @@ -6790,7 +7037,7 @@ M.funcs = { it returns the maximum of all values in the Dictionary. If {expr} is neither a List nor a Dictionary, or one of the items in {expr} cannot be used as a Number this results in - an error. An empty |List| or |Dictionary| results in zero. + an error. An empty |List| or |Dictionary| results in zero. ]=], name = 'max', @@ -7056,7 +7303,7 @@ M.funcs = { ]=], name = 'mode', - params = {}, + params = { { 'expr', 'any' } }, signature = 'mode([expr])', }, msgpackdump = { @@ -7155,7 +7402,6 @@ M.funcs = { are binary strings). 2. String with NUL byte inside. 3. Duplicate key. - 4. Empty key. ext |List| with two values: first is a signed integer representing extension type. Second is |readfile()|-style list of strings. @@ -7445,9 +7691,9 @@ M.funcs = { <This limits the length of the text used from "line" to "width" bytes. - If the argument to be formatted is specified using a posional - argument specifier, and a '*' is used to indicate that a - number argument is to be used to specify the width or + If the argument to be formatted is specified using a + positional argument specifier, and a '*' is used to indicate + that a number argument is to be used to specify the width or precision, the argument(s) to be used must also be specified using a {n$} positional argument specifier. See |printf-$|. @@ -9524,7 +9770,7 @@ M.funcs = { ]=], name = 'setreg', - params = { { 'regname', 'string' }, { 'value', 'any' }, { 'options', 'table' } }, + params = { { 'regname', 'string' }, { 'value', 'any' }, { 'options', 'string' } }, signature = 'setreg({regname}, {value} [, {options}])', }, settabvar = { @@ -9905,7 +10151,7 @@ M.funcs = { name = 'sign_jump', params = { { 'id', 'integer' }, { 'group', 'string' }, { 'buf', 'integer|string' } }, signature = 'sign_jump({id}, {group}, {buf})', - returns = 'integer' + returns = 'integer', }, sign_place = { args = { 4, 5 }, @@ -9968,7 +10214,7 @@ M.funcs = { { 'dict', 'vim.fn.sign_place.dict' }, }, signature = 'sign_place({id}, {group}, {name}, {buf} [, {dict}])', - returns = 'integer' + returns = 'integer', }, sign_placelist = { args = 1, @@ -10035,7 +10281,7 @@ M.funcs = { name = 'sign_placelist', params = { { 'list', 'vim.fn.sign_placelist.list.item[]' } }, signature = 'sign_placelist({list})', - returns = 'integer[]' + returns = 'integer[]', }, sign_undefine = { args = { 0, 1 }, @@ -10570,7 +10816,7 @@ M.funcs = { signature = 'stdpath({what})', }, state = { - args = {0, 1}, + args = { 0, 1 }, base = 1, desc = [=[ Return a string which contains characters indicating the @@ -11112,9 +11358,16 @@ M.funcs = { ]=], name = 'submatch', + params = { { 'nr', 'integer' }, { 'list', 'nil' } }, + signature = 'submatch({nr} [, {list}])', + returns = 'string', + }, + submatch__1 = { + args = { 2 }, + base = 1, + name = 'submatch', params = { { 'nr', 'integer' }, { 'list', 'integer' } }, returns = 'string|string[]', - signature = 'submatch({nr} [, {list}])', }, substitute = { args = 4, @@ -11365,7 +11618,7 @@ M.funcs = { ]=], name = 'synconcealed', params = { { 'lnum', 'integer' }, { 'col', 'integer' } }, - returns = '{[1]: integer, [2]: string, [3]: integer}[]', + returns = '{[1]: integer, [2]: string, [3]: integer}', signature = 'synconcealed({lnum}, {col})', }, synstack = { @@ -11702,7 +11955,6 @@ M.funcs = { test_write_list_log = { args = 1, params = { { 'fname', 'string' } }, - signature = '', lua = false, }, timer_info = { @@ -12698,7 +12950,7 @@ M.funcs = { name = 'winsaveview', params = {}, signature = 'winsaveview()', - returns = 'vim.fn.winsaveview.ret' + returns = 'vim.fn.winsaveview.ret', }, winwidth = { args = 1, diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c index c60a104381..7b8f71ef3f 100644 --- a/src/nvim/eval/buffer.c +++ b/src/nvim/eval/buffer.c @@ -5,6 +5,7 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" @@ -14,7 +15,6 @@ #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/memline.h" @@ -494,6 +494,7 @@ static dict_T *get_buffer_info(buf_T *buf) tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf)); tv_dict_add_nr(dict, S_LEN("hidden"), buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); + tv_dict_add_nr(dict, S_LEN("command"), buf == cmdwin_buf); // Get a reference to buffer variables tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); @@ -507,7 +508,7 @@ static dict_T *get_buffer_info(buf_T *buf) } tv_dict_add_list(dict, S_LEN("windows"), windows); - if (buf->b_signs) { + if (buf_has_signs(buf)) { // List of signs placed in this buffer tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf)); } @@ -584,7 +585,8 @@ void f_getbufinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// /// @return range (from start to end) of lines in rettv from the specified /// buffer. -static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv) +static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, bool retlist, + typval_T *rettv) { rettv->v_type = (retlist ? VAR_LIST : VAR_STRING); rettv->vval.v_string = NULL; diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 03f79fca84..d7df7bb150 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -14,9 +14,9 @@ #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" -#include "nvim/func_attr.h" +#include "nvim/eval_defs.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" @@ -142,9 +142,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack ValuesStackItem key = kv_pop(*stack); if (last_container.special_val == NULL) { // These cases should have already been handled. - assert(!(key.is_special_string - || key.val.vval.v_string == NULL - || *key.val.vval.v_string == NUL)); + assert(!(key.is_special_string || key.val.vval.v_string == NULL)); dictitem_T *const obj_di = tv_dict_item_alloc(key.val.vval.v_string); tv_clear(&key.val); if (tv_dict_add(last_container.container.vval.v_dict, obj_di) @@ -171,11 +169,10 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack tv_clear(&obj.val); return FAIL; } - // Handle empty key and key represented as special dictionary + // Handle special dictionaries if (last_container.special_val == NULL && (obj.is_special_string || obj.val.vval.v_string == NULL - || *obj.val.vval.v_string == NUL || tv_dict_find(last_container.container.vval.v_dict, obj.val.vval.v_string, -1))) { tv_clear(&obj.val); @@ -405,13 +402,6 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, semsg(_("E474: Expected string end: %.*s"), (int)buf_len, buf); goto parse_json_string_fail; } - if (len == 0) { - POP(((typval_T) { - .v_type = VAR_STRING, - .vval = { .v_string = NULL }, - }), false); - goto parse_json_string_ret; - } char *str = xmalloc(len + 1); int fst_in_pair = 0; char *str_end = str; diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 8505c30fad..d35ac4eb7b 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -20,7 +20,8 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_encode.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" +#include "nvim/globals.h" #include "nvim/hashtab.h" #include "nvim/macros_defs.h" #include "nvim/math.h" @@ -1055,3 +1056,17 @@ char *encode_tv2json(typval_T *tv, size_t *len) #undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS #undef TYPVAL_ENCODE_CONV_RECURSE #undef TYPVAL_ENCODE_ALLOW_SPECIALS + +/// Initialize ListReaderState structure +ListReaderState encode_init_lrstate(const list_T *const list) + FUNC_ATTR_NONNULL_ALL +{ + return (ListReaderState) { + .list = list, + .li = tv_list_first(list), + .offset = 0, + .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL + ? 0 + : strlen(TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string)), + }; +} diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index 26a3286f2b..6d1c0b61c5 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -1,12 +1,8 @@ #pragma once -#include <msgpack.h> #include <msgpack/pack.h> -#include <stddef.h> #include <string.h> -#include "nvim/eval.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/garray_defs.h" @@ -36,20 +32,6 @@ typedef struct { size_t li_length; ///< Length of the string inside the read item. } ListReaderState; -/// Initialize ListReaderState structure -static inline ListReaderState encode_init_lrstate(const list_T *const list) - FUNC_ATTR_NONNULL_ALL -{ - return (ListReaderState) { - .list = list, - .li = tv_list_first(list), - .offset = 0, - .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL - ? 0 - : strlen(TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string)), - }; -} - /// Array mapping values from SpecialVarValue enum to names extern const char *const encode_bool_var_names[]; extern const char *const encode_special_var_names[]; diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index dc23fcdc72..1b8c057d7c 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -5,9 +5,8 @@ #include "nvim/eval/executor.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/message.h" #include "nvim/strings.h" @@ -15,7 +14,7 @@ #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "eval/executor.c.generated.h" // IWYU pragma: export +# include "eval/executor.c.generated.h" #endif char *e_list_index_out_of_range_nr diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 13425b21d1..d7237d6443 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -14,7 +14,6 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> -#include <sys/types.h> #include <time.h> #include <uv.h> @@ -27,9 +26,11 @@ #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cmdexpand_defs.h" @@ -48,22 +49,26 @@ #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/process.h" #include "nvim/event/time.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/file_search.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" @@ -75,24 +80,34 @@ #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/math.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/menu.h" +#include "nvim/menu_defs.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/dl.h" #include "nvim/os/fileio.h" +#include "nvim/os/fileio_defs.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/pty_process.h" #include "nvim/os/shell.h" #include "nvim/os/stdpaths_defs.h" @@ -103,15 +118,19 @@ #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/search.h" #include "nvim/sha256.h" #include "nvim/spell.h" #include "nvim/spellsuggest.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/version.h" #include "nvim/vim_defs.h" @@ -305,7 +324,7 @@ int call_internal_method(const char *const fname, const int argcount, typval_T * } /// @return true for a non-zero Number and a non-empty String. -static int non_zero_arg(typval_T *argvars) +static bool non_zero_arg(typval_T *argvars) { return ((argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number != 0) @@ -341,33 +360,28 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) MsgpackRpcRequestHandler handler = *fptr.api_handler; - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, false)); } Error err = ERROR_INIT; - Arena res_arena = ARENA_EMPTY; - Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err); + Object result = handler.fn(VIML_INTERNAL_CALL, args, &arena, &err); if (ERROR_SET(&err)) { semsg_multiline(e_api_error, err.msg); goto end; } - if (!object_to_vim(result, rettv, &err)) { - assert(ERROR_SET(&err)); - semsg(_("Error converting the call result: %s"), err.msg); - } + object_to_vim_take_luaref(&result, rettv, true, &err); end: - api_free_array(args); - if (handler.arena_return) { - arena_mem_free(arena_finish(&res_arena)); - } else { + if (handler.ret_alloc) { api_free_object(result); } + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); } @@ -428,8 +442,7 @@ static void f_and(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "api_info()" function static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - Dictionary metadata = api_metadata(); - (void)object_to_vim(DICTIONARY_OBJ(metadata), rettv, NULL); + object_to_vim(api_metadata(), rettv, NULL); } /// "atan2()" function @@ -1022,10 +1035,11 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Dictionary ctx_dict = ctx_to_dict(ctx); + Arena arena = ARENA_EMPTY; + Dictionary ctx_dict = ctx_to_dict(ctx, &arena); Error err = ERROR_INIT; - (void)object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err); - api_free_dictionary(ctx_dict); + object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err); + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); } @@ -1093,7 +1107,8 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const int save_did_emsg = did_emsg; did_emsg = false; - Dictionary dict = vim_to_object(&argvars[0]).data.dictionary; + Arena arena = ARENA_EMPTY; + Dictionary dict = vim_to_object(&argvars[0], &arena, true).data.dictionary; Context tmp = CONTEXT_INIT; Error err = ERROR_INIT; ctx_from_dict(dict, &tmp, &err); @@ -1106,7 +1121,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) *ctx = tmp; } - api_free_dictionary(dict); + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); did_emsg = save_did_emsg; } @@ -1675,7 +1690,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char *path = NULL; - (void)os_can_exe(tv_get_string(&argvars[0]), &path, true); + os_can_exe(tv_get_string(&argvars[0]), &path, true); #ifdef BACKSLASH_IN_FILENAME if (path != NULL) { @@ -1711,7 +1726,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) n = false; // Trailing garbage. } } else if (*p == '*') { // Internal or user defined function. - n = function_exists(p + 1, false); + n = strnequal(p, "*v:lua.", 7) ? nlua_func_exists(p + 7) : function_exists(p + 1, false); } else if (*p == ':') { n = cmd_exists(p + 1); } else if (*p == '#') { @@ -1922,44 +1937,47 @@ static void extend_list(typval_T *argvars, const char *arg_errmsg, bool is_new, list_T *l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (is_new || !value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { - if (is_new) { - l1 = tv_list_copy(NULL, l1, false, get_copyID()); - if (l1 == NULL) { - return; - } - } - listitem_T *item; - if (argvars[2].v_type != VAR_UNKNOWN) { - int before = (int)tv_get_number_chk(&argvars[2], &error); - if (error) { - return; // Type error; errmsg already given. - } + if (!is_new && value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { + return; + } - if (before == tv_list_len(l1)) { - item = NULL; - } else { - item = tv_list_find(l1, before); - if (item == NULL) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)before); - return; - } - } - } else { - item = NULL; + if (is_new) { + l1 = tv_list_copy(NULL, l1, false, get_copyID()); + if (l1 == NULL) { + return; } - tv_list_extend(l1, l2, item); + } - if (is_new) { - *rettv = (typval_T){ - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval.v_list = l1, - }; + listitem_T *item; + if (argvars[2].v_type != VAR_UNKNOWN) { + int before = (int)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // Type error; errmsg already given. + } + + if (before == tv_list_len(l1)) { + item = NULL; } else { - tv_copy(&argvars[0], rettv); + item = tv_list_find(l1, before); + if (item == NULL) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)before); + return; + } } + } else { + item = NULL; + } + tv_list_extend(l1, l2, item); + + if (is_new) { + *rettv = (typval_T){ + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval.v_list = l1, + }; + } else { + tv_copy(&argvars[0], rettv); } } @@ -1970,54 +1988,61 @@ static void extend_list(typval_T *argvars, const char *arg_errmsg, bool is_new, static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, typval_T *rettv) { dict_T *d1 = argvars[0].vval.v_dict; - dict_T *const d2 = argvars[1].vval.v_dict; if (d1 == NULL) { const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); (void)locked; assert(locked == true); - } else if (d2 == NULL) { + return; + } + dict_T *const d2 = argvars[1].vval.v_dict; + if (d2 == NULL) { // Do nothing tv_copy(&argvars[0], rettv); - } else if (is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { - if (is_new) { - d1 = tv_dict_copy(NULL, d1, false, get_copyID()); - if (d1 == NULL) { - return; - } + return; + } + + if (!is_new && value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { + return; + } + + if (is_new) { + d1 = tv_dict_copy(NULL, d1, false, get_copyID()); + if (d1 == NULL) { + return; } + } - const char *action = "force"; - // Check the third argument. - if (argvars[2].v_type != VAR_UNKNOWN) { - const char *const av[] = { "keep", "force", "error" }; + const char *action = "force"; + // Check the third argument. + if (argvars[2].v_type != VAR_UNKNOWN) { + const char *const av[] = { "keep", "force", "error" }; - action = tv_get_string_chk(&argvars[2]); - if (action == NULL) { - return; // Type error; error message already given. - } - size_t i; - for (i = 0; i < ARRAY_SIZE(av); i++) { - if (strcmp(action, av[i]) == 0) { - break; - } - } - if (i == 3) { - semsg(_(e_invarg2), action); - return; + action = tv_get_string_chk(&argvars[2]); + if (action == NULL) { + return; // Type error; error message already given. + } + size_t i; + for (i = 0; i < ARRAY_SIZE(av); i++) { + if (strcmp(action, av[i]) == 0) { + break; } } + if (i == 3) { + semsg(_(e_invarg2), action); + return; + } + } - tv_dict_extend(d1, d2, action); + tv_dict_extend(d1, d2, action); - if (is_new) { - *rettv = (typval_T){ - .v_type = VAR_DICT, - .v_lock = VAR_UNLOCKED, - .vval.v_dict = d1, - }; - } else { - tv_copy(&argvars[0], rettv); - } + if (is_new) { + *rettv = (typval_T){ + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval.v_dict = d1, + }; + } else { + tv_copy(&argvars[0], rettv); } } @@ -2068,8 +2093,8 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) flags = tv_get_string_buf(&argvars[1], nbuf); } - nvim_feedkeys(cstr_as_string((char *)keys), - cstr_as_string((char *)flags), true); + nvim_feedkeys(cstr_as_string(keys), + cstr_as_string(flags), true); } /// "filereadable()" function @@ -2175,7 +2200,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - if (f <= (float_T) - VARNUMBER_MAX + DBL_EPSILON) { + if (f <= (float_T)(-VARNUMBER_MAX) + DBL_EPSILON) { rettv->vval.v_number = -VARNUMBER_MAX; } else if (f >= (float_T)VARNUMBER_MAX - DBL_EPSILON) { rettv->vval.v_number = VARNUMBER_MAX; @@ -2219,8 +2244,8 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) len = strlen(fname); if (*mods != NUL) { size_t usedlen = 0; - (void)modify_fname((char *)mods, false, &usedlen, - (char **)&fname, &fbuf, &len); + modify_fname((char *)mods, false, &usedlen, + (char **)&fname, &fbuf, &len); } } @@ -2444,7 +2469,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool : (varnumber_T)0)); tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { - const int save_set_curswant = curwin->w_set_curswant; + const bool save_set_curswant = curwin->w_set_curswant; const colnr_T save_curswant = curwin->w_curswant; const colnr_T save_virtcol = curwin->w_virtcol; @@ -2776,6 +2801,167 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) getpos_both(argvars, rettv, false, false); } +/// Convert from block_def to string +static char *block_def2str(struct block_def *bd) +{ + size_t size = (size_t)bd->startspaces + (size_t)bd->endspaces + (size_t)bd->textlen; + char *ret = xmalloc(size + 1); + char *p = ret; + memset(p, ' ', (size_t)bd->startspaces); + p += bd->startspaces; + memmove(p, bd->textstart, (size_t)bd->textlen); + p += bd->textlen; + memset(p, ' ', (size_t)bd->endspaces); + *(p + bd->endspaces) = NUL; + return ret; +} + +/// "getregion()" function +static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (tv_check_for_list_arg(argvars, 0) == FAIL + || tv_check_for_list_arg(argvars, 1) == FAIL + || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { + return; + } + + int fnum1 = -1; + int fnum2 = -1; + pos_T p1, p2; + if (list2fpos(&argvars[0], &p1, &fnum1, NULL, false) != OK + || list2fpos(&argvars[1], &p2, &fnum2, NULL, false) != OK + || fnum1 != fnum2) { + return; + } + + bool is_select_exclusive; + char *type; + char default_type[] = "v"; + if (argvars[2].v_type == VAR_DICT) { + is_select_exclusive = tv_dict_get_bool(argvars[2].vval.v_dict, "exclusive", + *p_sel == 'e'); + type = tv_dict_get_string(argvars[2].vval.v_dict, "type", false); + if (type == NULL) { + type = default_type; + } + } else { + is_select_exclusive = *p_sel == 'e'; + type = default_type; + } + + MotionType region_type = kMTUnknown; + if (type[0] == 'v' && type[1] == NUL) { + region_type = kMTCharWise; + } else if (type[0] == 'V' && type[1] == NUL) { + region_type = kMTLineWise; + } else if (type[0] == Ctrl_V && type[1] == NUL) { + region_type = kMTBlockWise; + } else { + return; + } + + buf_T *const save_curbuf = curbuf; + + if (fnum1 != 0) { + buf_T *findbuf = buflist_findnr(fnum1); + // buffer not loaded + if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { + return; + } + curbuf = findbuf; + } + + const TriState save_virtual = virtual_op; + virtual_op = virtual_active(); + + // NOTE: Adjust is needed. + p1.col--; + p2.col--; + + if (!lt(p1, p2)) { + // swap position + pos_T p = p1; + p1 = p2; + p2 = p; + } + + oparg_T oa; + bool inclusive = true; + + if (region_type == kMTCharWise) { + // handle 'selection' == "exclusive" + if (is_select_exclusive && !equalpos(p1, p2)) { + if (p2.coladd > 0) { + p2.coladd--; + } else if (p2.col > 0) { + p2.col--; + mark_mb_adjustpos(curbuf, &p2); + } else if (p2.lnum > 1) { + p2.lnum--; + p2.col = (colnr_T)strlen(ml_get(p2.lnum)); + if (p2.col > 0) { + p2.col--; + mark_mb_adjustpos(curbuf, &p2); + } + } + } + // if fp2 is on NUL (empty line) inclusive becomes false + if (*ml_get_pos(&p2) == NUL && !virtual_op) { + inclusive = false; + } + } else if (region_type == kMTBlockWise) { + colnr_T sc1, ec1, sc2, ec2; + getvvcol(curwin, &p1, &sc1, NULL, &ec1); + getvvcol(curwin, &p2, &sc2, NULL, &ec2); + oa.motion_type = kMTBlockWise; + oa.inclusive = true; + oa.op_type = OP_NOP; + oa.start = p1; + oa.end = p2; + oa.start_vcol = MIN(sc1, sc2); + if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1) { + oa.end_vcol = sc2 - 1; + } else { + oa.end_vcol = MAX(ec1, ec2); + } + } + + // Include the trailing byte of a multi-byte char. + int l = utfc_ptr2len(ml_get_pos(&p2)); + if (l > 1) { + p2.col += l - 1; + } + + for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { + char *akt = NULL; + + if (region_type == kMTLineWise) { + akt = xstrdup(ml_get(lnum)); + } else if (region_type == kMTBlockWise) { + struct block_def bd; + block_prep(&oa, &bd, lnum, false); + akt = block_def2str(&bd); + } else if (p1.lnum < lnum && lnum < p2.lnum) { + akt = xstrdup(ml_get(lnum)); + } else { + struct block_def bd; + charwise_block_prep(p1, p2, &bd, lnum, inclusive); + akt = block_def2str(&bd); + } + + assert(akt != NULL); + tv_list_append_allocated_string(rettv->vval.v_list, akt); + } + + if (curbuf != save_curbuf) { + curbuf = save_curbuf; + } + + virtual_op = save_virtual; +} + /// Common between getreg(), getreginfo() and getregtype(): get the register /// name from the first argument. /// Returns zero on error. @@ -3140,7 +3326,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "path_extra", "persistent_undo", "profile", - "pythonx", "reltime", "quickfix", "rightleft", @@ -3182,6 +3367,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "xattr", #endif "nvim", + "rneovim", }; // XXX: eval_has_provider() may shell out :( @@ -3231,6 +3417,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) n = syntax_present(curwin); } else if (STRICMP(name, "clipboard_working") == 0) { n = eval_has_provider("clipboard"); + } else if (STRICMP(name, "pythonx") == 0) { + n = eval_has_provider("python3"); } else if (STRICMP(name, "wsl") == 0) { n = has_wsl(); #ifdef UNIX @@ -3253,13 +3441,11 @@ static bool has_wsl(void) static TriState has_wsl = kNone; if (has_wsl == kNone) { Error err = ERROR_INIT; - Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim.uv.os_uname()['release']:lower()" - ":match('microsoft') and true or false"), - (Array)ARRAY_DICT_INIT, &err); + Object o = NLUA_EXEC_STATIC("return vim.uv.os_uname()['release']:lower()" + ":match('microsoft')", + (Array)ARRAY_DICT_INIT, kRetNilBool, NULL, &err); assert(!ERROR_SET(&err)); - assert(o.type == kObjectTypeBoolean); - has_wsl = o.data.boolean ? kTrue : kFalse; - api_free_object(o); + has_wsl = LUARET_TRUTHY(o) ? kTrue : kFalse; } return has_wsl == kTrue; } @@ -3639,7 +3825,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) }); // Ask for choice. - int mouse_used; + bool mouse_used; int selected = prompt_for_number(&mouse_used); if (mouse_used) { selected -= lines_left; @@ -3899,13 +4085,16 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = 1; } -static const char *ignored_env_vars[] = { +static const char *pty_ignored_env_vars[] = { #ifndef MSWIN "COLUMNS", "LINES", "TERMCAP", "COLORFGBG", + "COLORTERM", #endif + "VIM", + "VIMRUNTIME", NULL }; @@ -3944,9 +4133,9 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en // child process. We're removing them here so the user can still decide // they want to explicitly set them. for (size_t i = 0; - i < ARRAY_SIZE(ignored_env_vars) && ignored_env_vars[i]; + i < ARRAY_SIZE(pty_ignored_env_vars) && pty_ignored_env_vars[i]; i++) { - dictitem_T *dv = tv_dict_find(env, ignored_env_vars[i], -1); + dictitem_T *dv = tv_dict_find(env, pty_ignored_env_vars[i], -1); if (dv) { tv_dict_item_remove(env, dv); } @@ -3954,10 +4143,6 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en #ifndef MSWIN // Set COLORTERM to "truecolor" if termguicolors is set if (p_tgc) { - dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM")); - if (dv) { - tv_dict_item_remove(env, dv); - } tv_dict_add_str(env, S_LEN("COLORTERM"), "truecolor"); } #endif @@ -4056,8 +4241,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool clear_env = false; bool overlapped = false; ChannelStdinMode stdin_mode = kChannelStdinPipe; - CallbackReader on_stdout = CALLBACK_READER_INIT, - on_stderr = CALLBACK_READER_INIT; + CallbackReader on_stdout = CALLBACK_READER_INIT; + CallbackReader on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; char *cwd = NULL; dictitem_T *job_env = NULL; @@ -4120,7 +4305,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } - uint16_t width = 0, height = 0; + uint16_t width = 0; + uint16_t height = 0; char *term_name = NULL; if (pty) { @@ -4168,7 +4354,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const char *error = NULL; if (data->is_rpc) { // Ignore return code, but show error later. - (void)channel_close(data->id, kChannelPartRpc, &error); + channel_close(data->id, kChannelPartRpc, &error); } process_stop(&data->stream.proc); rettv->vval.v_number = 1; @@ -4503,7 +4689,7 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - nlua_typval_eval(cstr_as_string((char *)str), &argvars[1], rettv); + nlua_typval_eval(cstr_as_string(str), &argvars[1], rettv); } static void find_some_match(typval_T *const argvars, typval_T *const rettv, @@ -4714,6 +4900,151 @@ theend: p_cpo = save_cpo; } +/// Return all the matches in string "str" for pattern "rmp". +/// The matches are returned in the List "mlist". +/// If "submatches" is true, then submatch information is also returned. +/// "matchbuf" is true when called for matchbufline(). +static void get_matches_in_str(const char *str, regmatch_T *rmp, list_T *mlist, int idx, + bool submatches, bool matchbuf) +{ + size_t len = strlen(str); + int match = 0; + colnr_T startidx = 0; + + while (true) { + match = vim_regexec_nl(rmp, str, startidx); + if (!match) { + break; + } + + dict_T *d = tv_dict_alloc(); + tv_list_append_dict(mlist, d); + + if (matchbuf) { + tv_dict_add_nr(d, S_LEN("lnum"), idx); + } else { + tv_dict_add_nr(d, S_LEN("idx"), idx); + } + + tv_dict_add_nr(d, S_LEN("byteidx"), + (colnr_T)(rmp->startp[0] - str)); + + tv_dict_add_str_len(d, S_LEN("text"), rmp->startp[0], + (int)(rmp->endp[0] - rmp->startp[0])); + + if (submatches) { + list_T *sml = tv_list_alloc(NSUBEXP - 1); + + tv_dict_add_list(d, S_LEN("submatches"), sml); + + // return a list with the submatches + for (int i = 1; i < NSUBEXP; i++) { + if (rmp->endp[i] == NULL) { + tv_list_append_string(sml, "", 0); + } else { + tv_list_append_string(sml, rmp->startp[i], rmp->endp[i] - rmp->startp[i]); + } + } + } + startidx = (colnr_T)(rmp->endp[0] - str); + if (startidx >= (colnr_T)len || str + startidx <= rmp->startp[0]) { + break; + } + } +} + +/// "matchbufline()" function +static void f_matchbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *retlist = rettv->vval.v_list; + + if (tv_check_for_buffer_arg(argvars, 0) == FAIL + || tv_check_for_string_arg(argvars, 1) == FAIL + || tv_check_for_lnum_arg(argvars, 2) == FAIL + || tv_check_for_lnum_arg(argvars, 3) == FAIL + || tv_check_for_opt_dict_arg(argvars, 4) == FAIL) { + return; + } + + const int prev_did_emsg = did_emsg; + buf_T *buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + if (did_emsg == prev_did_emsg) { + semsg(_(e_invalid_buffer_name_str), tv_get_string(&argvars[0])); + } + return; + } + if (buf->b_ml.ml_mfp == NULL) { + emsg(_(e_buffer_is_not_loaded)); + return; + } + + char patbuf[NUMBUFLEN]; + const char *pat = tv_get_string_buf(&argvars[1], patbuf); + + const int did_emsg_before = did_emsg; + linenr_T slnum = tv_get_lnum_buf(&argvars[2], buf); + if (did_emsg > did_emsg_before) { + return; + } + if (slnum < 1) { + semsg(_(e_invargval), "lnum"); + return; + } + + linenr_T elnum = tv_get_lnum_buf(&argvars[3], buf); + if (did_emsg > did_emsg_before) { + return; + } + if (elnum < 1 || elnum < slnum) { + semsg(_(e_invargval), "end_lnum"); + return; + } + + if (elnum > buf->b_ml.ml_line_count) { + elnum = buf->b_ml.ml_line_count; + } + + bool submatches = false; + if (argvars[4].v_type != VAR_UNKNOWN) { + dict_T *d = argvars[4].vval.v_dict; + if (d != NULL) { + dictitem_T *di = tv_dict_find(d, S_LEN("submatches")); + if (di != NULL) { + if (di->di_tv.v_type != VAR_BOOL) { + semsg(_(e_invargval), "submatches"); + return; + } + submatches = tv_get_bool(&di->di_tv); + } + } + } + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + char *const save_cpo = p_cpo; + p_cpo = empty_string_option; + + regmatch_T regmatch; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog == NULL) { + goto theend; + } + regmatch.rm_ic = p_ic; + + while (slnum <= elnum) { + const char *str = ml_get_buf(buf, slnum); + get_matches_in_str(str, ®match, retlist, slnum, submatches, true); + slnum++; + } + + vim_regfree(regmatch.regprog); + +theend: + p_cpo = save_cpo; +} + /// "match()" function static void f_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -4738,6 +5069,73 @@ static void f_matchstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) find_some_match(argvars, rettv, kSomeMatchStr); } +/// "matchstrlist()" function +static void f_matchstrlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *retlist = rettv->vval.v_list; + + if (tv_check_for_list_arg(argvars, 0) == FAIL + || tv_check_for_string_arg(argvars, 1) == FAIL + || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { + return; + } + + list_T *l = NULL; + if ((l = argvars[0].vval.v_list) == NULL) { + return; + } + + char patbuf[NUMBUFLEN]; + const char *pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) { + return; + } + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + char *const save_cpo = p_cpo; + p_cpo = empty_string_option; + + regmatch_T regmatch; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog == NULL) { + goto theend; + } + regmatch.rm_ic = p_ic; + + bool submatches = false; + if (argvars[2].v_type != VAR_UNKNOWN) { + dict_T *d = argvars[2].vval.v_dict; + if (d != NULL) { + dictitem_T *di = tv_dict_find(d, S_LEN("submatches")); + if (di != NULL) { + if (di->di_tv.v_type != VAR_BOOL) { + semsg(_(e_invargval), "submatches"); + goto cleanup; + } + submatches = tv_get_bool(&di->di_tv); + } + } + } + + int idx = 0; + TV_LIST_ITER_CONST(l, li, { + const typval_T *const li_tv = TV_LIST_ITEM_TV(li); + if (li_tv->v_type == VAR_STRING && li_tv->vval.v_string != NULL) { + const char *str = li_tv->vval.v_string; + get_matches_in_str(str, ®match, retlist, idx, submatches, false); + } + idx++; + }); + +cleanup: + vim_regfree(regmatch.regprog); + +theend: + p_cpo = save_cpo; +} + /// "matchstrpos()" function static void f_matchstrpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -5216,7 +5614,7 @@ static void f_printf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (!did_emsg) { char *s = xmalloc((size_t)len + 1); rettv->vval.v_string = s; - (void)vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); + vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); } did_emsg |= saved_did_emsg; } @@ -5620,6 +6018,12 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl } } + if (blob) { + tv_blob_alloc_ret(rettv); + } else { + tv_list_alloc_ret(rettv, kListLenUnknown); + } + // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. const char *const fname = tv_get_string(&argvars[0]); @@ -5634,7 +6038,6 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl } if (blob) { - tv_blob_alloc_ret(rettv); if (read_blob(fd, rettv, offset, size) == FAIL) { semsg(_(e_notread), fname); } @@ -5642,7 +6045,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl return; } - list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *const l = rettv->vval.v_list; while (maxline < 0 || tv_list_len(l) < maxline) { int readlen = (int)fread(buf, 1, (size_t)io_size, fd); @@ -5807,7 +6210,7 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (list == NULL) { return; } - (void)tv_dict_add_list(dict, S_LEN("regcontents"), list); + tv_dict_add_list(dict, S_LEN("regcontents"), list); char buf[NUMBUFLEN + 2]; buf[0] = NUL; @@ -5826,15 +6229,15 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) case kMTUnknown: abort(); } - (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); + tv_dict_add_str(dict, S_LEN("regtype"), buf); buf[0] = (char)get_register_name(get_unname_register()); buf[1] = NUL; if (regname == '"') { - (void)tv_dict_add_str(dict, S_LEN("points_to"), buf); + tv_dict_add_str(dict, S_LEN("points_to"), buf); } else { - (void)tv_dict_add_bool(dict, S_LEN("isunnamed"), - regname == buf[0] ? kBoolVarTrue : kBoolVarFalse); + tv_dict_add_bool(dict, S_LEN("isunnamed"), + regname == buf[0] ? kBoolVarTrue : kBoolVarFalse); } } @@ -6648,16 +7051,17 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, true)); } bool ok = rpc_send_event((uint64_t)argvars[0].vval.v_number, tv_get_string(&argvars[1]), args); - api_free_array(args); + arena_mem_free(arena_finish(&arena)); if (!ok) { semsg(_(e_invarg2), "Channel doesn't exist"); @@ -6687,10 +7091,11 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, true)); } sctx_T save_current_sctx; @@ -6726,6 +7131,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) ArenaMem res_mem = NULL; Object result = rpc_send_call(chan_id, method, args, &res_mem, &err); + arena_mem_free(arena_finish(&arena)); if (l_provider_call_nesting) { current_sctx = save_current_sctx; @@ -6755,10 +7161,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) goto end; } - if (!object_to_vim(result, rettv, &err)) { - assert(ERROR_SET(&err)); - semsg(_("Error converting the call result: %s"), err.msg); - } + object_to_vim(result, rettv, &err); end: arena_mem_free(res_mem); @@ -6889,15 +7292,8 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) ScreenGrid *grid; screenchar_adjust(&grid, &row, &col); - int c; - if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { - c = -1; - } else { - char buf[MAX_SCHAR_SIZE + 1]; - schar_get(buf, grid_getchar(grid, row, col, NULL)); - c = utf_ptr2char(buf); - } - rettv->vval.v_number = c; + rettv->vval.v_number = (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) + ? -1 : schar_get_first_codepoint(grid_getchar(grid, row, col, NULL)); } /// "screenchars()" function @@ -7247,7 +7643,7 @@ int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir, // If it's still empty it was changed and restored, need to restore in // the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); + set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0); } free_string_option(save_cpo); } @@ -8090,7 +8486,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (*p == '+' || *p == '-') { p = skipwhite(p + 1); } - (void)string2float(p, &rettv->vval.v_float); + string2float(p, &rettv->vval.v_float); if (isneg) { rettv->vval.v_float *= -1; } @@ -8391,7 +8787,6 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int syntax_flags = 0; - int cchar; int matchid = 0; char str[NUMBUFLEN]; @@ -8405,19 +8800,18 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= strlen(ml_get(lnum)) && curwin->w_p_cole > 0) { - (void)syn_get_id(curwin, lnum, col, false, NULL, false); + syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); // get the conceal character if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { - cchar = syn_get_sub_char(); + schar_T cchar = schar_from_char(syn_get_sub_char()); if (cchar == NUL && curwin->w_p_cole == 1) { cchar = (curwin->w_p_lcs_chars.conceal == NUL) - ? ' ' - : curwin->w_p_lcs_chars.conceal; + ? schar_from_ascii(' ') : curwin->w_p_lcs_chars.conceal; } if (cchar != NUL) { - utf_char2bytes(cchar, str); + schar_get(str, cchar); } } } @@ -8443,7 +8837,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) && col >= 0 && (size_t)col <= strlen(ml_get(lnum))) { tv_list_alloc_ret(rettv, kListLenMayKnow); - (void)syn_get_id(curwin, lnum, col, false, NULL, true); + syn_get_id(curwin, lnum, col, false, NULL, true); int id; int i = 0; @@ -8517,8 +8911,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (argvars[1].v_type != VAR_UNKNOWN) { fname = tv_get_string(&argvars[1]); } - (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), - (char *)tag_pattern, (char *)fname); + get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), + (char *)tag_pattern, (char *)fname); } /// "tempname()" function @@ -8558,8 +8952,8 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - CallbackReader on_stdout = CALLBACK_READER_INIT, - on_stderr = CALLBACK_READER_INIT; + CallbackReader on_stdout = CALLBACK_READER_INIT; + CallbackReader on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; const char *cwd = "."; @@ -8637,17 +9031,17 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) curbuf->b_p_swf = false; apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); - (void)setfname(curbuf, NameBuff, NULL, true); + setfname(curbuf, NameBuff, NULL, true); apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); // Save the job id and pid in b:terminal_job_{id,pid} Error err = ERROR_INIT; // deprecated: use 'channel' buffer option dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ((Integer)chan->id), false, false, &err); + INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err); api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), - INTEGER_OBJ(pid), false, false, &err); + INTEGER_OBJ(pid), false, false, NULL, &err); api_clear_error(&err); channel_incref(chan); diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 0c345dacb4..e3a574b233 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -3,7 +3,6 @@ #include <stdbool.h> #include <stdint.h> -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" #include "nvim/pos_defs.h" // IWYU pragma: keep diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h index 36149ec060..ea91952fff 100644 --- a/src/nvim/eval/gc.h +++ b/src/nvim/eval/gc.h @@ -6,5 +6,5 @@ extern dict_T *gc_first_dict; extern list_T *gc_first_list; #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "eval/gc.h.generated.h" // IWYU pragma: export +# include "eval/gc.h.generated.h" #endif diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 069cdced34..9328f53dbd 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -5,7 +5,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" @@ -20,10 +20,11 @@ #include "nvim/eval/vars.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" -#include "nvim/lib/queue.h" +#include "nvim/hashtab_defs.h" +#include "nvim/lib/queue_defs.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" @@ -622,13 +623,14 @@ tv_list_copy_error: listitem_T *tv_list_check_range_index_one(list_T *const l, int *const n1, const bool quiet) { listitem_T *li = tv_list_find_index(l, n1); - if (li == NULL) { - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n1)); - } - return NULL; + if (li != NULL) { + return li; } - return li; + + if (!quiet) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n1)); + } + return NULL; } /// Check that "n2" can be used as the second index in a range of list "l". @@ -1633,11 +1635,13 @@ static listitem_T *tv_list_find_index(list_T *const l, int *const idx) FUNC_ATTR_WARN_UNUSED_RESULT { listitem_T *li = tv_list_find(l, *idx); - if (li == NULL) { - if (*idx < 0) { - *idx = 0; - li = tv_list_find(l, *idx); - } + if (li != NULL) { + return li; + } + + if (*idx < 0) { + *idx = 0; + li = tv_list_find(l, *idx); } return li; } @@ -1801,10 +1805,10 @@ void callback_copy(Callback *dest, Callback *src) } /// Generate a string description of a callback -char *callback_to_string(Callback *cb) +char *callback_to_string(Callback *cb, Arena *arena) { if (cb->type == kCallbackLua) { - return nlua_funcref_str(cb->data.luaref); + return nlua_funcref_str(cb->data.luaref, arena); } const size_t msglen = 100; @@ -2129,10 +2133,12 @@ void tv_dict_free_dict(dict_T *const d) void tv_dict_free(dict_T *const d) FUNC_ATTR_NONNULL_ALL { - if (!tv_in_free_unref_items) { - tv_dict_free_contents(d); - tv_dict_free_dict(d); + if (tv_in_free_unref_items) { + return; } + + tv_dict_free_contents(d); + tv_dict_free_dict(d); } /// Unreference a dictionary @@ -2910,7 +2916,7 @@ static int tv_blob_index(const blob_T *blob, int len, varnumber_T idx, typval_T return OK; } -int tv_blob_slice_or_index(const blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, +int tv_blob_slice_or_index(const blob_T *blob, bool is_range, varnumber_T n1, varnumber_T n2, bool exclusive, typval_T *rettv) { int len = tv_blob_len(rettv->vval.v_blob); @@ -4345,6 +4351,22 @@ int tv_check_for_string_or_number_arg(const typval_T *const args, const int idx) return OK; } +/// Give an error and return FAIL unless "args[idx]" is a buffer number. +/// Buffer number can be a number or a string. +int tv_check_for_buffer_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + return tv_check_for_string_or_number_arg(args, idx); +} + +/// Give an error and return FAIL unless "args[idx]" is a line number. +/// Line number can be a number or a string. +int tv_check_for_lnum_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + return tv_check_for_string_or_number_arg(args, idx); +} + /// Give an error and return FAIL unless "args[idx]" is a string or a list. int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 58f74a9796..f9ebd2f778 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -6,14 +6,13 @@ #include <stdint.h> #include <string.h> -#include "nvim/eval/typval_defs.h" // IWYU pragma: export +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" -#include "nvim/garray_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/hashtab.h" -#include "nvim/lib/queue.h" +#include "nvim/lib/queue_defs.h" #include "nvim/macros_defs.h" -#include "nvim/mbyte_defs.h" +#include "nvim/mbyte_defs.h" // IWYU pragma: keep #include "nvim/message.h" #include "nvim/types_defs.h" @@ -85,6 +84,9 @@ static inline void tv_list_set_lock(list_T *const l, const VarLockStatus lock) l->lv_lock = lock; } +static inline void tv_list_set_copyid(list_T *l, int copyid) + REAL_FATTR_NONNULL_ALL; + /// Set list copyID /// /// Does not expect NULL list, be careful. @@ -92,7 +94,6 @@ static inline void tv_list_set_lock(list_T *const l, const VarLockStatus lock) /// @param[out] l List to modify. /// @param[in] copyid New copyID. static inline void tv_list_set_copyid(list_T *const l, const int copyid) - FUNC_ATTR_NONNULL_ALL { l->lv_copyID = copyid; } @@ -359,7 +360,7 @@ extern bool tv_in_free_unref_items; /// @param li Name of the variable with current listitem_T entry. /// @param code Cycle body. #define TV_LIST_ITER(l, li, code) \ - TV_LIST_ITER_MOD( , l, li, code) // NOLINT(whitespace/parens) + TV_LIST_ITER_MOD( , l, li, code) /// Iterate over a list /// @@ -442,22 +443,20 @@ static inline bool tv_get_float_chk(const typval_T *const tv, float_T *const ret } static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) - REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE - REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE - FUNC_ATTR_NO_SANITIZE_ADDRESS; + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET + REAL_FATTR_NO_SANITIZE_ADDRESS REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Compute the `DictWatcher` address from a QUEUE node. /// /// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer /// arithmetic). static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) - FUNC_ATTR_NO_SANITIZE_ADDRESS { return QUEUE_DATA(q, DictWatcher, node); } static inline bool tv_is_func(typval_T tv) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST; + REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; /// Check whether given typval_T contains a function /// diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h index c6bd11ccdb..0d6ee28adc 100644 --- a/src/nvim/eval/typval_defs.h +++ b/src/nvim/eval/typval_defs.h @@ -2,10 +2,11 @@ #include <inttypes.h> #include <limits.h> +#include <stdbool.h> #include "nvim/garray_defs.h" #include "nvim/hashtab_defs.h" -#include "nvim/lib/queue.h" +#include "nvim/lib/queue_defs.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" @@ -13,8 +14,10 @@ typedef int64_t varnumber_T; typedef uint64_t uvarnumber_T; -/// Refcount for dict or list that should not be freed -enum { DO_NOT_FREE_CNT = (INT_MAX / 2), }; +enum { + /// Refcount for dict or list that should not be freed + DO_NOT_FREE_CNT = (INT_MAX / 2), +}; /// Additional values for tv_list_alloc() len argument enum ListLenSpecials { @@ -72,7 +75,7 @@ typedef struct { #define CALLBACK_NONE ((Callback)CALLBACK_INIT) /// Structure holding dictionary watcher -typedef struct dict_watcher { +typedef struct { Callback callback; char *key_pattern; size_t key_pattern_len; @@ -291,12 +294,9 @@ typedef struct { uint64_t channel_id; /// Only used when script_id is SID_API_CLIENT. } LastSet; -/// Maximum number of function arguments -enum { MAX_FUNC_ARGS = 20, }; -/// Short variable name length -enum { VAR_SHORT_LEN = 20, }; -/// Number of fixed variables used for arguments -enum { FIXVAR_CNT = 12, }; +enum { MAX_FUNC_ARGS = 20, }; ///< Maximum number of function arguments +enum { VAR_SHORT_LEN = 20, }; ///< Short variable name length +enum { FIXVAR_CNT = 12, }; ///< Number of fixed variables used for arguments /// Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 2e0b68d486..c0cd0ce557 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -246,11 +246,12 @@ #include <inttypes.h> #include <stddef.h> +#include "klib/kvec.h" +#include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_encode.h" #include "nvim/func_attr.h" -#include "klib/kvec.h" /// Dummy variable used because some macros need lvalue /// diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 33fab82d21..d16814ed1e 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -10,6 +10,8 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" @@ -22,11 +24,13 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/ex_eval_defs.h" #include "nvim/ex_getln.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" #include "nvim/insexpand.h" @@ -41,11 +45,13 @@ #include "nvim/path.h" #include "nvim/profile.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -105,8 +111,6 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int * bool mustend = false; char *arg = *argp; char *p = arg; - uint8_t c; - int i; if (newargs != NULL) { ga_init(newargs, (int)sizeof(char *), 3); @@ -143,12 +147,12 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int * } if (newargs != NULL) { ga_grow(newargs, 1); - c = (uint8_t)(*p); + uint8_t c = (uint8_t)(*p); *p = NUL; arg = xstrdup(arg); // Check for duplicate argument name. - for (i = 0; i < newargs->ga_len; i++) { + for (int i = 0; i < newargs->ga_len; i++) { if (strcmp(((char **)(newargs->ga_data))[i], arg) == 0) { semsg(_("E853: Duplicate argument name: %s"), arg); xfree(arg); @@ -174,7 +178,7 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int * while (p > expr && ascii_iswhite(p[-1])) { p--; } - c = (uint8_t)(*p); + uint8_t c = (uint8_t)(*p); *p = NUL; expr = xstrdup(expr); ((char **)(default_args->ga_data))[default_args->ga_len] = expr; @@ -326,7 +330,6 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg) if (evaluate) { int flags = 0; - char *p; garray_T newlines; char *name = get_lambda_name(); @@ -339,7 +342,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg) // Add "return " before the expression. size_t len = (size_t)(7 + end - start + 1); - p = xmalloc(len); + char *p = xmalloc(len); ((char **)(newlines.ga_data))[newlines.ga_len++] = p; STRCPY(p, "return "); xstrlcpy(p + 7, start, (size_t)(end - start) + 1); @@ -919,11 +922,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett FUNC_ATTR_NONNULL_ARG(1, 3, 4) { bool using_sandbox = false; - int save_did_emsg; static int depth = 0; dictitem_T *v; int fixvar_idx = 0; // index in fc_fixvar[] - int ai; bool islambda = false; char numbuf[NUMBUFLEN]; char *name; @@ -931,7 +932,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett int tv_to_free_len = 0; proftime_T wait_start; proftime_T call_start; - int started_profiling = false; + bool started_profiling = false; bool did_save_redo = false; save_redo_T save_redo; char* saved_repeat_cmdline = NULL; @@ -1030,7 +1031,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett bool isdefault = false; typval_T def_rettv; - ai = i - fp->uf_args.ga_len; + int ai = i - fp->uf_args.ga_len; if (ai < 0) { // named argument a:name name = FUNCARG(fp, i); @@ -1173,7 +1174,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett const sctx_T save_current_sctx = current_sctx; current_sctx = fp->uf_script_ctx; - save_did_emsg = did_emsg; + int save_did_emsg = did_emsg; did_emsg = false; if (default_arg_err && (fp->uf_flags & FC_ABORT)) { @@ -1184,7 +1185,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // A Lambda always has the command "return {expr}". It is much faster // to evaluate {expr} directly. ex_nesting_level++; - (void)eval1(&p, rettv, &EVALARG_EVALUATE); + eval1(&p, rettv, &EVALARG_EVALUATE); ex_nesting_level--; } else { // call do_cmdline() to execute the lines @@ -2080,7 +2081,7 @@ char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi) if (strncmp(p, "<lambda>", 8) == 0) { p += 8; - (void)getdigits(&p, false, 0); + getdigits(&p, false, 0); saved = xmemdupz(*name, (size_t)(p - *name)); if (fudi != NULL) { CLEAR_POINTER(fudi); @@ -3044,7 +3045,7 @@ static inline bool fc_referenced(const funccall_T *const fc) /// @return true if items in "fc" do not have "copyID". That means they are not /// referenced from anywhere that is in use. -static int can_free_funccal(funccall_T *fc, int copyID) +static bool can_free_funccal(funccall_T *fc, int copyID) { return fc->fc_l_varlist.lv_copyID != copyID && fc->fc_l_vars.dv_copyID != copyID @@ -3057,7 +3058,7 @@ void ex_return(exarg_T *eap) { char *arg = eap->arg; typval_T rettv; - int returning = false; + bool returning = false; if (current_funccal == NULL) { emsg(_("E133: :return not inside a function")); @@ -3404,7 +3405,7 @@ end: /// /// @return true when the return can be carried out, /// false when the return gets pending. -int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) +bool do_return(exarg_T *eap, bool reanimate, bool is_cmd, void *rettv) { cstack_T *const cstack = eap->cstack; @@ -3669,7 +3670,7 @@ bool free_unref_funccal(int copyID, int testing) if (did_free_funccal) { // When a funccal was freed some more items might be garbage // collected, so run again. - (void)garbage_collect(testing); + garbage_collect(testing); } return did_free; } @@ -3825,7 +3826,7 @@ bool set_ref_in_previous_funccal(int copyID) fc->fc_copyID = copyID + 1; if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID + 1, NULL) || set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID + 1, NULL) - || set_ref_in_list(&fc->fc_l_varlist, copyID + 1, NULL)) { + || set_ref_in_list_items(&fc->fc_l_varlist, copyID + 1, NULL)) { return true; } } @@ -3838,7 +3839,7 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) fc->fc_copyID = copyID; if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID, NULL) || set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID, NULL) - || set_ref_in_list(&fc->fc_l_varlist, copyID, NULL) + || set_ref_in_list_items(&fc->fc_l_varlist, copyID, NULL) || set_ref_in_func(NULL, fc->fc_func, copyID)) { return true; } diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 8050caab2b..b3488b15a7 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -4,15 +4,13 @@ #include <stddef.h> #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep -#include "nvim/eval.h" #include "nvim/eval/typval_defs.h" +#include "nvim/eval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/hashtab_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" #include "nvim/types_defs.h" // IWYU pragma: keep -struct funccal_entry; - // From user function to hashitem and back. #define UF2HIKEY(fp) ((fp)->uf_name) #define HIKEY2UF(p) ((ufunc_T *)((p) - offsetof(ufunc_T, uf_name))) diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 2968f75f4d..91ac60d8ea 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -7,10 +7,11 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" @@ -18,16 +19,17 @@ #include "nvim/eval/encode.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" +#include "nvim/eval_defs.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" #include "nvim/macros_defs.h" @@ -44,6 +46,8 @@ #include "nvim/vim_defs.h" #include "nvim/window.h" +typedef int (*ex_unletlock_callback)(lval_T *, char *, exarg_T *, int); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/vars.c.generated.h" #endif @@ -377,7 +381,7 @@ void ex_let(exarg_T *eap) if (!eap->skip) { op[0] = '='; op[1] = NUL; - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); + ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); } tv_clear(&rettv); } @@ -414,7 +418,7 @@ void ex_let(exarg_T *eap) clear_evalarg(&evalarg, eap); if (!eap->skip && eval_res != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); + ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); } if (eval_res != FAIL) { tv_clear(&rettv); @@ -553,7 +557,7 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) static const char *skip_var_one(const char *arg) { if (*arg == '@' && arg[1] != NUL) { - return arg + 2; + return arg + 1 + utfc_ptr2len(arg + 1); } return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); @@ -617,7 +621,7 @@ static void list_tab_vars(int *first) /// List variables in "arg". static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) { - int error = false; + bool error = false; int len; const char *name; const char *name_start; @@ -762,11 +766,12 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, // Find the end of the name. char *arg_end = NULL; + OptIndex opt_idx; int scope; - char *const p = (char *)find_option_end((const char **)&arg, &scope); - if (p == NULL - || (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { + + char *const p = (char *)find_option_var_end((const char **)&arg, &opt_idx, &scope); + + if (p == NULL || (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { emsg(_(e_letunexp)); return NULL; } @@ -774,11 +779,12 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, const char c1 = *p; *p = NUL; - uint32_t opt_p_flags; - bool hidden; - OptVal curval = get_option_value(arg, &opt_p_flags, scope, &hidden); + bool is_tty_opt = is_tty_option(arg); + bool hidden = is_option_hidden(opt_idx); + OptVal curval = is_tty_opt ? get_tty_option(arg) : get_option_value(opt_idx, scope); OptVal newval = NIL_OPTVAL; - if (curval.type == kOptValTypeNil && arg[0] != 't' && arg[1] != '_') { + + if (curval.type == kOptValTypeNil) { semsg(_(e_unknown_option2), arg); goto theend; } @@ -790,7 +796,7 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, } bool error; - newval = tv_to_optval(tv, arg, opt_p_flags, &error); + newval = tv_to_optval(tv, opt_idx, arg, &error); if (error) { goto theend; } @@ -832,7 +838,7 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, } } - const char *err = set_option_value(arg, newval, scope); + const char *err = set_option_value_handle_tty(arg, opt_idx, newval, scope); arg_end = p; if (err != NULL) { emsg(_(err)); @@ -857,16 +863,20 @@ static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const, char *arg_end = NULL; arg++; + + int regname = utf_ptr2char(arg); + int mblen = utf_ptr2len(arg); + if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { semsg(_(e_letwrong), op); } else if (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) { + && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + mblen))) == NULL) { emsg(_(e_letunexp)); } else { char *ptofree = NULL; const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { - char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); + char *s = get_reg_contents(*arg == '@' ? '"' : regname, kGRegExprSrc); if (s != NULL) { ptofree = concat_str(s, p); p = ptofree; @@ -874,8 +884,9 @@ static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const, } } if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false); - arg_end = arg + 1; + write_reg_contents(*arg == '@' ? '"' : regname, + p, (ssize_t)strlen(p), false); + arg_end = arg + mblen; } xfree(ptofree); } @@ -1300,7 +1311,7 @@ void vars_clear(hashtab_T *ht) } /// Like vars_clear(), but only free the value if "free_val" is true. -void vars_clear_ext(hashtab_T *ht, int free_val) +void vars_clear_ext(hashtab_T *ht, bool free_val) { int todo; hashitem_T *hi; @@ -1847,22 +1858,27 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) /// /// @return Typval converted to OptVal. Must be freed by caller. /// Returns NIL_OPTVAL for invalid option name. -static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, bool *error) +/// +/// TODO(famiu): Refactor this to support multitype options. +static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, bool *error) { OptVal value = NIL_OPTVAL; char nbuf[NUMBUFLEN]; bool err = false; + const bool is_tty_opt = is_tty_option(option); + const bool option_has_bool = !is_tty_opt && option_has_type(opt_idx, kOptValTypeBoolean); + const bool option_has_num = !is_tty_opt && option_has_type(opt_idx, kOptValTypeNumber); + const bool option_has_str = is_tty_opt || option_has_type(opt_idx, kOptValTypeString); - if ((flags & P_FUNC) && tv_is_func(*tv)) { + if (!is_tty_opt && (get_option(opt_idx)->flags & P_FUNC) && tv_is_func(*tv)) { // If the option can be set to a function reference or a lambda // and the passed value is a function reference, then convert it to // the name (string) of the function reference. char *strval = encode_tv2string(tv, NULL); err = strval == NULL; value = CSTR_AS_OPTVAL(strval); - } else if (flags & (P_NUM | P_BOOL)) { - varnumber_T n = (flags & P_NUM) ? tv_get_number_chk(tv, &err) - : tv_get_bool_chk(tv, &err); + } else if (option_has_bool || option_has_num) { + varnumber_T n = option_has_num ? tv_get_number_chk(tv, &err) : tv_get_bool_chk(tv, &err); // This could be either "0" or a string that's not a number. // So we need to check if it's actually a number. if (!err && tv->v_type == VAR_STRING && n == 0) { @@ -1875,14 +1891,14 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo semsg(_("E521: Number required: &%s = '%s'"), option, tv->vval.v_string); } } - value = (flags & P_NUM) ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n)); - } else if ((flags & P_STRING) || is_tty_option(option)) { + value = option_has_num ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n)); + } else if (option_has_str) { // Avoid setting string option to a boolean or a special value. if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { const char *strval = tv_get_string_buf_chk(tv, nbuf); err = strval == NULL; value = CSTR_TO_OPTVAL(strval); - } else if (flags & P_STRING) { + } else if (!is_tty_opt) { err = true; emsg(_(e_stringreq)); } @@ -1898,10 +1914,12 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo /// Convert an option value to typval. /// -/// @param[in] value Option value to convert. +/// @param[in] value Option value to convert. +/// @param numbool Whether to convert boolean values to number. +/// Used for backwards compatibility. /// /// @return OptVal converted to typval. -typval_T optval_as_tv(OptVal value) +typval_T optval_as_tv(OptVal value, bool numbool) { typval_T rettv = { .v_type = VAR_SPECIAL, .vval = { .v_special = kSpecialVarNull } }; @@ -1909,19 +1927,16 @@ typval_T optval_as_tv(OptVal value) case kOptValTypeNil: break; case kOptValTypeBoolean: - switch (value.data.boolean) { - case kTrue: - rettv.v_type = VAR_BOOL; - rettv.vval.v_bool = kBoolVarTrue; - break; - case kFalse: - rettv.v_type = VAR_BOOL; - rettv.vval.v_bool = kBoolVarFalse; - break; - case kNone: - break; // return v:null for None boolean value + if (value.data.boolean != kNone) { + if (numbool) { + rettv.v_type = VAR_NUMBER; + rettv.vval.v_number = value.data.boolean == kTrue; + } else { + rettv.v_type = VAR_BOOL; + rettv.vval.v_bool = value.data.boolean == kTrue; + } } - break; + break; // return v:null for None boolean value. case kOptValTypeNumber: rettv.v_type = VAR_NUMBER; rettv.vval.v_number = value.data.number; @@ -1938,25 +1953,27 @@ typval_T optval_as_tv(OptVal value) /// Set option "varname" to the value of "varp" for the current buffer/window. static void set_option_from_tv(const char *varname, typval_T *varp) { - int opt_idx = findoption(varname); - if (opt_idx < 0) { + OptIndex opt_idx = find_option(varname); + if (opt_idx == kOptInvalid) { semsg(_(e_unknown_option2), varname); return; } - uint32_t opt_p_flags = get_option(opt_idx)->flags; bool error = false; - OptVal value = tv_to_optval(varp, varname, opt_p_flags, &error); + OptVal value = tv_to_optval(varp, opt_idx, varname, &error); if (!error) { - set_option_value_give_err(varname, value, OPT_LOCAL); - } + const char *errmsg = set_option_value_handle_tty(varname, opt_idx, value, OPT_LOCAL); + if (errmsg) { + emsg(errmsg); + } + } optval_free(value); } /// "setwinvar()" and "settabwinvar()" functions -static void setwinvar(typval_T *argvars, typval_T *rettv, int off) +static void setwinvar(typval_T *argvars, int off) { if (check_secure()) { return; @@ -2065,8 +2082,6 @@ void f_getbufvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "settabvar()" function void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - rettv->vval.v_number = 0; - if (check_secure()) { return; } @@ -2080,6 +2095,7 @@ void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } tabpage_T *const save_curtab = curtab; + tabpage_T *const save_lu_tp = lastused_tabpage; goto_tabpage_tp(tp, false, false); const size_t varname_len = strlen(varname); @@ -2089,22 +2105,25 @@ void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) set_var(tabvarname, varname_len + 2, varp, true); xfree(tabvarname); - // Restore current tabpage. + // Restore current tabpage and last accessed tabpage. if (valid_tabpage(save_curtab)) { goto_tabpage_tp(save_curtab, false, false); + if (valid_tabpage(save_lu_tp)) { + lastused_tabpage = save_lu_tp; + } } } /// "settabwinvar()" function void f_settabwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - setwinvar(argvars, rettv, 1); + setwinvar(argvars, 1); } /// "setwinvar()" function void f_setwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - setwinvar(argvars, rettv, 0); + setwinvar(argvars, 0); } /// "setbufvar()" function diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index e0abbad477..b8aa0c9641 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -9,19 +9,22 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" -#include "nvim/buffer_defs.h" #include "nvim/cursor.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/window.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" +#include "nvim/mark_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" +#include "nvim/option_vars.h" +#include "nvim/os/fs.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" #include "nvim/vim_defs.h" @@ -480,6 +483,68 @@ void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = nr; } +/// Switch to a window for executing user code. +/// Caller must call win_execute_after() later regardless of return value. +/// +/// @return whether switching the window succeeded. +bool win_execute_before(win_execute_T *args, win_T *wp, tabpage_T *tp) +{ + args->wp = wp; + args->curpos = wp->w_cursor; + args->cwd_status = FAIL; + args->apply_acd = false; + + // Getting and setting directory can be slow on some systems, only do + // this when the current or target window/tab have a local directory or + // 'acd' is set. + if (curwin != wp + && (curwin->w_localdir != NULL || wp->w_localdir != NULL + || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) + || p_acd)) { + args->cwd_status = os_dirname(args->cwd, MAXPATHL); + } + + // If 'acd' is set, check we are using that directory. If yes, then + // apply 'acd' afterwards, otherwise restore the current directory. + if (args->cwd_status == OK && p_acd) { + do_autochdir(); + char autocwd[MAXPATHL]; + if (os_dirname(autocwd, MAXPATHL) == OK) { + args->apply_acd = strcmp(args->cwd, autocwd) == 0; + } + } + + if (switch_win_noblock(&args->switchwin, wp, tp, true) == OK) { + check_cursor(); + return true; + } + return false; +} + +/// Restore the previous window after executing user code. +void win_execute_after(win_execute_T *args) +{ + restore_win_noblock(&args->switchwin, true); + + if (args->apply_acd) { + do_autochdir(); + } else if (args->cwd_status == OK) { + os_chdir(args->cwd); + } + + // Update the status line if the cursor moved. + if (win_valid(args->wp) && !equalpos(args->curpos, args->wp->w_cursor)) { + args->wp->w_redr_status = true; + } + + // In case the command moved the cursor or changed the Visual area, + // check it is valid. + check_cursor(); + if (VIsual_active) { + check_pos(curbuf, &VIsual); + } +} + /// "win_execute(win_id, command)" function void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -494,7 +559,11 @@ void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, 1)); + win_execute_T win_execute_args; + if (win_execute_before(&win_execute_args, wp, tp)) { + execute_common(argvars, rettv, 1); + } + win_execute_after(&win_execute_args); } /// "win_findbuf()" function @@ -607,13 +676,13 @@ static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags // Remove the old window and frame from the tree of frames int dir; - (void)winframe_remove(wp, &dir, NULL); + winframe_remove(wp, &dir, NULL); win_remove(wp, NULL); last_status(false); // may need to remove last status line - (void)win_comp_pos(); // recompute window positions + win_comp_pos(); // recompute window positions // Split a window on the desired side and put the old window there - (void)win_split_ins(size, flags, wp, dir); + win_split_ins(size, flags, wp, dir); // If splitting horizontally, try to preserve height if (size == 0 && !(flags & WSP_VERT)) { @@ -642,7 +711,8 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - int flags = 0, size = 0; + int flags = 0; + int size = 0; if (argvars[2].v_type != VAR_UNKNOWN) { dict_T *d; @@ -685,7 +755,7 @@ void f_win_gettype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_string = xstrdup("preview"); } else if (wp->w_floating) { rettv->vval.v_string = xstrdup("popup"); - } else if (wp == curwin && cmdwin_type != 0) { + } else if (wp == cmdwin_win) { rettv->vval.v_string = xstrdup("command"); } else if (bt_quickfix(wp->w_buffer)) { rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix")); @@ -914,16 +984,16 @@ int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool n return OK; } -// Restore current tabpage and window saved by switch_win(), if still valid. -// When "no_display" is true the display won't be affected, no redraw is -// triggered. +/// Restore current tabpage and window saved by switch_win(), if still valid. +/// When "no_display" is true the display won't be affected, no redraw is +/// triggered. void restore_win(switchwin_T *switchwin, bool no_display) { restore_win_noblock(switchwin, no_display); unblock_autocmds(); } -// As restore_win() but without unblocking autocommands. +/// As restore_win() but without unblocking autocommands. void restore_win_noblock(switchwin_T *switchwin, bool no_display) { if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) { diff --git a/src/nvim/eval/window.h b/src/nvim/eval/window.h index ed879c895a..37cb138404 100644 --- a/src/nvim/eval/window.h +++ b/src/nvim/eval/window.h @@ -1,20 +1,12 @@ #pragma once #include <stdbool.h> -#include <string.h> -#include "nvim/buffer.h" #include "nvim/buffer_defs.h" -#include "nvim/cursor.h" -#include "nvim/eval/typval_defs.h" -#include "nvim/globals.h" -#include "nvim/mark.h" -#include "nvim/option_defs.h" -#include "nvim/option_vars.h" -#include "nvim/os/fs.h" +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep +#include "nvim/os/os_defs.h" #include "nvim/pos_defs.h" -#include "nvim/vim_defs.h" -#include "nvim/window.h" +#include "nvim/types_defs.h" /// Structure used by switch_win() to pass values to restore_win() typedef struct { @@ -24,53 +16,15 @@ typedef struct { bool sw_visual_active; } switchwin_T; -/// Execute a block of code in the context of window `wp` in tabpage `tp`. -/// Ensures the status line is redrawn and cursor position is valid if it is moved. -#define WIN_EXECUTE(wp, tp, block) \ - do { \ - win_T *const wp_ = (wp); \ - const pos_T curpos_ = wp_->w_cursor; \ - char cwd_[MAXPATHL]; \ - char autocwd_[MAXPATHL]; \ - bool apply_acd_ = false; \ - int cwd_status_ = FAIL; \ - /* Getting and setting directory can be slow on some systems, only do */ \ - /* this when the current or target window/tab have a local directory or */ \ - /* 'acd' is set. */ \ - if (curwin != wp \ - && (curwin->w_localdir != NULL || wp->w_localdir != NULL \ - || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) \ - || p_acd)) { \ - cwd_status_ = os_dirname(cwd_, MAXPATHL); \ - } \ - /* If 'acd' is set, check we are using that directory. If yes, then */ \ - /* apply 'acd' afterwards, otherwise restore the current directory. */ \ - if (cwd_status_ == OK && p_acd) { \ - do_autochdir(); \ - apply_acd_ = os_dirname(autocwd_, MAXPATHL) == OK && strcmp(cwd_, autocwd_) == 0; \ - } \ - switchwin_T switchwin_; \ - if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \ - check_cursor(); \ - block; \ - } \ - restore_win_noblock(&switchwin_, true); \ - if (apply_acd_) { \ - do_autochdir(); \ - } else if (cwd_status_ == OK) { \ - os_chdir(cwd_); \ - } \ - /* Update the status line if the cursor moved. */ \ - if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \ - wp_->w_redr_status = true; \ - } \ - /* In case the command moved the cursor or changed the Visual area, */ \ - /* check it is valid. */ \ - check_cursor(); \ - if (VIsual_active) { \ - check_pos(curbuf, &VIsual); \ - } \ - } while (false) +/// Structure used by win_execute_before() to pass values to win_execute_after() +typedef struct { + win_T *wp; + pos_T curpos; + char cwd[MAXPATHL]; + int cwd_status; + bool apply_acd; + switchwin_T switchwin; +} win_execute_T; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/window.h.generated.h" diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h new file mode 100644 index 0000000000..4bbebb14f5 --- /dev/null +++ b/src/nvim/eval_defs.h @@ -0,0 +1,30 @@ +#pragma once + +#include "nvim/ex_cmds_defs.h" + +/// All recognized msgpack types +typedef enum { + kMPNil, + kMPBoolean, + kMPInteger, + kMPFloat, + kMPString, + kMPBinary, + kMPArray, + kMPMap, + kMPExt, +} MessagePackType; +#define NUM_MSGPACK_TYPES (kMPExt + 1) + +/// Struct passed through eval() functions. +/// See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE. +typedef struct { + int eval_flags; ///< EVAL_ flag values below + + /// copied from exarg_T when "getline" is "getsourceline". Can be NULL. + LineGetter eval_getline; + void *eval_cookie; ///< argument for eval_getline() + + /// pointer to the last line obtained with getsourceline() + char *eval_tofree; +} evalarg_T; diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h index 571f61dfdb..9b7d8708be 100644 --- a/src/nvim/event/defs.h +++ b/src/nvim/event/defs.h @@ -2,34 +2,155 @@ #include <assert.h> #include <stdarg.h> +#include <stdbool.h> +#include <uv.h> -#define EVENT_HANDLER_MAX_ARGC 10 +#include "nvim/eval/typval_defs.h" +#include "nvim/rbuffer_defs.h" +#include "nvim/types_defs.h" + +enum { EVENT_HANDLER_MAX_ARGC = 10, }; typedef void (*argv_callback)(void **argv); -typedef struct message { +typedef struct { argv_callback handler; void *argv[EVENT_HANDLER_MAX_ARGC]; } Event; -typedef void (*event_scheduler)(Event event, void *data); - -#define VA_EVENT_INIT(event, h, a) \ - do { \ - assert(a <= EVENT_HANDLER_MAX_ARGC); \ - (event)->handler = h; \ - if (a) { \ - va_list args; \ - va_start(args, a); \ - for (int i = 0; i < a; i++) { \ - (event)->argv[i] = va_arg(args, void *); \ - } \ - va_end(args); \ - } \ - } while (0) - -static inline Event event_create(argv_callback cb, int argc, ...) -{ - assert(argc <= EVENT_HANDLER_MAX_ARGC); - Event event; - VA_EVENT_INIT(&event, cb, argc); - return event; -} + +#define event_create(cb, ...) ((Event){ .handler = cb, .argv = { __VA_ARGS__ } }) + +typedef struct multiqueue MultiQueue; +typedef void (*PutCallback)(MultiQueue *multiq, void *data); + +typedef struct signal_watcher SignalWatcher; +typedef void (*signal_cb)(SignalWatcher *watcher, int signum, void *data); +typedef void (*signal_close_cb)(SignalWatcher *watcher, void *data); + +struct signal_watcher { + uv_signal_t uv; + void *data; + signal_cb cb; + signal_close_cb close_cb; + MultiQueue *events; +}; + +typedef struct time_watcher TimeWatcher; +typedef void (*time_cb)(TimeWatcher *watcher, void *data); + +struct time_watcher { + uv_timer_t uv; + void *data; + time_cb cb, close_cb; + MultiQueue *events; + bool blockable; +}; + +typedef struct wbuffer WBuffer; +typedef void (*wbuffer_data_finalizer)(void *data); + +struct wbuffer { + size_t size, refcount; + char *data; + wbuffer_data_finalizer cb; +}; + +typedef struct stream Stream; +/// Type of function called when the Stream buffer is filled with data +/// +/// @param stream The Stream instance +/// @param buf The associated RBuffer instance +/// @param count Number of bytes that was read. +/// @param data User-defined data +/// @param eof If the stream reached EOF. +typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof); + +/// Type of function called when the Stream has information about a write +/// request. +/// +/// @param stream The Stream instance +/// @param data User-defined data +/// @param status 0 on success, anything else indicates failure +typedef void (*stream_write_cb)(Stream *stream, void *data, int status); +typedef void (*stream_close_cb)(Stream *stream, void *data); + +struct stream { + bool closed; + bool did_eof; + union { + uv_pipe_t pipe; + uv_tcp_t tcp; + uv_idle_t idle; +#ifdef MSWIN + uv_tty_t tty; +#endif + } uv; + uv_stream_t *uvstream; + uv_buf_t uvbuf; + RBuffer *buffer; + uv_file fd; + stream_read_cb read_cb; + stream_write_cb write_cb; + void *cb_data; + stream_close_cb close_cb, internal_close_cb; + void *close_cb_data, *internal_data; + size_t fpos; + size_t curmem; + size_t maxmem; + size_t pending_reqs; + size_t num_bytes; + MultiQueue *events; +}; + +#define ADDRESS_MAX_SIZE 256 + +typedef struct socket_watcher SocketWatcher; +typedef void (*socket_cb)(SocketWatcher *watcher, int result, void *data); +typedef void (*socket_close_cb)(SocketWatcher *watcher, void *data); + +struct socket_watcher { + // Pipe/socket path, or TCP address string + char addr[ADDRESS_MAX_SIZE]; + // TCP server or unix socket (named pipe on Windows) + union { + struct { + uv_tcp_t handle; + struct addrinfo *addrinfo; + } tcp; + struct { + uv_pipe_t handle; + } pipe; + } uv; + uv_stream_t *stream; + void *data; + socket_cb cb; + socket_close_cb close_cb; + MultiQueue *events; +}; + +typedef enum { + kProcessTypeUv, + kProcessTypePty, +} ProcessType; + +typedef struct process Process; +typedef void (*process_exit_cb)(Process *proc, int status, void *data); +typedef void (*internal_process_cb)(Process *proc); + +struct process { + ProcessType type; + Loop *loop; + void *data; + int pid, status, refcount; + uint8_t exit_signal; // Signal used when killing (on Windows). + uint64_t stopped_time; // process_stop() timestamp + const char *cwd; + char **argv; + const char *exepath; + dict_T *env; + Stream in, out, err; + /// Exit handler. If set, user must call process_free(). + process_exit_cb cb; + internal_process_cb internal_exit_cb, internal_close_cb; + bool closed, detach, overlapped, fwd_err; + MultiQueue *events; +}; diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index be48b39af1..a7966994e0 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -4,12 +4,14 @@ #include <uv.h> #include "nvim/eval/typval.h" +#include "nvim/event/defs.h" #include "nvim/event/libuv_process.h" +#include "nvim/event/loop.h" #include "nvim/event/process.h" -#include "nvim/event/stream.h" -#include "nvim/func_attr.h" #include "nvim/log.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" +#include "nvim/types_defs.h" #include "nvim/ui_client.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -127,3 +129,11 @@ static void exit_cb(uv_process_t *handle, int64_t status, int term_signal) proc->status = term_signal ? 128 + term_signal : (int)status; proc->internal_exit_cb(proc); } + +LibuvProcess libuv_process_init(Loop *loop, void *data) +{ + LibuvProcess rv = { + .process = process_init(loop, kProcessTypeUv, data) + }; + return rv; +} diff --git a/src/nvim/event/libuv_process.h b/src/nvim/event/libuv_process.h index e3e2bfeb76..12401dbb35 100644 --- a/src/nvim/event/libuv_process.h +++ b/src/nvim/event/libuv_process.h @@ -2,24 +2,15 @@ #include <uv.h> -#include "nvim/event/loop.h" -#include "nvim/event/process.h" +#include "nvim/event/defs.h" -typedef struct libuv_process { +typedef struct { Process process; uv_process_t uv; uv_process_options_t uvopts; uv_stdio_container_t uvstdio[4]; } LibuvProcess; -static inline LibuvProcess libuv_process_init(Loop *loop, void *data) -{ - LibuvProcess rv = { - .process = process_init(loop, kProcessTypeUv, data) - }; - return rv; -} - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/libuv_process.h.generated.h" #endif diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index d61666e6d4..93948d3eaa 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -3,11 +3,12 @@ #include <stdlib.h> #include <uv.h> -#include "nvim/event/defs.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/log.h" #include "nvim/memory.h" #include "nvim/os/time.h" +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/loop.c.generated.h" @@ -111,7 +112,7 @@ void loop_schedule_deferred(Loop *loop, Event event) { Event *eventp = xmalloc(sizeof(*eventp)); *eventp = event; - loop_schedule_fast(loop, event_create(loop_deferred_event, 2, loop, eventp)); + loop_schedule_fast(loop, event_create(loop_deferred_event, loop, eventp)); } static void loop_deferred_event(void **argv) { diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h index 5665332e95..6ecc7cb781 100644 --- a/src/nvim/event/loop.h +++ b/src/nvim/event/loop.h @@ -1,19 +1,18 @@ #pragma once #include <stdbool.h> -#include <stdint.h> #include <uv.h> #include "klib/klist.h" -#include "nvim/event/multiqueue.h" -#include "nvim/os/time.h" +#include "nvim/event/defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep typedef void *WatcherPtr; #define _NOOP(x) KLIST_INIT(WatcherPtr, WatcherPtr, _NOOP) -typedef struct loop { +struct loop { uv_loop_t uv; MultiQueue *events; MultiQueue *thread_events; @@ -42,46 +41,7 @@ typedef struct loop { uv_mutex_t mutex; int recursive; bool closing; ///< Set to true if loop_close() has been called -} Loop; - -#define CREATE_EVENT(multiqueue, handler, argc, ...) \ - do { \ - if (multiqueue) { \ - multiqueue_put((multiqueue), (handler), argc, __VA_ARGS__); \ - } else { \ - void *argv[argc] = { __VA_ARGS__ }; \ - (handler)(argv); \ - } \ - } while (0) - -// Poll for events until a condition or timeout -#define LOOP_PROCESS_EVENTS_UNTIL(loop, multiqueue, timeout, condition) \ - do { \ - int64_t remaining = timeout; \ - uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ - while (!(condition)) { \ - LOOP_PROCESS_EVENTS(loop, multiqueue, remaining); \ - if (remaining == 0) { \ - break; \ - } else if (remaining > 0) { \ - uint64_t now = os_hrtime(); \ - remaining -= (int64_t)((now - before) / 1000000); \ - before = now; \ - if (remaining <= 0) { \ - break; \ - } \ - } \ - } \ - } while (0) - -#define LOOP_PROCESS_EVENTS(loop, multiqueue, timeout) \ - do { \ - if (multiqueue && !multiqueue_empty(multiqueue)) { \ - multiqueue_process_events(multiqueue); \ - } else { \ - loop_poll_events(loop, timeout); \ - } \ - } while (0) +}; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/loop.h.generated.h" diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index 3ab41bd299..8646173776 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -48,8 +48,7 @@ #include "nvim/event/defs.h" #include "nvim/event/multiqueue.h" -#include "nvim/func_attr.h" -#include "nvim/lib/queue.h" +#include "nvim/lib/queue_defs.h" #include "nvim/memory.h" typedef struct multiqueue_item MultiQueueItem; @@ -157,7 +156,7 @@ void multiqueue_purge_events(MultiQueue *self) { assert(self); while (!multiqueue_empty(self)) { - (void)multiqueue_remove(self); + multiqueue_remove(self); } } @@ -261,7 +260,7 @@ Event event_create_oneshot(Event ev, int num) data->event = ev; data->fired = false; data->refcount = num; - return event_create(multiqueue_oneshot_event, 1, data); + return event_create(multiqueue_oneshot_event, data); } static void multiqueue_oneshot_event(void **argv) { diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h index e01ee1e710..24aaa34ef7 100644 --- a/src/nvim/event/multiqueue.h +++ b/src/nvim/event/multiqueue.h @@ -1,16 +1,54 @@ #pragma once -#include <uv.h> +#include <stddef.h> // IWYU pragma: keep -#include "nvim/event/defs.h" -#include "nvim/lib/queue.h" - -typedef struct multiqueue MultiQueue; -typedef void (*PutCallback)(MultiQueue *multiq, void *data); - -#define multiqueue_put(q, h, ...) \ - multiqueue_put_event(q, event_create(h, __VA_ARGS__)); +#include "nvim/event/defs.h" // IWYU pragma: keep +#include "nvim/os/time.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/multiqueue.h.generated.h" #endif + +#define multiqueue_put(q, h, ...) \ + do { \ + multiqueue_put_event(q, event_create(h, __VA_ARGS__)); \ + } while (0) + +#define CREATE_EVENT(multiqueue, handler, ...) \ + do { \ + if (multiqueue) { \ + multiqueue_put((multiqueue), (handler), __VA_ARGS__); \ + } else { \ + void *argv[] = { __VA_ARGS__ }; \ + (handler)(argv); \ + } \ + } while (0) + +// Poll for events until a condition or timeout +#define LOOP_PROCESS_EVENTS_UNTIL(loop, multiqueue, timeout, condition) \ + do { \ + int64_t remaining = timeout; \ + uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ + while (!(condition)) { \ + LOOP_PROCESS_EVENTS(loop, multiqueue, remaining); \ + if (remaining == 0) { \ + break; \ + } else if (remaining > 0) { \ + uint64_t now = os_hrtime(); \ + remaining -= (int64_t)((now - before) / 1000000); \ + before = now; \ + if (remaining <= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LOOP_PROCESS_EVENTS(loop, multiqueue, timeout) \ + do { \ + if (multiqueue && !multiqueue_empty(multiqueue)) { \ + multiqueue_process_events(multiqueue); \ + } else { \ + loop_poll_events(loop, timeout); \ + } \ + } while (0) diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 864fc2c1d8..7460e92766 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -6,8 +6,9 @@ #include "klib/klist.h" #include "nvim/event/libuv_process.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/process.h" -#include "nvim/func_attr.h" +#include "nvim/event/stream.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/main.h" @@ -15,7 +16,7 @@ #include "nvim/os/pty_process.h" #include "nvim/os/shell.h" #include "nvim/os/time.h" -#include "nvim/rbuffer.h" +#include "nvim/rbuffer_defs.h" #include "nvim/ui_client.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -133,7 +134,7 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL Process *proc = (*current)->data; if (proc->detach || proc->type == kProcessTypePty) { // Close handles to process without killing it. - CREATE_EVENT(loop->events, process_close_handles, 1, proc); + CREATE_EVENT(loop->events, process_close_handles, proc); } else { process_stop(proc); } @@ -301,7 +302,7 @@ static void decref(Process *proc) } assert(node); kl_shift_at(WatcherPtr, loop->children, node); - CREATE_EVENT(proc->events, process_close_event, 1, proc); + CREATE_EVENT(proc->events, process_close_event, proc); } static void process_close(Process *proc) @@ -396,7 +397,7 @@ static void process_close_handles(void **argv) static void exit_delay_cb(uv_timer_t *handle) { uv_timer_stop(&main_loop.exit_delay_timer); - multiqueue_put(main_loop.fast_events, exit_event, 1, main_loop.exit_delay_timer.data); + multiqueue_put(main_loop.fast_events, exit_event, main_loop.exit_delay_timer.data); } static void exit_event(void **argv) @@ -421,7 +422,7 @@ static void exit_event(void **argv) void exit_from_channel(int status) { - multiqueue_put(main_loop.fast_events, exit_event, 1, status); + multiqueue_put(main_loop.fast_events, exit_event, (void *)(intptr_t)status); } static void on_process_exit(Process *proc) @@ -439,7 +440,7 @@ static void on_process_exit(Process *proc) // more data directly. Instead delay the reading after the libuv loop by // queueing process_close_handles() as an event. MultiQueue *queue = proc->events ? proc->events : loop->events; - CREATE_EVENT(queue, process_close_handles, 1, proc); + CREATE_EVENT(queue, process_close_handles, proc); } static void on_process_stream_close(Stream *stream, void *data) diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 234fc815af..421a470244 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -2,44 +2,9 @@ #include <stdbool.h> #include <stddef.h> -#include <stdint.h> -#include "nvim/eval/typval_defs.h" -#include "nvim/event/loop.h" -#include "nvim/event/multiqueue.h" -#include "nvim/event/rstream.h" -#include "nvim/event/stream.h" -#include "nvim/event/wstream.h" - -struct process; - -typedef enum { - kProcessTypeUv, - kProcessTypePty, -} ProcessType; - -typedef struct process Process; -typedef void (*process_exit_cb)(Process *proc, int status, void *data); -typedef void (*internal_process_cb)(Process *proc); - -struct process { - ProcessType type; - Loop *loop; - void *data; - int pid, status, refcount; - uint8_t exit_signal; // Signal used when killing (on Windows). - uint64_t stopped_time; // process_stop() timestamp - const char *cwd; - char **argv; - const char *exepath; - dict_T *env; - Stream in, out, err; - /// Exit handler. If set, user must call process_free(). - process_exit_cb cb; - internal_process_cb internal_exit_cb, internal_close_cb; - bool closed, detach, overlapped, fwd_err; - MultiQueue *events; -}; +#include "nvim/event/defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" static inline Process process_init(Loop *loop, ProcessType type, void *data) { diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 73828a2271..f50c8a0e5a 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -2,18 +2,18 @@ #include <stdbool.h> #include <stddef.h> #include <stdint.h> -#include <sys/types.h> #include <uv.h> -#include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/rstream.h" #include "nvim/event/stream.h" -#include "nvim/func_attr.h" #include "nvim/log.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/os/os_defs.h" #include "nvim/rbuffer.h" +#include "nvim/rbuffer_defs.h" +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/rstream.c.generated.h" @@ -201,10 +201,6 @@ static void invoke_read_cb(Stream *stream, size_t count, bool eof) // Don't let the stream be closed before the event is processed. stream->pending_reqs++; - CREATE_EVENT(stream->events, - read_event, - 3, - stream, - (void *)(uintptr_t *)count, - (void *)(uintptr_t)eof); + CREATE_EVENT(stream->events, read_event, + stream, (void *)(uintptr_t *)count, (void *)(uintptr_t)eof); } diff --git a/src/nvim/event/rstream.h b/src/nvim/event/rstream.h index b2a62acf83..4e2893bf88 100644 --- a/src/nvim/event/rstream.h +++ b/src/nvim/event/rstream.h @@ -1,11 +1,7 @@ #pragma once -#include <stdbool.h> -#include <stddef.h> -#include <uv.h> - -#include "nvim/event/loop.h" -#include "nvim/event/stream.h" +#include "nvim/event/defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/rstream.h.generated.h" diff --git a/src/nvim/event/signal.c b/src/nvim/event/signal.c index e64d526856..57241e79b2 100644 --- a/src/nvim/event/signal.c +++ b/src/nvim/event/signal.c @@ -1,9 +1,11 @@ #include <stddef.h> #include <uv.h> +#include "nvim/event/defs.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/signal.h" -#include "nvim/func_attr.h" +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/signal.c.generated.h" @@ -48,7 +50,7 @@ static void signal_event(void **argv) static void signal_watcher_cb(uv_signal_t *handle, int signum) { SignalWatcher *watcher = handle->data; - CREATE_EVENT(watcher->events, signal_event, 1, watcher); + CREATE_EVENT(watcher->events, signal_event, watcher); } static void close_cb(uv_handle_t *handle) diff --git a/src/nvim/event/signal.h b/src/nvim/event/signal.h index 946de1b4f0..16cd2be951 100644 --- a/src/nvim/event/signal.h +++ b/src/nvim/event/signal.h @@ -1,23 +1,7 @@ #pragma once -#include <uv.h> - -#include "nvim/event/loop.h" -#include "nvim/event/multiqueue.h" - -struct signal_watcher; - -typedef struct signal_watcher SignalWatcher; -typedef void (*signal_cb)(SignalWatcher *watcher, int signum, void *data); -typedef void (*signal_close_cb)(SignalWatcher *watcher, void *data); - -struct signal_watcher { - uv_signal_t uv; - void *data; - signal_cb cb; - signal_close_cb close_cb; - MultiQueue *events; -}; +#include "nvim/event/defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/signal.h.generated.h" diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c index e787e023f0..4e878a2ecf 100644 --- a/src/nvim/event/socket.c +++ b/src/nvim/event/socket.c @@ -7,17 +7,19 @@ #include "nvim/ascii_defs.h" #include "nvim/charset.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/socket.h" #include "nvim/event/stream.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/log.h" #include "nvim/main.h" #include "nvim/memory.h" #include "nvim/os/fs.h" -#include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/socket.c.generated.h" @@ -175,8 +177,7 @@ static void connection_event(void **argv) static void connection_cb(uv_stream_t *handle, int status) { SocketWatcher *watcher = handle->data; - CREATE_EVENT(watcher->events, connection_event, 2, watcher, - (void *)(uintptr_t)status); + CREATE_EVENT(watcher->events, connection_event, watcher, (void *)(uintptr_t)status); } static void close_cb(uv_handle_t *handle) diff --git a/src/nvim/event/socket.h b/src/nvim/event/socket.h index 504af3c7a8..64a77a9a06 100644 --- a/src/nvim/event/socket.h +++ b/src/nvim/event/socket.h @@ -1,39 +1,7 @@ #pragma once -#include <uv.h> - -#include "nvim/event/loop.h" -#include "nvim/event/multiqueue.h" -#include "nvim/event/rstream.h" -#include "nvim/event/wstream.h" - -struct socket_watcher; - -#define ADDRESS_MAX_SIZE 256 - -typedef struct socket_watcher SocketWatcher; -typedef void (*socket_cb)(SocketWatcher *watcher, int result, void *data); -typedef void (*socket_close_cb)(SocketWatcher *watcher, void *data); - -struct socket_watcher { - // Pipe/socket path, or TCP address string - char addr[ADDRESS_MAX_SIZE]; - // TCP server or unix socket (named pipe on Windows) - union { - struct { - uv_tcp_t handle; - struct addrinfo *addrinfo; - } tcp; - struct { - uv_pipe_t handle; - } pipe; - } uv; - uv_stream_t *stream; - void *data; - socket_cb cb; - socket_close_cb close_cb; - MultiQueue *events; -}; +#include "nvim/event/defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/socket.h.generated.h" diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c index aff116bad9..0b9ed4f25b 100644 --- a/src/nvim/event/stream.c +++ b/src/nvim/event/stream.c @@ -4,11 +4,12 @@ #include <uv.h> #include <uv/version.h> +#include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/stream.h" -#include "nvim/func_attr.h" #include "nvim/log.h" #include "nvim/rbuffer.h" +#include "nvim/types_defs.h" #ifdef MSWIN # include "nvim/os/os_win_console.h" #endif @@ -129,15 +130,22 @@ void stream_may_close(Stream *stream) void stream_close_handle(Stream *stream) FUNC_ATTR_NONNULL_ALL { + uv_handle_t *handle = NULL; if (stream->uvstream) { if (uv_stream_get_write_queue_size(stream->uvstream) > 0) { WLOG("closed Stream (%p) with %zu unwritten bytes", (void *)stream, uv_stream_get_write_queue_size(stream->uvstream)); } - uv_close((uv_handle_t *)stream->uvstream, close_cb); + handle = (uv_handle_t *)stream->uvstream; } else { - uv_close((uv_handle_t *)&stream->uv.idle, close_cb); + handle = (uv_handle_t *)&stream->uv.idle; + } + + assert(handle != NULL); + + if (!uv_is_closing(handle)) { + uv_close(handle, close_cb); } } diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h index d02707dc45..9bdfc421d6 100644 --- a/src/nvim/event/stream.h +++ b/src/nvim/event/stream.h @@ -1,61 +1,7 @@ #pragma once -#include <stdbool.h> -#include <stddef.h> -#include <uv.h> - -#include "nvim/event/loop.h" -#include "nvim/event/multiqueue.h" -#include "nvim/rbuffer.h" - -struct stream; - -typedef struct stream Stream; -/// Type of function called when the Stream buffer is filled with data -/// -/// @param stream The Stream instance -/// @param buf The associated RBuffer instance -/// @param count Number of bytes that was read. -/// @param data User-defined data -/// @param eof If the stream reached EOF. -typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof); - -/// Type of function called when the Stream has information about a write -/// request. -/// -/// @param stream The Stream instance -/// @param data User-defined data -/// @param status 0 on success, anything else indicates failure -typedef void (*stream_write_cb)(Stream *stream, void *data, int status); -typedef void (*stream_close_cb)(Stream *stream, void *data); - -struct stream { - bool closed; - bool did_eof; - union { - uv_pipe_t pipe; - uv_tcp_t tcp; - uv_idle_t idle; -#ifdef MSWIN - uv_tty_t tty; -#endif - } uv; - uv_stream_t *uvstream; - uv_buf_t uvbuf; - RBuffer *buffer; - uv_file fd; - stream_read_cb read_cb; - stream_write_cb write_cb; - void *cb_data; - stream_close_cb close_cb, internal_close_cb; - void *close_cb_data, *internal_data; - size_t fpos; - size_t curmem; - size_t maxmem; - size_t pending_reqs; - size_t num_bytes; - MultiQueue *events; -}; +#include "nvim/event/defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/stream.h.generated.h" diff --git a/src/nvim/event/time.c b/src/nvim/event/time.c index f678f25f3f..861b15f6dd 100644 --- a/src/nvim/event/time.c +++ b/src/nvim/event/time.c @@ -1,9 +1,12 @@ +#include <stdbool.h> #include <stdint.h> #include <uv.h> +#include "nvim/event/defs.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/time.h" -#include "nvim/func_attr.h" +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/time.c.generated.h" @@ -53,7 +56,7 @@ static void time_watcher_cb(uv_timer_t *handle) // the timer blocked and there already is an unprocessed event waiting return; } - CREATE_EVENT(watcher->events, time_event, 1, watcher); + CREATE_EVENT(watcher->events, time_event, watcher); } static void close_event(void **argv) @@ -67,6 +70,6 @@ static void close_cb(uv_handle_t *handle) { TimeWatcher *watcher = handle->data; if (watcher->close_cb) { - CREATE_EVENT(watcher->events, close_event, 1, watcher); + CREATE_EVENT(watcher->events, close_event, watcher); } } diff --git a/src/nvim/event/time.h b/src/nvim/event/time.h index 3514566901..d1f92a3c0e 100644 --- a/src/nvim/event/time.h +++ b/src/nvim/event/time.h @@ -1,23 +1,7 @@ #pragma once -#include <stdbool.h> -#include <uv.h> - -#include "nvim/event/loop.h" -#include "nvim/event/multiqueue.h" - -struct time_watcher; - -typedef struct time_watcher TimeWatcher; -typedef void (*time_cb)(TimeWatcher *watcher, void *data); - -struct time_watcher { - uv_timer_t uv; - void *data; - time_cb cb, close_cb; - MultiQueue *events; - bool blockable; -}; +#include "nvim/event/defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/time.h.generated.h" diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c index e8f757874b..406ff1620d 100644 --- a/src/nvim/event/wstream.c +++ b/src/nvim/event/wstream.c @@ -1,13 +1,14 @@ #include <assert.h> #include <stdbool.h> +#include <stddef.h> #include <uv.h> -#include "nvim/event/loop.h" +#include "nvim/event/defs.h" #include "nvim/event/stream.h" #include "nvim/event/wstream.h" -#include "nvim/func_attr.h" #include "nvim/macros_defs.h" #include "nvim/memory.h" +#include "nvim/types_defs.h" #define DEFAULT_MAXMEM 1024 * 1024 * 2000 diff --git a/src/nvim/event/wstream.h b/src/nvim/event/wstream.h index 4cba7bde8f..a431f940d2 100644 --- a/src/nvim/event/wstream.h +++ b/src/nvim/event/wstream.h @@ -1,23 +1,7 @@ #pragma once -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <uv.h> - -#include "nvim/event/loop.h" -#include "nvim/event/stream.h" - -struct wbuffer; - -typedef struct wbuffer WBuffer; -typedef void (*wbuffer_data_finalizer)(void *data); - -struct wbuffer { - size_t size, refcount; - char *data; - wbuffer_data_finalizer cb; -}; +#include "nvim/event/defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/wstream.h.generated.h" diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index e369397047..74ad8e95a2 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -18,6 +18,7 @@ #include "nvim/arglist.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" @@ -25,6 +26,7 @@ #include "nvim/change.h" #include "nvim/channel.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/decoration.h" @@ -34,6 +36,7 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_cmds_defs.h" @@ -41,22 +44,25 @@ #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/help.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/input.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" @@ -64,11 +70,14 @@ #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/shell.h" #include "nvim/os/time.h" #include "nvim/path.h" @@ -77,6 +86,7 @@ #include "nvim/profile.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/state_defs.h" @@ -84,6 +94,7 @@ #include "nvim/terminal.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -284,12 +295,12 @@ void ex_align(exarg_T *eap) // to the right. if (has_tab) { while (new_indent > 0) { - (void)set_indent(new_indent, 0); + set_indent(new_indent, 0); if (linelen(NULL) <= width) { // Now try to move the line as much as possible to // the right. Stop when it moves too far. do { - (void)set_indent(++new_indent, 0); + set_indent(++new_indent, 0); } while (linelen(NULL) <= width); new_indent--; break; @@ -302,7 +313,7 @@ void ex_align(exarg_T *eap) if (new_indent < 0) { new_indent = 0; } - (void)set_indent(new_indent, 0); // set indent + set_indent(new_indent, 0); // set indent } changed_lines(curbuf, eap->line1, 0, eap->line2 + 1, 0, true); curwin->w_cursor = save_curpos; @@ -342,13 +353,13 @@ static int linelen(int *has_tab) static char *sortbuf1; static char *sortbuf2; -static int sort_lc; ///< sort using locale -static int sort_ic; ///< ignore case -static int sort_nr; ///< sort on number -static int sort_rx; ///< sort on regex instead of skipping it -static int sort_flt; ///< sort on floating number +static bool sort_lc; ///< sort using locale +static bool sort_ic; ///< ignore case +static bool sort_nr; ///< sort on number +static bool sort_rx; ///< sort on regex instead of skipping it +static bool sort_flt; ///< sort on floating number -static int sort_abort; ///< flag to indicate if sorting has been interrupted +static bool sort_abort; ///< flag to indicate if sorting has been interrupted /// Struct to store info to be sorted. typedef struct { @@ -395,18 +406,16 @@ static int sort_compare(const void *s1, const void *s2) // number. if (sort_nr) { if (l1.st_u.num.is_number != l2.st_u.num.is_number) { - result = l1.st_u.num.is_number - l2.st_u.num.is_number; + result = l1.st_u.num.is_number > l2.st_u.num.is_number ? 1 : -1; } else { result = l1.st_u.num.value == l2.st_u.num.value ? 0 - : l1.st_u.num.value > l2.st_u.num.value - ? 1 - : -1; + : l1.st_u.num.value > l2.st_u.num.value ? 1 : -1; } } else if (sort_flt) { result = l1.st_u.value_flt == l2.st_u.value_flt - ? 0 : l1.st_u.value_flt > l2.st_u.value_flt - ? 1 : -1; + ? 0 + : l1.st_u.value_flt > l2.st_u.value_flt ? 1 : -1; } else { // We need to copy one line into "sortbuf1", because there is no // guarantee that the first pointer becomes invalid when obtaining the @@ -451,7 +460,7 @@ void ex_sort(exarg_T *eap) regmatch.regprog = NULL; sorti_T *nrs = xmalloc(count * sizeof(sorti_T)); - sort_abort = sort_ic = sort_lc = sort_rx = sort_nr = sort_flt = 0; + sort_abort = sort_ic = sort_lc = sort_rx = sort_nr = sort_flt = false; size_t format_found = 0; bool change_occurred = false; // Buffer contents changed. @@ -465,10 +474,10 @@ void ex_sort(exarg_T *eap) } else if (*p == 'r') { sort_rx = true; } else if (*p == 'n') { - sort_nr = 1; + sort_nr = true; format_found++; } else if (*p == 'f') { - sort_flt = 1; + sort_flt = true; format_found++; } else if (*p == 'b') { sort_what = STR2NR_BIN + STR2NR_FORCE; @@ -522,7 +531,7 @@ void ex_sort(exarg_T *eap) // From here on "sort_nr" is used as a flag for any integer number // sorting. - sort_nr += sort_what; + sort_nr |= sort_what; // Make an array with all line numbers. This avoids having to copy all // the lines into allocated memory. @@ -619,7 +628,8 @@ void ex_sort(exarg_T *eap) goto sortend; } - bcount_t old_count = 0, new_count = 0; + bcount_t old_count = 0; + bcount_t new_count = 0; // Insert the lines in the sorted order below the last one. linenr_T lnum = eap->line2; @@ -1305,7 +1315,7 @@ void do_shell(char *cmd, int flags) // This ui_cursor_goto is required for when the '\n' resulted in a "delete line // 1" command to the terminal. ui_cursor_goto(msg_row, msg_col); - (void)call_shell(cmd, (ShellOpts)flags, NULL); + call_shell(cmd, (ShellOpts)flags, NULL); if (msg_silent == 0) { msg_didout = true; } @@ -1462,7 +1472,7 @@ void append_redir(char *const buf, const size_t buflen, const char *const opt, } } -void print_line_no_prefix(linenr_T lnum, int use_number, int list) +void print_line_no_prefix(linenr_T lnum, int use_number, bool list) { char numbuf[30]; @@ -1475,9 +1485,9 @@ void print_line_no_prefix(linenr_T lnum, int use_number, int list) } /// Print a text line. Also in silent mode ("ex -s"). -void print_line(linenr_T lnum, int use_number, int list) +void print_line(linenr_T lnum, int use_number, bool list) { - int save_silent = silent_mode; + bool save_silent = silent_mode; // apply :filter /pat/ if (message_filtered(ml_get(lnum))) { @@ -1486,7 +1496,7 @@ void print_line(linenr_T lnum, int use_number, int list) msg_start(); silent_mode = false; - info_message = true; // use os_msg(), not os_errmsg() + info_message = true; // use stdout, not stderr print_line_no_prefix(lnum, use_number, list); if (save_silent) { msg_putchar('\n'); @@ -1566,7 +1576,7 @@ void ex_file(exarg_T *eap) void ex_update(exarg_T *eap) { if (curbufIsChanged()) { - (void)do_write(eap); + do_write(eap); } } @@ -1582,7 +1592,7 @@ void ex_write(exarg_T *eap) if (eap->usefilter) { // input lines to shell command do_bang(1, eap, false, true, false); } else { - (void)do_write(eap); + do_write(eap); } } @@ -1605,7 +1615,7 @@ static int check_writable(const char *fname) /// @return FAIL for failure, OK otherwise. int do_write(exarg_T *eap) { - int other; + bool other; char *fname = NULL; // init to shut up gcc int retval = FAIL; char *free_fname = NULL; @@ -1724,7 +1734,7 @@ int do_write(exarg_T *eap) // If 'filetype' was empty try detecting it now. if (*curbuf->b_p_ft == NUL) { if (augroup_exists("filetypedetect")) { - (void)do_doautocmd("filetypedetect BufRead", true, NULL); + do_doautocmd("filetypedetect BufRead", true, NULL); } do_modelines(0); } @@ -1774,7 +1784,7 @@ theend: /// @param other writing under other name /// /// @return OK if it's OK, FAIL if it is not. -int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int other) +int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, bool other) { // Write to another file or b_flags set or not writing the whole file: // overwriting only allowed with '!' @@ -1815,8 +1825,6 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth // For ":w! filename" check that no swap file exists for "filename". if (other && !emsg_silent) { char *dir; - char *p; - char *swapname; // We only try the first entry in 'directory', without checking if // it's writable. If the "." directory is not writable the write @@ -1828,10 +1836,10 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth STRCPY(dir, "."); } else { dir = xmalloc(MAXPATHL); - p = p_dir; + char *p = p_dir; copy_option_part(&p, dir, MAXPATHL, ","); } - swapname = makeswapname(fname, ffname, curbuf, dir); + char *swapname = makeswapname(fname, ffname, curbuf, dir); xfree(dir); if (os_path_exists(swapname)) { if (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) { @@ -1998,11 +2006,11 @@ static int check_readonly(int *forceit, buf_T *buf) /// GETFILE_NOT_WRITTEN for "not written" error, /// GETFILE_SAME_FILE for success /// GETFILE_OPEN_OTHER for successfully opening another file. -int getfile(int fnum, char *ffname_arg, char *sfname_arg, int setpm, linenr_T lnum, int forceit) +int getfile(int fnum, char *ffname_arg, char *sfname_arg, bool setpm, linenr_T lnum, bool forceit) { char *ffname = ffname_arg; char *sfname = sfname_arg; - int other; + bool other; int retval; char *free_me = NULL; @@ -2179,7 +2187,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum | ((flags & ECMD_FORCEIT) ? CCGD_FORCEIT : 0) | (eap == NULL ? 0 : CCGD_EXCMD))) { if (fnum == 0 && other_file && ffname != NULL) { - (void)setaltfname(ffname, sfname, newlnum < 0 ? 0 : newlnum); + setaltfname(ffname, sfname, newlnum < 0 ? 0 : newlnum); } goto theend; } @@ -2270,7 +2278,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum // Existing memfile. oldbuf = true; set_bufref(&bufref, buf); - (void)buf_check_timestamp(buf); + buf_check_timestamp(buf); // Check if autocommands made buffer invalid or changed the current // buffer. if (!bufref_valid(&bufref) || curbuf != old_curbuf.br_buf) { @@ -2295,10 +2303,19 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum // If the current buffer was empty and has no file name, curbuf // is returned by buflist_new(), nothing to do here. if (buf != curbuf) { + // Should only be possible to get here if the cmdwin is closed, or + // if it's opening and its buffer hasn't been set yet (the new + // buffer is for it). + assert(cmdwin_buf == NULL); + const int save_cmdwin_type = cmdwin_type; + win_T *const save_cmdwin_win = cmdwin_win; + win_T *const save_cmdwin_old_curwin = cmdwin_old_curwin; // BufLeave applies to the old buffer. cmdwin_type = 0; + cmdwin_win = NULL; + cmdwin_old_curwin = NULL; // Be careful: The autocommands may delete any buffer and change // the current buffer. @@ -2314,7 +2331,11 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum const bufref_T save_au_new_curbuf = au_new_curbuf; set_bufref(&au_new_curbuf, buf); apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf); + cmdwin_type = save_cmdwin_type; + cmdwin_win = save_cmdwin_win; + cmdwin_old_curwin = save_cmdwin_old_curwin; + if (!bufref_valid(&au_new_curbuf)) { // New buffer has been deleted. delbuf_msg(new_name); // Frees new_name. @@ -2606,7 +2627,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum // If the window options were changed may need to set the spell language. // Can only do this after the buffer has been properly setup. if (did_get_winopts && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { - (void)parse_spelllang(curwin); + parse_spelllang(curwin); } if (command == NULL) { @@ -2669,7 +2690,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum } if (curbuf->b_kmap_state & KEYMAP_INIT) { - (void)keymap_init(); + keymap_init(); } RedrawingDisabled--; @@ -2722,7 +2743,7 @@ void ex_append(exarg_T *eap) linenr_T lnum = eap->line2; int indent = 0; char *p; - int empty = (curbuf->b_ml.ml_flags & ML_EMPTY); + bool empty = (curbuf->b_ml.ml_flags & ML_EMPTY); // the ! flag toggles autoindent if (eap->forceit) { @@ -2824,7 +2845,7 @@ void ex_append(exarg_T *eap) if (empty) { ml_delete(2, false); - empty = 0; + empty = false; } } State = MODE_NORMAL; @@ -3144,7 +3165,7 @@ static char *sub_grow_buf(char **new_start, int *new_start_len, int needed_len) // substitution into (and some extra space to avoid // too many calls to xmalloc()/free()). *new_start_len = needed_len + 50; - *new_start = xmalloc((size_t)(*new_start_len)); + *new_start = xcalloc(1, (size_t)(*new_start_len)); **new_start = NUL; new_end = *new_start; } else { @@ -3154,8 +3175,11 @@ static char *sub_grow_buf(char **new_start, int *new_start_len, int needed_len) size_t len = strlen(*new_start); needed_len += (int)len; if (needed_len > *new_start_len) { + size_t prev_new_start_len = (size_t)(*new_start_len); *new_start_len = needed_len + 50; + size_t added_len = (size_t)(*new_start_len) - prev_new_start_len; *new_start = xrealloc(*new_start, (size_t)(*new_start_len)); + memset(*new_start + prev_new_start_len, 0, added_len); } new_end = *new_start + len; } @@ -3261,7 +3285,7 @@ static int check_regexp_delim(int c) /// /// @param cmdpreview_ns The namespace to show 'inccommand' preview highlights. /// If <= 0, preview shouldn't be shown. -/// @return 0, 1 or 2. See show_cmdpreview() for more information on what the return value means. +/// @return 0, 1 or 2. See cmdpreview_may_show() for more information on the meaning. static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_ns, const handle_T cmdpreview_bufnr) { @@ -3301,7 +3325,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n .do_number = false, .do_ic = kSubHonorOptions }; - char *pat = NULL, *sub = NULL; // init for GCC + char *pat = NULL; + char *sub = NULL; // init for GCC int delimiter; bool has_second_delim = false; int sublen; @@ -3499,7 +3524,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n colnr_T copycol; colnr_T matchcol; colnr_T prev_matchcol = MAXCOL; - char *new_end, *new_start = NULL; + char *new_end; + char *new_start = NULL; int new_start_len = 0; char *p1; bool did_sub = false; @@ -3949,11 +3975,11 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n current_match.start.col = start_col; textlock++; - (void)vim_regsub_multi(®match, - sub_firstlnum - regmatch.startpos[0].lnum, - sub, new_end, sublen, - REGSUB_COPY | REGSUB_BACKSLASH - | (magic_isset() ? REGSUB_MAGIC : 0)); + vim_regsub_multi(®match, + sub_firstlnum - regmatch.startpos[0].lnum, + sub, new_end, sublen, + REGSUB_COPY | REGSUB_BACKSLASH + | (magic_isset() ? REGSUB_MAGIC : 0)); textlock--; sub_nsubs++; did_sub = true; @@ -3969,7 +3995,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n // TODO(bfredl): this has some robustness issues, look into later. bcount_t replaced_bytes = 0; - lpos_T start = regmatch.startpos[0], end = regmatch.endpos[0]; + lpos_T start = regmatch.startpos[0]; + lpos_T end = regmatch.endpos[0]; for (i = 0; i < nmatch - 1; i++) { replaced_bytes += (bcount_t)strlen(ml_get((linenr_T)(lnum_start + i))) + 1; } @@ -4262,7 +4289,7 @@ skip: // Show 'inccommand' preview if there are matched lines. if (cmdpreview_ns > 0 && !aborting()) { if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable. - set_string_option_direct("icm", -1, "", OPT_FREE, SID_NONE); + set_string_option_direct(kOptInccommand, "", 0, SID_NONE); } else if (*p_icm != NUL && pat != NULL) { if (pre_hl_id == 0) { pre_hl_id = syn_check_group(S_LEN("Substitute")); @@ -4541,8 +4568,8 @@ bool prepare_tagpreview(bool undo_sync) curwin->w_p_wfh = true; RESET_BINDING(curwin); // don't take over 'scrollbind' and 'cursorbind' curwin->w_p_diff = false; // no 'diff' - set_string_option_direct("fdc", -1, // no 'foldcolumn' - "0", OPT_FREE, SID_NONE); + + set_string_option_direct(kOptFoldcolumn, "0", 0, SID_NONE); // no 'foldcolumn' return true; } @@ -4561,10 +4588,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i buf_T *cmdpreview_buf = NULL; // disable file info message - set_string_option_direct("shm", -1, "F", OPT_FREE, SID_NONE); - - // Update the topline to ensure that main window is on the correct line - update_topline(curwin); + set_string_option_direct(kOptShortmess, "F", 0, SID_NONE); // Place cursor on nearest matching line, to undo do_sub() cursor placement. for (size_t i = 0; i < lines.subresults.size; i++) { @@ -4576,8 +4600,10 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i } // Else: All matches are above, do_sub() already placed cursor. } + // Update the topline to ensure that main window is on the correct line + update_topline(curwin); + // Width of the "| lnum|..." column which displays the line numbers. - linenr_T highest_num_line = 0; int col_width = 0; // Use preview window only when inccommand=split and range is not just the current line bool preview = (*p_icm == 's') && (eap->line1 != old_cusr.lnum || eap->line2 != old_cusr.lnum); @@ -4587,8 +4613,11 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i assert(cmdpreview_buf != NULL); if (lines.subresults.size > 0) { - highest_num_line = kv_last(lines.subresults).end.lnum; - col_width = (int)log10(highest_num_line) + 1 + 3; + SubResult last_match = kv_last(lines.subresults); + // `last_match.end.lnum` may be 0 when using 'n' flag. + linenr_T highest_lnum = MAX(last_match.start.lnum, last_match.end.lnum); + assert(highest_lnum > 0); + col_width = (int)log10(highest_lnum) + 1 + 3; } } @@ -4645,7 +4674,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i snprintf(str, line_size, "|%*" PRIdLINENR "| %s", col_width - 3, next_linenr, line); if (linenr_preview == 0) { - ml_replace_buf(cmdpreview_buf, 1, str, true); + ml_replace_buf(cmdpreview_buf, 1, str, true, false); } else { ml_append_buf(cmdpreview_buf, linenr_preview, str, (colnr_T)line_size, false); } @@ -4660,7 +4689,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i xfree(str); - set_string_option_direct("shm", -1, save_shm_p, OPT_FREE, SID_NONE); + set_string_option_direct(kOptShortmess, save_shm_p, 0, SID_NONE); xfree(save_shm_p); return preview ? 2 : 1; @@ -4669,7 +4698,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i /// :substitute command. void ex_substitute(exarg_T *eap) { - (void)do_sub(eap, profile_zero(), 0, 0); + do_sub(eap, profile_zero(), 0, 0); } /// :substitute command preview callback. diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index de13f03197..e0894cf7a8 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -1,34 +1,24 @@ #pragma once -#include <stdbool.h> +#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep -#include "nvim/buffer_defs.h" // IWYU pragma: keep -#include "nvim/eval/typval_defs.h" -#include "nvim/ex_cmds_defs.h" // IWYU pragma: export -#include "nvim/os/time.h" -#include "nvim/pos_defs.h" // IWYU pragma: keep +/// flags for do_ecmd() +enum { + ECMD_HIDE = 0x01, ///< don't free the current buffer + ECMD_SET_HELP = 0x02, ///< set b_help flag of (new) buffer before opening file + ECMD_OLDBUF = 0x04, ///< use existing buffer if it exists + ECMD_FORCEIT = 0x08, ///< ! used in Ex command + ECMD_ADDBUF = 0x10, ///< don't edit, just add to buffer list + ECMD_ALTBUF = 0x20, ///< like ECMD_ADDBUF and set the alternate file + ECMD_NOWINENTER = 0x40, ///< do not trigger BufWinEnter +}; -// flags for do_ecmd() -#define ECMD_HIDE 0x01 // don't free the current buffer -#define ECMD_SET_HELP 0x02 // set b_help flag of (new) buffer before - // opening file -#define ECMD_OLDBUF 0x04 // use existing buffer if it exists -#define ECMD_FORCEIT 0x08 // ! used in Ex command -#define ECMD_ADDBUF 0x10 // don't edit, just add to buffer list -#define ECMD_ALTBUF 0x20 // like ECMD_ADDBUF and set the alternate file -#define ECMD_NOWINENTER 0x40 // do not trigger BufWinEnter - -// for lnum argument in do_ecmd() -#define ECMD_LASTL 0 // use last position in loaded file -#define ECMD_LAST (-1) // use last position in all files -#define ECMD_ONE 1 // use first line - -/// Previous :substitute replacement string definition -typedef struct { - char *sub; ///< Previous replacement string. - Timestamp timestamp; ///< Time when it was last set. - list_T *additional_elements; ///< Additional data left from ShaDa file. -} SubReplacementString; +/// for lnum argument in do_ecmd() +enum { + ECMD_LASTL = 0, ///< use last position in loaded file + ECMD_LAST = -1, ///< use last position in all files + ECMD_ONE = 1, ///< use first line +}; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds.h.generated.h" diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 4859a70553..1318eda5eb 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -4,3358 +4,3358 @@ local module = {} -- Description of the values below is contained in ex_cmds_defs.h file. -- "EX_" prefix is omitted. -local RANGE = 0x001 -local BANG = 0x002 -local EXTRA = 0x004 -local XFILE = 0x008 -local NOSPC = 0x010 -local DFLALL = 0x020 -local WHOLEFOLD = 0x040 -local NEEDARG = 0x080 -local TRLBAR = 0x100 -local REGSTR = 0x200 -local COUNT = 0x400 -local NOTRLCOM = 0x800 -local ZEROR = 0x1000 -local CTRLV = 0x2000 -local CMDARG = 0x4000 -local BUFNAME = 0x8000 -local BUFUNL = 0x10000 -local ARGOPT = 0x20000 -local SBOXOK = 0x40000 -local CMDWIN = 0x80000 -local MODIFY = 0x100000 -local FLAGS = 0x200000 -local LOCK_OK = 0x1000000 -local PREVIEW = 0x8000000 -local FILES = bit.bor(XFILE, EXTRA) -local WORD1 = bit.bor(EXTRA, NOSPC) -local FILE1 = bit.bor(FILES, NOSPC) +local RANGE = 0x001 +local BANG = 0x002 +local EXTRA = 0x004 +local XFILE = 0x008 +local NOSPC = 0x010 +local DFLALL = 0x020 +local WHOLEFOLD = 0x040 +local NEEDARG = 0x080 +local TRLBAR = 0x100 +local REGSTR = 0x200 +local COUNT = 0x400 +local NOTRLCOM = 0x800 +local ZEROR = 0x1000 +local CTRLV = 0x2000 +local CMDARG = 0x4000 +local BUFNAME = 0x8000 +local BUFUNL = 0x10000 +local ARGOPT = 0x20000 +local SBOXOK = 0x40000 +local CMDWIN = 0x80000 +local MODIFY = 0x100000 +local FLAGS = 0x200000 +local LOCK_OK = 0x1000000 +local PREVIEW = 0x8000000 +local FILES = bit.bor(XFILE, EXTRA) +local WORD1 = bit.bor(EXTRA, NOSPC) +local FILE1 = bit.bor(FILES, NOSPC) module.flags = { RANGE = RANGE, DFLALL = DFLALL, - PREVIEW = PREVIEW + PREVIEW = PREVIEW, } -- The following table is described in ex_cmds_defs.h file. module.cmds = { { - command='append', - flags=bit.bor(BANG, RANGE, ZEROR, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_append', + command = 'append', + flags = bit.bor(BANG, RANGE, ZEROR, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_append', }, { - command='abbreviate', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abbreviate', + command = 'abbreviate', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abbreviate', }, { - command='abclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abclear', + command = 'abclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abclear', }, { - command='aboveleft', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'aboveleft', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='all', - flags=bit.bor(BANG, RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_all', + command = 'all', + flags = bit.bor(BANG, RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_all', }, { - command='amenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'amenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='anoremenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'anoremenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='args', - flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_args', + command = 'args', + flags = bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_args', }, { - command='argadd', - flags=bit.bor(BANG, RANGE, ZEROR, FILES, TRLBAR), - addr_type='ADDR_ARGUMENTS', - func='ex_argadd', + command = 'argadd', + flags = bit.bor(BANG, RANGE, ZEROR, FILES, TRLBAR), + addr_type = 'ADDR_ARGUMENTS', + func = 'ex_argadd', }, { - command='argdelete', - flags=bit.bor(BANG, RANGE, FILES, TRLBAR), - addr_type='ADDR_ARGUMENTS', - func='ex_argdelete', + command = 'argdelete', + flags = bit.bor(BANG, RANGE, FILES, TRLBAR), + addr_type = 'ADDR_ARGUMENTS', + func = 'ex_argdelete', }, { - command='argdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), - addr_type='ADDR_ARGUMENTS', - func='ex_listdo', + command = 'argdo', + flags = bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type = 'ADDR_ARGUMENTS', + func = 'ex_listdo', }, { - command='argdedupe', - flags=TRLBAR, - addr_type='ADDR_NONE', - func='ex_argdedupe', + command = 'argdedupe', + flags = TRLBAR, + addr_type = 'ADDR_NONE', + func = 'ex_argdedupe', }, { - command='argedit', - flags=bit.bor(BANG, NEEDARG, RANGE, ZEROR, FILES, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_ARGUMENTS', - func='ex_argedit', + command = 'argedit', + flags = bit.bor(BANG, NEEDARG, RANGE, ZEROR, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_ARGUMENTS', + func = 'ex_argedit', }, { - command='argglobal', - flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_args', + command = 'argglobal', + flags = bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_args', }, { - command='arglocal', - flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_args', + command = 'arglocal', + flags = bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_args', }, { - command='argument', - flags=bit.bor(BANG, RANGE, COUNT, EXTRA, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_ARGUMENTS', - func='ex_argument', + command = 'argument', + flags = bit.bor(BANG, RANGE, COUNT, EXTRA, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_ARGUMENTS', + func = 'ex_argument', }, { - command='ascii', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='do_ascii', + command = 'ascii', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'do_ascii', }, { - command='autocmd', - flags=bit.bor(BANG, EXTRA, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_autocmd', + command = 'autocmd', + flags = bit.bor(BANG, EXTRA, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_autocmd', }, { - command='augroup', - flags=bit.bor(BANG, WORD1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_autocmd', + command = 'augroup', + flags = bit.bor(BANG, WORD1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_autocmd', }, { - command='aunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'aunmenu', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='buffer', - flags=bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR), - addr_type='ADDR_BUFFERS', - func='ex_buffer', + command = 'buffer', + flags = bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR), + addr_type = 'ADDR_BUFFERS', + func = 'ex_buffer', }, { - command='bNext', - flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_bprevious', + command = 'bNext', + flags = bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_bprevious', }, { - command='ball', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_buffer_all', + command = 'ball', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_buffer_all', }, { - command='badd', - flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_edit', + command = 'badd', + flags = bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_edit', }, { - command='balt', - flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_edit', + command = 'balt', + flags = bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_edit', }, { - command='bdelete', - flags=bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), - addr_type='ADDR_BUFFERS', - func='ex_bunload', + command = 'bdelete', + flags = bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), + addr_type = 'ADDR_BUFFERS', + func = 'ex_bunload', }, { - command='belowright', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'belowright', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='bfirst', - flags=bit.bor(BANG, RANGE, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_brewind', + command = 'bfirst', + flags = bit.bor(BANG, RANGE, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_brewind', }, { - command='blast', - flags=bit.bor(BANG, RANGE, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_blast', + command = 'blast', + flags = bit.bor(BANG, RANGE, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_blast', }, { - command='bmodified', - flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_bmodified', + command = 'bmodified', + flags = bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_bmodified', }, { - command='bnext', - flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_bnext', + command = 'bnext', + flags = bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_bnext', }, { - command='botright', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'botright', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='bprevious', - flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_bprevious', + command = 'bprevious', + flags = bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_bprevious', }, { - command='brewind', - flags=bit.bor(BANG, RANGE, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_brewind', + command = 'brewind', + flags = bit.bor(BANG, RANGE, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_brewind', }, { - command='break', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_break', + command = 'break', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_break', }, { - command='breakadd', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_breakadd', + command = 'breakadd', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_breakadd', }, { - command='breakdel', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_breakdel', + command = 'breakdel', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_breakdel', }, { - command='breaklist', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_breaklist', + command = 'breaklist', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_breaklist', }, { - command='browse', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'browse', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='buffers', - flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='buflist_list', + command = 'buffers', + flags = bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'buflist_list', }, { - command='bufdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), - addr_type='ADDR_BUFFERS', - func='ex_listdo', + command = 'bufdo', + flags = bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type = 'ADDR_BUFFERS', + func = 'ex_listdo', }, { - command='bunload', - flags=bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), - addr_type='ADDR_LOADED_BUFFERS', - func='ex_bunload', + command = 'bunload', + flags = bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), + addr_type = 'ADDR_LOADED_BUFFERS', + func = 'ex_bunload', }, { - command='bwipeout', - flags=bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, TRLBAR), - addr_type='ADDR_BUFFERS', - func='ex_bunload', + command = 'bwipeout', + flags = bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, TRLBAR), + addr_type = 'ADDR_BUFFERS', + func = 'ex_bunload', }, { - command='change', - flags=bit.bor(BANG, WHOLEFOLD, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_change', + command = 'change', + flags = bit.bor(BANG, WHOLEFOLD, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_change', }, { - command='cNext', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'cNext', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='cNfile', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'cNfile', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='cabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abbreviate', + command = 'cabbrev', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abbreviate', }, { - command='cabclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abclear', + command = 'cabclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abclear', }, { - command='cabove', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='ex_cbelow', + command = 'cabove', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cbelow', }, { - command='caddbuffer', - flags=bit.bor(RANGE, WORD1, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_cbuffer', + command = 'caddbuffer', + flags = bit.bor(RANGE, WORD1, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_cbuffer', }, { - command='caddexpr', - flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_cexpr', + command = 'caddexpr', + flags = bit.bor(NEEDARG, WORD1, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_cexpr', }, { - command='caddfile', - flags=bit.bor(TRLBAR, FILE1), - addr_type='ADDR_NONE', - func='ex_cfile', + command = 'caddfile', + flags = bit.bor(TRLBAR, FILE1), + addr_type = 'ADDR_NONE', + func = 'ex_cfile', }, { - command='cafter', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='ex_cbelow', + command = 'cafter', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cbelow', }, { - command='call', - flags=bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_call', + command = 'call', + flags = bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_call', }, { - command='catch', - flags=bit.bor(EXTRA, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_catch', + command = 'catch', + flags = bit.bor(EXTRA, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_catch', }, { - command='cbuffer', - flags=bit.bor(BANG, RANGE, WORD1, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_cbuffer', + command = 'cbuffer', + flags = bit.bor(BANG, RANGE, WORD1, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_cbuffer', }, { - command='cbefore', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='ex_cbelow', + command = 'cbefore', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cbelow', }, { - command='cbelow', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='ex_cbelow', + command = 'cbelow', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cbelow', }, { - command='cbottom', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_cbottom', + command = 'cbottom', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_cbottom', }, { - command='cc', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_QUICKFIX', - func='ex_cc', + command = 'cc', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_QUICKFIX', + func = 'ex_cc', }, { - command='cclose', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_cclose', + command = 'cclose', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_cclose', }, { - command='cd', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_cd', + command = 'cd', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_cd', }, { - command='cdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), - addr_type='ADDR_QUICKFIX_VALID', - func='ex_listdo', + command = 'cdo', + flags = bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type = 'ADDR_QUICKFIX_VALID', + func = 'ex_listdo', }, { - command='center', - flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_align', + command = 'center', + flags = bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_align', }, { - command='cexpr', - flags=bit.bor(NEEDARG, WORD1, NOTRLCOM, BANG), - addr_type='ADDR_NONE', - func='ex_cexpr', + command = 'cexpr', + flags = bit.bor(NEEDARG, WORD1, NOTRLCOM, BANG), + addr_type = 'ADDR_NONE', + func = 'ex_cexpr', }, { - command='cfile', - flags=bit.bor(TRLBAR, FILE1, BANG), - addr_type='ADDR_NONE', - func='ex_cfile', + command = 'cfile', + flags = bit.bor(TRLBAR, FILE1, BANG), + addr_type = 'ADDR_NONE', + func = 'ex_cfile', }, -- Even though 'cfdo' is alphabetically lower than 'cfile', it is after -- 'cfile' in this cmd list to support the existing ":cf" abbreviation. { - command='cfdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), - addr_type='ADDR_QUICKFIX_VALID', - func='ex_listdo', + command = 'cfdo', + flags = bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type = 'ADDR_QUICKFIX_VALID', + func = 'ex_listdo', }, { - command='cfirst', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cc', + command = 'cfirst', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cc', }, { - command='cgetfile', - flags=bit.bor(TRLBAR, FILE1), - addr_type='ADDR_NONE', - func='ex_cfile', + command = 'cgetfile', + flags = bit.bor(TRLBAR, FILE1), + addr_type = 'ADDR_NONE', + func = 'ex_cfile', }, { - command='cgetbuffer', - flags=bit.bor(RANGE, WORD1, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_cbuffer', + command = 'cgetbuffer', + flags = bit.bor(RANGE, WORD1, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_cbuffer', }, { - command='cgetexpr', - flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_cexpr', + command = 'cgetexpr', + flags = bit.bor(NEEDARG, WORD1, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_cexpr', }, { - command='chdir', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_cd', + command = 'chdir', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_cd', }, { - command='changes', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_changes', + command = 'changes', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_changes', }, { - command='checkhealth', - flags=bit.bor(EXTRA, TRLBAR), - addr_type='ADDR_NONE', - func='ex_checkhealth', + command = 'checkhealth', + flags = bit.bor(EXTRA, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_checkhealth', }, { - command='checkpath', - flags=bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_checkpath', + command = 'checkpath', + flags = bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_checkpath', }, { - command='checktime', - flags=bit.bor(RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_checktime', + command = 'checktime', + flags = bit.bor(RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_checktime', }, { - command='chistory', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='qf_history', + command = 'chistory', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'qf_history', }, { - command='clist', - flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='qf_list', + command = 'clist', + flags = bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'qf_list', }, { - command='clast', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cc', + command = 'clast', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cc', }, { - command='close', - flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_WINDOWS', - func='ex_close', + command = 'close', + flags = bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_WINDOWS', + func = 'ex_close', }, { - command='clearjumps', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_clearjumps', + command = 'clearjumps', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_clearjumps', }, { - command='cmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'cmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='cmapclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'cmapclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='cmenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'cmenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='cnext', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'cnext', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='cnewer', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='qf_age', + command = 'cnewer', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'qf_age', }, { - command='cnfile', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'cnfile', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='cnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'cnoremap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='cnoreabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abbreviate', + command = 'cnoreabbrev', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abbreviate', }, { - command='cnoremenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'cnoremenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='copy', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_copymove', + command = 'copy', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_copymove', }, { - command='colder', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='qf_age', + command = 'colder', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'qf_age', }, { - command='colorscheme', - flags=bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_colorscheme', + command = 'colorscheme', + flags = bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_colorscheme', }, { - command='command', - flags=bit.bor(EXTRA, BANG, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_command', + command = 'command', + flags = bit.bor(EXTRA, BANG, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_command', }, { - command='comclear', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_comclear', + command = 'comclear', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_comclear', }, { - command='compiler', - flags=bit.bor(BANG, TRLBAR, WORD1, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_compiler', + command = 'compiler', + flags = bit.bor(BANG, TRLBAR, WORD1, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_compiler', }, { - command='continue', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_continue', + command = 'continue', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_continue', }, { - command='confirm', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'confirm', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='const', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_let', + command = 'const', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_let', }, { - command='copen', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_copen', + command = 'copen', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_copen', }, { - command='cprevious', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'cprevious', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='cpfile', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_OTHER', - func='ex_cnext', + command = 'cpfile', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_OTHER', + func = 'ex_cnext', }, { - command='cquit', - flags=bit.bor(RANGE, COUNT, ZEROR, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cquit', + command = 'cquit', + flags = bit.bor(RANGE, COUNT, ZEROR, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cquit', }, { - command='crewind', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cc', + command = 'crewind', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cc', }, { - command='cunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'cunmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='cunabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abbreviate', + command = 'cunabbrev', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abbreviate', }, { - command='cunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'cunmenu', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='cwindow', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_cwindow', + command = 'cwindow', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_cwindow', }, { - command='delete', - flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_operators', + command = 'delete', + flags = bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_operators', }, { - command='delmarks', - flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_delmarks', + command = 'delmarks', + flags = bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_delmarks', }, { - command='debug', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_debug', + command = 'debug', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_debug', }, { - command='debuggreedy', - flags=bit.bor(RANGE, ZEROR, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_debuggreedy', + command = 'debuggreedy', + flags = bit.bor(RANGE, ZEROR, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_debuggreedy', }, { - command='defer', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_call', + command = 'defer', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_call', }, { - command='delcommand', - flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_delcommand', + command = 'delcommand', + flags = bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_delcommand', }, { - command='delfunction', - flags=bit.bor(BANG, NEEDARG, WORD1, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_delfunction', + command = 'delfunction', + flags = bit.bor(BANG, NEEDARG, WORD1, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_delfunction', }, { - command='display', - flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_display', + command = 'display', + flags = bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_display', }, { - command='diffupdate', - flags=bit.bor(BANG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_diffupdate', + command = 'diffupdate', + flags = bit.bor(BANG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_diffupdate', }, { - command='diffget', - flags=bit.bor(RANGE, EXTRA, TRLBAR, MODIFY), - addr_type='ADDR_LINES', - func='ex_diffgetput', + command = 'diffget', + flags = bit.bor(RANGE, EXTRA, TRLBAR, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_diffgetput', }, { - command='diffoff', - flags=bit.bor(BANG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_diffoff', + command = 'diffoff', + flags = bit.bor(BANG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_diffoff', }, { - command='diffpatch', - flags=bit.bor(EXTRA, FILE1, TRLBAR, MODIFY), - addr_type='ADDR_NONE', - func='ex_diffpatch', + command = 'diffpatch', + flags = bit.bor(EXTRA, FILE1, TRLBAR, MODIFY), + addr_type = 'ADDR_NONE', + func = 'ex_diffpatch', }, { - command='diffput', - flags=bit.bor(RANGE, EXTRA, TRLBAR), - addr_type='ADDR_LINES', - func='ex_diffgetput', + command = 'diffput', + flags = bit.bor(RANGE, EXTRA, TRLBAR), + addr_type = 'ADDR_LINES', + func = 'ex_diffgetput', }, { - command='diffsplit', - flags=bit.bor(EXTRA, FILE1, TRLBAR), - addr_type='ADDR_NONE', - func='ex_diffsplit', + command = 'diffsplit', + flags = bit.bor(EXTRA, FILE1, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_diffsplit', }, { - command='diffthis', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_diffthis', + command = 'diffthis', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_diffthis', }, { - command='digraphs', - flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_digraphs', + command = 'digraphs', + flags = bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_digraphs', }, { - command='djump', - flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type='ADDR_LINES', - func='ex_findpat', + command = 'djump', + flags = bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), + addr_type = 'ADDR_LINES', + func = 'ex_findpat', }, { - command='dlist', - flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_findpat', + command = 'dlist', + flags = bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_findpat', }, { - command='doautocmd', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_doautocmd', + command = 'doautocmd', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_doautocmd', }, { - command='doautoall', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_doautoall', + command = 'doautoall', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_doautoall', }, { - command='drop', - flags=bit.bor(FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_drop', + command = 'drop', + flags = bit.bor(FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_drop', }, { - command='dsearch', - flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_findpat', + command = 'dsearch', + flags = bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_findpat', }, { - command='dsplit', - flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type='ADDR_LINES', - func='ex_findpat', + command = 'dsplit', + flags = bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), + addr_type = 'ADDR_LINES', + func = 'ex_findpat', }, { - command='edit', - flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_edit', + command = 'edit', + flags = bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_edit', }, { - command='earlier', - flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_later', + command = 'earlier', + flags = bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_later', }, { - command='echo', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_echo', + command = 'echo', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_echo', }, { - command='echoerr', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_execute', + command = 'echoerr', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_execute', }, { - command='echohl', - flags=bit.bor(EXTRA, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_echohl', + command = 'echohl', + flags = bit.bor(EXTRA, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_echohl', }, { - command='echomsg', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_execute', + command = 'echomsg', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_execute', }, { - command='echon', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_echo', + command = 'echon', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_echo', }, { - command='else', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_else', + command = 'else', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_else', }, { - command='elseif', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_else', + command = 'elseif', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_else', }, { - command='emenu', - flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_emenu', + command = 'emenu', + flags = bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_emenu', }, { - command='endif', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_endif', + command = 'endif', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_endif', }, { - command='endfunction', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_endfunction', + command = 'endfunction', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_endfunction', }, { - command='endfor', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_endwhile', + command = 'endfor', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_endwhile', }, { - command='endtry', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_endtry', + command = 'endtry', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_endtry', }, { - command='endwhile', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_endwhile', + command = 'endwhile', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_endwhile', }, { - command='enew', - flags=bit.bor(BANG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_edit', + command = 'enew', + flags = bit.bor(BANG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_edit', }, { - command='eval', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_eval', + command = 'eval', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_eval', }, { - command='ex', - flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_edit', + command = 'ex', + flags = bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_edit', }, { - command='execute', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_execute', + command = 'execute', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_execute', }, { - command='exit', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_exit', + command = 'exit', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_exit', }, { - command='exusage', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_exusage', + command = 'exusage', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_exusage', }, { - command='file', - flags=bit.bor(RANGE, ZEROR, BANG, FILE1, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_file', + command = 'file', + flags = bit.bor(RANGE, ZEROR, BANG, FILE1, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_file', }, { - command='files', - flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='buflist_list', + command = 'files', + flags = bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'buflist_list', }, { - command='filetype', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_filetype', + command = 'filetype', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_filetype', }, { - command='filter', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'filter', + flags = bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='find', - flags=bit.bor(RANGE, BANG, FILE1, CMDARG, ARGOPT, TRLBAR, NEEDARG), - addr_type='ADDR_OTHER', - func='ex_find', + command = 'find', + flags = bit.bor(RANGE, BANG, FILE1, CMDARG, ARGOPT, TRLBAR, NEEDARG), + addr_type = 'ADDR_OTHER', + func = 'ex_find', }, { - command='finally', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_finally', + command = 'finally', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_finally', }, { - command='finish', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_finish', + command = 'finish', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_finish', }, { - command='first', - flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_rewind', + command = 'first', + flags = bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_rewind', }, { - command='fold', - flags=bit.bor(RANGE, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_fold', + command = 'fold', + flags = bit.bor(RANGE, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_fold', }, { - command='foldclose', - flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_foldopen', + command = 'foldclose', + flags = bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_foldopen', }, { - command='folddoopen', - flags=bit.bor(RANGE, DFLALL, NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_LINES', - func='ex_folddo', + command = 'folddoopen', + flags = bit.bor(RANGE, DFLALL, NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_LINES', + func = 'ex_folddo', }, { - command='folddoclosed', - flags=bit.bor(RANGE, DFLALL, NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_LINES', - func='ex_folddo', + command = 'folddoclosed', + flags = bit.bor(RANGE, DFLALL, NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_LINES', + func = 'ex_folddo', }, { - command='foldopen', - flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_foldopen', + command = 'foldopen', + flags = bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_foldopen', }, { - command='for', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_while', + command = 'for', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_while', }, { - command='function', - flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_function', + command = 'function', + flags = bit.bor(EXTRA, BANG, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_function', }, { - command='fclose', - flags=bit.bor(BANG, RANGE), - addr_type='ADDR_OTHER', - func='ex_fclose', + command = 'fclose', + flags = bit.bor(BANG, RANGE), + addr_type = 'ADDR_OTHER', + func = 'ex_fclose', }, { - command='global', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, DFLALL, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_global', + command = 'global', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, DFLALL, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_global', }, { - command='goto', - flags=bit.bor(RANGE, COUNT, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_goto', + command = 'goto', + flags = bit.bor(RANGE, COUNT, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_goto', }, { - command='grep', - flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type='ADDR_OTHER', - func='ex_make', + command = 'grep', + flags = bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type = 'ADDR_OTHER', + func = 'ex_make', }, { - command='grepadd', - flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type='ADDR_OTHER', - func='ex_make', + command = 'grepadd', + flags = bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type = 'ADDR_OTHER', + func = 'ex_make', }, { - command='gui', - flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_nogui', + command = 'gui', + flags = bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_nogui', }, { - command='gvim', - flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_nogui', + command = 'gvim', + flags = bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_nogui', }, { - command='help', - flags=bit.bor(BANG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_help', + command = 'help', + flags = bit.bor(BANG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_help', }, { - command='helpclose', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_helpclose', + command = 'helpclose', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_helpclose', }, { - command='helpgrep', - flags=bit.bor(EXTRA, NOTRLCOM, NEEDARG), - addr_type='ADDR_NONE', - func='ex_helpgrep', + command = 'helpgrep', + flags = bit.bor(EXTRA, NOTRLCOM, NEEDARG), + addr_type = 'ADDR_NONE', + func = 'ex_helpgrep', }, { - command='helptags', - flags=bit.bor(NEEDARG, FILES, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_helptags', + command = 'helptags', + flags = bit.bor(NEEDARG, FILES, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_helptags', }, { - command='highlight', - flags=bit.bor(BANG, EXTRA, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_highlight', + command = 'highlight', + flags = bit.bor(BANG, EXTRA, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_highlight', }, { - command='hide', - flags=bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR), - addr_type='ADDR_WINDOWS', - func='ex_hide', + command = 'hide', + flags = bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR), + addr_type = 'ADDR_WINDOWS', + func = 'ex_hide', }, { - command='history', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_history', + command = 'history', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_history', }, { - command='horizontal', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'horizontal', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='insert', - flags=bit.bor(BANG, RANGE, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_append', + command = 'insert', + flags = bit.bor(BANG, RANGE, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_append', }, { - command='iabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abbreviate', + command = 'iabbrev', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abbreviate', }, { - command='iabclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abclear', + command = 'iabclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abclear', }, { - command='if', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_if', + command = 'if', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_if', }, { - command='ijump', - flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type='ADDR_LINES', - func='ex_findpat', + command = 'ijump', + flags = bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), + addr_type = 'ADDR_LINES', + func = 'ex_findpat', }, { - command='ilist', - flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_findpat', + command = 'ilist', + flags = bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_findpat', }, { - command='imap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'imap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='imapclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'imapclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='imenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'imenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='inoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'inoremap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='inoreabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abbreviate', + command = 'inoreabbrev', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abbreviate', }, { - command='inoremenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'inoremenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='intro', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_intro', + command = 'intro', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_intro', }, { - command='isearch', - flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_findpat', + command = 'isearch', + flags = bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_findpat', }, { - command='isplit', - flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type='ADDR_LINES', - func='ex_findpat', + command = 'isplit', + flags = bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), + addr_type = 'ADDR_LINES', + func = 'ex_findpat', }, { - command='iunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'iunmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='iunabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abbreviate', + command = 'iunabbrev', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abbreviate', }, { - command='iunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'iunmenu', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='join', - flags=bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_join', + command = 'join', + flags = bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_join', }, { - command='jumps', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_jumps', + command = 'jumps', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_jumps', }, { - command='k', - flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_mark', + command = 'k', + flags = bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_mark', }, { - command='keepmarks', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'keepmarks', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='keepjumps', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'keepjumps', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='keeppatterns', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'keeppatterns', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='keepalt', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'keepalt', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='list', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_print', + command = 'list', + flags = bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_print', }, { - command='lNext', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'lNext', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='lNfile', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'lNfile', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='last', - flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_last', + command = 'last', + flags = bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_last', }, { - command='labove', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='ex_cbelow', + command = 'labove', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cbelow', }, { - command='language', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_language', + command = 'language', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_language', }, { - command='laddexpr', - flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_cexpr', + command = 'laddexpr', + flags = bit.bor(NEEDARG, WORD1, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_cexpr', }, { - command='laddbuffer', - flags=bit.bor(RANGE, WORD1, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_cbuffer', + command = 'laddbuffer', + flags = bit.bor(RANGE, WORD1, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_cbuffer', }, { - command='laddfile', - flags=bit.bor(TRLBAR, FILE1), - addr_type='ADDR_NONE', - func='ex_cfile', + command = 'laddfile', + flags = bit.bor(TRLBAR, FILE1), + addr_type = 'ADDR_NONE', + func = 'ex_cfile', }, { - command='lafter', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='ex_cbelow', + command = 'lafter', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cbelow', }, { - command='later', - flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_later', + command = 'later', + flags = bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_later', }, { - command='lbuffer', - flags=bit.bor(BANG, RANGE, WORD1, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_cbuffer', + command = 'lbuffer', + flags = bit.bor(BANG, RANGE, WORD1, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_cbuffer', }, { - command='lbefore', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='ex_cbelow', + command = 'lbefore', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cbelow', }, { - command='lbelow', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='ex_cbelow', + command = 'lbelow', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cbelow', }, { - command='lbottom', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_cbottom', + command = 'lbottom', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_cbottom', }, { - command='lcd', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_cd', + command = 'lcd', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_cd', }, { - command='lchdir', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_cd', + command = 'lchdir', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_cd', }, { - command='lclose', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_cclose', + command = 'lclose', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_cclose', }, { - command='ldo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), - addr_type='ADDR_QUICKFIX_VALID', - func='ex_listdo', + command = 'ldo', + flags = bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type = 'ADDR_QUICKFIX_VALID', + func = 'ex_listdo', }, { - command='left', - flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_align', + command = 'left', + flags = bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_align', }, { - command='leftabove', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'leftabove', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='let', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_let', + command = 'let', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_let', }, { - command='lexpr', - flags=bit.bor(NEEDARG, WORD1, NOTRLCOM, BANG), - addr_type='ADDR_NONE', - func='ex_cexpr', + command = 'lexpr', + flags = bit.bor(NEEDARG, WORD1, NOTRLCOM, BANG), + addr_type = 'ADDR_NONE', + func = 'ex_cexpr', }, { - command='lfile', - flags=bit.bor(TRLBAR, FILE1, BANG), - addr_type='ADDR_NONE', - func='ex_cfile', + command = 'lfile', + flags = bit.bor(TRLBAR, FILE1, BANG), + addr_type = 'ADDR_NONE', + func = 'ex_cfile', }, -- Even though 'lfdo' is alphabetically lower than 'lfile', it is after -- 'lfile' in this cmd list to support the existing ":lf" abbreviation. { - command='lfdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), - addr_type='ADDR_QUICKFIX_VALID', - func='ex_listdo', + command = 'lfdo', + flags = bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type = 'ADDR_QUICKFIX_VALID', + func = 'ex_listdo', }, { - command='lfirst', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cc', + command = 'lfirst', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cc', }, { - command='lgetfile', - flags=bit.bor(TRLBAR, FILE1), - addr_type='ADDR_NONE', - func='ex_cfile', + command = 'lgetfile', + flags = bit.bor(TRLBAR, FILE1), + addr_type = 'ADDR_NONE', + func = 'ex_cfile', }, { - command='lgetbuffer', - flags=bit.bor(RANGE, WORD1, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_cbuffer', + command = 'lgetbuffer', + flags = bit.bor(RANGE, WORD1, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_cbuffer', }, { - command='lgetexpr', - flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_cexpr', + command = 'lgetexpr', + flags = bit.bor(NEEDARG, WORD1, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_cexpr', }, { - command='lgrep', - flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type='ADDR_OTHER', - func='ex_make', + command = 'lgrep', + flags = bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type = 'ADDR_OTHER', + func = 'ex_make', }, { - command='lgrepadd', - flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type='ADDR_OTHER', - func='ex_make', + command = 'lgrepadd', + flags = bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type = 'ADDR_OTHER', + func = 'ex_make', }, { - command='lhelpgrep', - flags=bit.bor(EXTRA, NOTRLCOM, NEEDARG), - addr_type='ADDR_NONE', - func='ex_helpgrep', + command = 'lhelpgrep', + flags = bit.bor(EXTRA, NOTRLCOM, NEEDARG), + addr_type = 'ADDR_NONE', + func = 'ex_helpgrep', }, { - command='lhistory', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='qf_history', + command = 'lhistory', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'qf_history', }, { - command='ll', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_QUICKFIX', - func='ex_cc', + command = 'll', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_QUICKFIX', + func = 'ex_cc', }, { - command='llast', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cc', + command = 'llast', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cc', }, { - command='llist', - flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='qf_list', + command = 'llist', + flags = bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'qf_list', }, { - command='lmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'lmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='lmapclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'lmapclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='lmake', - flags=bit.bor(BANG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type='ADDR_NONE', - func='ex_make', + command = 'lmake', + flags = bit.bor(BANG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type = 'ADDR_NONE', + func = 'ex_make', }, { - command='lnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'lnoremap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='lnext', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'lnext', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='lnewer', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='qf_age', + command = 'lnewer', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'qf_age', }, { - command='lnfile', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'lnfile', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='loadview', - flags=bit.bor(FILE1, TRLBAR), - addr_type='ADDR_NONE', - func='ex_loadview', + command = 'loadview', + flags = bit.bor(FILE1, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_loadview', }, { - command='loadkeymap', - flags=bit.bor(CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_loadkeymap', + command = 'loadkeymap', + flags = bit.bor(CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_loadkeymap', }, { - command='lockmarks', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'lockmarks', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='lockvar', - flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_lockvar', + command = 'lockvar', + flags = bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_lockvar', }, { - command='lolder', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_UNSIGNED', - func='qf_age', + command = 'lolder', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_UNSIGNED', + func = 'qf_age', }, { - command='lopen', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_copen', + command = 'lopen', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_copen', }, { - command='lprevious', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cnext', + command = 'lprevious', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cnext', }, { - command='lpfile', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_OTHER', - func='ex_cnext', + command = 'lpfile', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_OTHER', + func = 'ex_cnext', }, { - command='lrewind', - flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), - addr_type='ADDR_UNSIGNED', - func='ex_cc', + command = 'lrewind', + flags = bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type = 'ADDR_UNSIGNED', + func = 'ex_cc', }, { - command='ltag', - flags=bit.bor(TRLBAR, BANG, WORD1), - addr_type='ADDR_NONE', - func='ex_tag', + command = 'ltag', + flags = bit.bor(TRLBAR, BANG, WORD1), + addr_type = 'ADDR_NONE', + func = 'ex_tag', }, { - command='lunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'lunmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='lua', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_lua', + command = 'lua', + flags = bit.bor(RANGE, EXTRA, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_lua', }, { - command='luado', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_luado', + command = 'luado', + flags = bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_luado', }, { - command='luafile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_luafile', + command = 'luafile', + flags = bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_luafile', }, { - command='lvimgrep', - flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_vimgrep', + command = 'lvimgrep', + flags = bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_vimgrep', }, { - command='lvimgrepadd', - flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_vimgrep', + command = 'lvimgrepadd', + flags = bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_vimgrep', }, { - command='lwindow', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_cwindow', + command = 'lwindow', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_cwindow', }, { - command='ls', - flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='buflist_list', + command = 'ls', + flags = bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'buflist_list', }, { - command='move', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_copymove', + command = 'move', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_copymove', }, { - command='mark', - flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_mark', + command = 'mark', + flags = bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_mark', }, { - command='make', - flags=bit.bor(BANG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type='ADDR_NONE', - func='ex_make', + command = 'make', + flags = bit.bor(BANG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type = 'ADDR_NONE', + func = 'ex_make', }, { - command='map', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'map', + flags = bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='mapclear', - flags=bit.bor(EXTRA, BANG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'mapclear', + flags = bit.bor(EXTRA, BANG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='marks', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_marks', + command = 'marks', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_marks', }, { - command='match', - flags=bit.bor(RANGE, EXTRA, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_match', + command = 'match', + flags = bit.bor(RANGE, EXTRA, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_match', }, { - command='menu', - flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'menu', + flags = bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='menutranslate', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menutranslate', + command = 'menutranslate', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menutranslate', }, { - command='messages', - flags=bit.bor(EXTRA, TRLBAR, RANGE, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_messages', + command = 'messages', + flags = bit.bor(EXTRA, TRLBAR, RANGE, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_messages', }, { - command='mkexrc', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mkrc', + command = 'mkexrc', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mkrc', }, { - command='mksession', - flags=bit.bor(BANG, FILE1, TRLBAR), - addr_type='ADDR_NONE', - func='ex_mkrc', + command = 'mksession', + flags = bit.bor(BANG, FILE1, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_mkrc', }, { - command='mkspell', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type='ADDR_NONE', - func='ex_mkspell', + command = 'mkspell', + flags = bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type = 'ADDR_NONE', + func = 'ex_mkspell', }, { - command='mkvimrc', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mkrc', + command = 'mkvimrc', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mkrc', }, { - command='mkview', - flags=bit.bor(BANG, FILE1, TRLBAR), - addr_type='ADDR_NONE', - func='ex_mkrc', + command = 'mkview', + flags = bit.bor(BANG, FILE1, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_mkrc', }, { - command='mode', - flags=bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mode', + command = 'mode', + flags = bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mode', }, { - command='mzscheme', - flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, LOCK_OK, SBOXOK), - addr_type='ADDR_LINES', - func='ex_script_ni', + command = 'mzscheme', + flags = bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, LOCK_OK, SBOXOK), + addr_type = 'ADDR_LINES', + func = 'ex_script_ni', }, { - command='mzfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_ni', + command = 'mzfile', + flags = bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_ni', }, { - command='next', - flags=bit.bor(RANGE, BANG, FILES, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_next', + command = 'next', + flags = bit.bor(RANGE, BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_next', }, { - command='new', - flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_splitview', + command = 'new', + flags = bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_splitview', }, { - command='nmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'nmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='nmapclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'nmapclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='nmenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'nmenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='nnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'nnoremap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='nnoremenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'nnoremenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='noremap', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'noremap', + flags = bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='noautocmd', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'noautocmd', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='nohlsearch', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_nohlsearch', + command = 'nohlsearch', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_nohlsearch', }, { - command='noreabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abbreviate', + command = 'noreabbrev', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abbreviate', }, { - command='noremenu', - flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'noremenu', + flags = bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='noswapfile', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'noswapfile', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='normal', - flags=bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, CTRLV, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_normal', + command = 'normal', + flags = bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, CTRLV, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_normal', }, { - command='number', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_print', + command = 'number', + flags = bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_print', }, { - command='nunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'nunmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='nunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'nunmenu', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='oldfiles', - flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_oldfiles', + command = 'oldfiles', + flags = bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_oldfiles', }, { - command='omap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'omap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='omapclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'omapclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='omenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'omenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='only', - flags=bit.bor(BANG, RANGE, COUNT, TRLBAR), - addr_type='ADDR_WINDOWS', - func='ex_only', + command = 'only', + flags = bit.bor(BANG, RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_WINDOWS', + func = 'ex_only', }, { - command='onoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'onoremap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='onoremenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'onoremenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='options', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_options', + command = 'options', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_options', }, { - command='ounmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'ounmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='ounmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'ounmenu', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='ownsyntax', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_ownsyntax', + command = 'ownsyntax', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_ownsyntax', }, { - command='print', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, SBOXOK), - addr_type='ADDR_LINES', - func='ex_print', + command = 'print', + flags = bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, SBOXOK), + addr_type = 'ADDR_LINES', + func = 'ex_print', }, { - command='packadd', - flags=bit.bor(BANG, FILE1, NEEDARG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_packadd', + command = 'packadd', + flags = bit.bor(BANG, FILE1, NEEDARG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_packadd', }, { - command='packloadall', - flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_packloadall', + command = 'packloadall', + flags = bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_packloadall', }, { - command='pclose', - flags=bit.bor(BANG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_pclose', + command = 'pclose', + flags = bit.bor(BANG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_pclose', }, { - command='perl', - flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_perl', + command = 'perl', + flags = bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_perl', }, { - command='perldo', - flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_perldo', + command = 'perldo', + flags = bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_perldo', }, { - command='perlfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_perlfile', + command = 'perlfile', + flags = bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_perlfile', }, { - command='pedit', - flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_pedit', + command = 'pedit', + flags = bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_pedit', }, { - command='pop', - flags=bit.bor(RANGE, BANG, COUNT, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_tag', + command = 'pop', + flags = bit.bor(RANGE, BANG, COUNT, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_tag', }, { - command='popup', - flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_popup', + command = 'popup', + flags = bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_popup', }, { - command='ppop', - flags=bit.bor(RANGE, BANG, COUNT, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_ptag', + command = 'ppop', + flags = bit.bor(RANGE, BANG, COUNT, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_ptag', }, { - command='preserve', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_preserve', + command = 'preserve', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_preserve', }, { - command='previous', - flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_previous', + command = 'previous', + flags = bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_previous', }, { - command='profile', - flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_profile', + command = 'profile', + flags = bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_profile', }, { - command='profdel', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_breakdel', + command = 'profdel', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_breakdel', }, { - command='psearch', - flags=bit.bor(BANG, RANGE, WHOLEFOLD, DFLALL, EXTRA), - addr_type='ADDR_LINES', - func='ex_psearch', + command = 'psearch', + flags = bit.bor(BANG, RANGE, WHOLEFOLD, DFLALL, EXTRA), + addr_type = 'ADDR_LINES', + func = 'ex_psearch', }, { - command='ptag', - flags=bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_ptag', + command = 'ptag', + flags = bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_ptag', }, { - command='ptNext', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_ptag', + command = 'ptNext', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_ptag', }, { - command='ptfirst', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_ptag', + command = 'ptfirst', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_ptag', }, { - command='ptjump', - flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type='ADDR_NONE', - func='ex_ptag', + command = 'ptjump', + flags = bit.bor(BANG, TRLBAR, WORD1), + addr_type = 'ADDR_NONE', + func = 'ex_ptag', }, { - command='ptlast', - flags=bit.bor(BANG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_ptag', + command = 'ptlast', + flags = bit.bor(BANG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_ptag', }, { - command='ptnext', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_ptag', + command = 'ptnext', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_ptag', }, { - command='ptprevious', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_ptag', + command = 'ptprevious', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_ptag', }, { - command='ptrewind', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_ptag', + command = 'ptrewind', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_ptag', }, { - command='ptselect', - flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type='ADDR_NONE', - func='ex_ptag', + command = 'ptselect', + flags = bit.bor(BANG, TRLBAR, WORD1), + addr_type = 'ADDR_NONE', + func = 'ex_ptag', }, { - command='put', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, REGSTR, TRLBAR, ZEROR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_put', + command = 'put', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, REGSTR, TRLBAR, ZEROR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_put', }, { - command='pwd', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_pwd', + command = 'pwd', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_pwd', }, { - command='python', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_python3', + command = 'python', + flags = bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_python3', }, { - command='pydo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_pydo3', + command = 'pydo', + flags = bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_pydo3', }, { - command='pyfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_py3file', + command = 'pyfile', + flags = bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_py3file', }, { - command='py3', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_python3', + command = 'py3', + flags = bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_python3', }, { - command='py3do', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_pydo3', + command = 'py3do', + flags = bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_pydo3', }, { - command='python3', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_python3', + command = 'python3', + flags = bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_python3', }, { - command='py3file', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_py3file', + command = 'py3file', + flags = bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_py3file', }, { - command='pyx', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_python3', + command = 'pyx', + flags = bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_python3', }, { - command='pyxdo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_pydo3', + command = 'pyxdo', + flags = bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_pydo3', }, { - command='pythonx', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_python3', + command = 'pythonx', + flags = bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_python3', }, { - command='pyxfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_py3file', + command = 'pyxfile', + flags = bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_py3file', }, { - command='quit', - flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_WINDOWS', - func='ex_quit', + command = 'quit', + flags = bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_WINDOWS', + func = 'ex_quit', }, { - command='quitall', - flags=bit.bor(BANG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_quit_all', + command = 'quitall', + flags = bit.bor(BANG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_quit_all', }, { - command='qall', - flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_quit_all', + command = 'qall', + flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_quit_all', }, { - command='read', - flags=bit.bor(BANG, RANGE, WHOLEFOLD, FILE1, ARGOPT, TRLBAR, ZEROR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_read', + command = 'read', + flags = bit.bor(BANG, RANGE, WHOLEFOLD, FILE1, ARGOPT, TRLBAR, ZEROR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_read', }, { - command='recover', - flags=bit.bor(BANG, FILE1, TRLBAR), - addr_type='ADDR_NONE', - func='ex_recover', + command = 'recover', + flags = bit.bor(BANG, FILE1, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_recover', }, { - command='redo', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_redo', + command = 'redo', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_redo', }, { - command='redir', - flags=bit.bor(BANG, FILES, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_redir', + command = 'redir', + flags = bit.bor(BANG, FILES, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_redir', }, { - command='redraw', - flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_redraw', + command = 'redraw', + flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_redraw', }, { - command='redrawstatus', - flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_redrawstatus', + command = 'redrawstatus', + flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_redrawstatus', }, { - command='redrawtabline', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_redrawtabline', + command = 'redrawtabline', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_redrawtabline', }, { - command='registers', - flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_display', + command = 'registers', + flags = bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_display', }, { - command='resize', - flags=bit.bor(RANGE, TRLBAR, WORD1, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_resize', + command = 'resize', + flags = bit.bor(RANGE, TRLBAR, WORD1, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_resize', }, { - command='retab', - flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, DFLALL, BANG, WORD1, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_retab', + command = 'retab', + flags = bit.bor(TRLBAR, RANGE, WHOLEFOLD, DFLALL, BANG, WORD1, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_retab', }, { - command='return', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_return', + command = 'return', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_return', }, { - command='rewind', - flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_rewind', + command = 'rewind', + flags = bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_rewind', }, { - command='right', - flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_align', + command = 'right', + flags = bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_align', }, { - command='rightbelow', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'rightbelow', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='rshada', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_shada', + command = 'rshada', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_shada', }, { - command='runtime', - flags=bit.bor(BANG, NEEDARG, FILES, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_runtime', + command = 'runtime', + flags = bit.bor(BANG, NEEDARG, FILES, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_runtime', }, { - command='rundo', - flags=bit.bor(NEEDARG, FILE1), - addr_type='ADDR_NONE', - func='ex_rundo', + command = 'rundo', + flags = bit.bor(NEEDARG, FILE1), + addr_type = 'ADDR_NONE', + func = 'ex_rundo', }, { - command='ruby', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_ruby', + command = 'ruby', + flags = bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_ruby', }, { - command='rubydo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_rubydo', + command = 'rubydo', + flags = bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_rubydo', }, { - command='rubyfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_rubyfile', + command = 'rubyfile', + flags = bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_rubyfile', }, { - command='rviminfo', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_shada', + command = 'rviminfo', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_shada', }, { - command='substitute', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW), - addr_type='ADDR_LINES', - func='ex_substitute', - preview_func='ex_substitute_preview', + command = 'substitute', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW), + addr_type = 'ADDR_LINES', + func = 'ex_substitute', + preview_func = 'ex_substitute_preview', }, { - command='sNext', - flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_previous', + command = 'sNext', + flags = bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_previous', }, { - command='sargument', - flags=bit.bor(BANG, RANGE, COUNT, EXTRA, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_ARGUMENTS', - func='ex_argument', + command = 'sargument', + flags = bit.bor(BANG, RANGE, COUNT, EXTRA, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_ARGUMENTS', + func = 'ex_argument', }, { - command='sall', - flags=bit.bor(BANG, RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_all', + command = 'sall', + flags = bit.bor(BANG, RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_all', }, { - command='sandbox', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'sandbox', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='saveas', - flags=bit.bor(BANG, FILE1, ARGOPT, CMDWIN, LOCK_OK, TRLBAR), - addr_type='ADDR_NONE', - func='ex_write', + command = 'saveas', + flags = bit.bor(BANG, FILE1, ARGOPT, CMDWIN, LOCK_OK, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_write', }, { - command='sbuffer', - flags=bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR), - addr_type='ADDR_BUFFERS', - func='ex_buffer', + command = 'sbuffer', + flags = bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR), + addr_type = 'ADDR_BUFFERS', + func = 'ex_buffer', }, { - command='sbNext', - flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_bprevious', + command = 'sbNext', + flags = bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_bprevious', }, { - command='sball', - flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_buffer_all', + command = 'sball', + flags = bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_buffer_all', }, { - command='sbfirst', - flags=bit.bor(CMDARG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_brewind', + command = 'sbfirst', + flags = bit.bor(CMDARG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_brewind', }, { - command='sblast', - flags=bit.bor(CMDARG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_blast', + command = 'sblast', + flags = bit.bor(CMDARG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_blast', }, { - command='sbmodified', - flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_bmodified', + command = 'sbmodified', + flags = bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_bmodified', }, { - command='sbnext', - flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_bnext', + command = 'sbnext', + flags = bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_bnext', }, { - command='sbprevious', - flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_bprevious', + command = 'sbprevious', + flags = bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_bprevious', }, { - command='sbrewind', - flags=bit.bor(CMDARG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_brewind', + command = 'sbrewind', + flags = bit.bor(CMDARG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_brewind', }, { - command='scriptnames', - flags=bit.bor(BANG, FILES, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_scriptnames', + command = 'scriptnames', + flags = bit.bor(BANG, FILES, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_scriptnames', }, { - command='scriptencoding', - flags=bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_scriptencoding', + command = 'scriptencoding', + flags = bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_scriptencoding', }, { - command='set', - flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK), - addr_type='ADDR_NONE', - func='ex_set', + command = 'set', + flags = bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK), + addr_type = 'ADDR_NONE', + func = 'ex_set', }, { - command='setfiletype', - flags=bit.bor(TRLBAR, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_setfiletype', + command = 'setfiletype', + flags = bit.bor(TRLBAR, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_setfiletype', }, { - command='setglobal', - flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK), - addr_type='ADDR_NONE', - func='ex_set', + command = 'setglobal', + flags = bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK), + addr_type = 'ADDR_NONE', + func = 'ex_set', }, { - command='setlocal', - flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK), - addr_type='ADDR_NONE', - func='ex_set', + command = 'setlocal', + flags = bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK), + addr_type = 'ADDR_NONE', + func = 'ex_set', }, { - command='sfind', - flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR, NEEDARG), - addr_type='ADDR_OTHER', - func='ex_splitview', + command = 'sfind', + flags = bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR, NEEDARG), + addr_type = 'ADDR_OTHER', + func = 'ex_splitview', }, { - command='sfirst', - flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_rewind', + command = 'sfirst', + flags = bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_rewind', }, { - command='simalt', - flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_ni', + command = 'simalt', + flags = bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_ni', }, { - command='sign', - flags=bit.bor(NEEDARG, RANGE, EXTRA, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_sign', + command = 'sign', + flags = bit.bor(NEEDARG, RANGE, EXTRA, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_sign', }, { - command='silent', - flags=bit.bor(NEEDARG, EXTRA, BANG, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'silent', + flags = bit.bor(NEEDARG, EXTRA, BANG, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='sleep', - flags=bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_sleep', + command = 'sleep', + flags = bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_sleep', }, { - command='slast', - flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_last', + command = 'slast', + flags = bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_last', }, { - command='smagic', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW), - addr_type='ADDR_LINES', - func='ex_submagic', - preview_func='ex_submagic_preview', + command = 'smagic', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW), + addr_type = 'ADDR_LINES', + func = 'ex_submagic', + preview_func = 'ex_submagic_preview', }, { - command='smap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'smap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='smapclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'smapclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='smenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'smenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='snext', - flags=bit.bor(RANGE, BANG, FILES, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_next', + command = 'snext', + flags = bit.bor(RANGE, BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_next', }, { - command='snomagic', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW), - addr_type='ADDR_LINES', - func='ex_submagic', - preview_func='ex_submagic_preview', + command = 'snomagic', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW), + addr_type = 'ADDR_LINES', + func = 'ex_submagic', + preview_func = 'ex_submagic_preview', }, { - command='snoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'snoremap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='snoremenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'snoremenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='source', - flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, FILE1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_source', + command = 'source', + flags = bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, FILE1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_source', }, { - command='sort', - flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, EXTRA, NOTRLCOM, MODIFY), - addr_type='ADDR_LINES', - func='ex_sort', + command = 'sort', + flags = bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, EXTRA, NOTRLCOM, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_sort', }, { - command='split', - flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_splitview', + command = 'split', + flags = bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_splitview', }, { - command='spellgood', - flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_spell', + command = 'spellgood', + flags = bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_spell', }, { - command='spelldump', - flags=bit.bor(BANG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_spelldump', + command = 'spelldump', + flags = bit.bor(BANG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_spelldump', }, { - command='spellinfo', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_spellinfo', + command = 'spellinfo', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_spellinfo', }, { - command='spellrepall', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_spellrepall', + command = 'spellrepall', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_spellrepall', }, { - command='spellrare', - flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_spell', + command = 'spellrare', + flags = bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_spell', }, { - command='spellundo', - flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_spell', + command = 'spellundo', + flags = bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_spell', }, { - command='spellwrong', - flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_spell', + command = 'spellwrong', + flags = bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_spell', }, { - command='sprevious', - flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_previous', + command = 'sprevious', + flags = bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_previous', }, { - command='srewind', - flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_rewind', + command = 'srewind', + flags = bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_rewind', }, { - command='stop', - flags=bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_stop', + command = 'stop', + flags = bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_stop', }, { - command='stag', - flags=bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_stag', + command = 'stag', + flags = bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_stag', }, { - command='startinsert', - flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_startinsert', + command = 'startinsert', + flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_startinsert', }, { - command='startgreplace', - flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_startinsert', + command = 'startgreplace', + flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_startinsert', }, { - command='startreplace', - flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_startinsert', + command = 'startreplace', + flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_startinsert', }, { - command='stopinsert', - flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_stopinsert', + command = 'stopinsert', + flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_stopinsert', }, { - command='stjump', - flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type='ADDR_NONE', - func='ex_stag', + command = 'stjump', + flags = bit.bor(BANG, TRLBAR, WORD1), + addr_type = 'ADDR_NONE', + func = 'ex_stag', }, { - command='stselect', - flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type='ADDR_NONE', - func='ex_stag', + command = 'stselect', + flags = bit.bor(BANG, TRLBAR, WORD1), + addr_type = 'ADDR_NONE', + func = 'ex_stag', }, { - command='sunhide', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_buffer_all', + command = 'sunhide', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_buffer_all', }, { - command='sunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'sunmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='sunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'sunmenu', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='suspend', - flags=bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_stop', + command = 'suspend', + flags = bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_stop', }, { - command='sview', - flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_splitview', + command = 'sview', + flags = bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_splitview', }, { - command='swapname', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_swapname', + command = 'swapname', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_swapname', }, { - command='syntax', - flags=bit.bor(EXTRA, NOTRLCOM, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_syntax', + command = 'syntax', + flags = bit.bor(EXTRA, NOTRLCOM, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_syntax', }, { - command='syntime', - flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_syntime', + command = 'syntime', + flags = bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_syntime', }, { - command='syncbind', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_syncbind', + command = 'syncbind', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_syncbind', }, { - command='t', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_copymove', + command = 't', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_copymove', }, { - command='tcd', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_cd', + command = 'tcd', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_cd', }, { - command='tchdir', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_cd', + command = 'tchdir', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_cd', }, { - command='tNext', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_tag', + command = 'tNext', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_tag', }, { - command='tag', - flags=bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_tag', + command = 'tag', + flags = bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_tag', }, { - command='tags', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='do_tags', + command = 'tags', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'do_tags', }, { - command='tab', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'tab', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='tabclose', - flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_TABS', - func='ex_tabclose', + command = 'tabclose', + flags = bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_TABS', + func = 'ex_tabclose', }, { - command='tabdo', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), - addr_type='ADDR_TABS', - func='ex_listdo', + command = 'tabdo', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type = 'ADDR_TABS', + func = 'ex_listdo', }, { - command='tabedit', - flags=bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_TABS', - func='ex_splitview', + command = 'tabedit', + flags = bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_TABS', + func = 'ex_splitview', }, { - command='tabfind', - flags=bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, NEEDARG, TRLBAR), - addr_type='ADDR_TABS', - func='ex_splitview', + command = 'tabfind', + flags = bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, NEEDARG, TRLBAR), + addr_type = 'ADDR_TABS', + func = 'ex_splitview', }, { - command='tabfirst', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_tabnext', + command = 'tabfirst', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_tabnext', }, { - command='tabmove', - flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type='ADDR_TABS', - func='ex_tabmove', + command = 'tabmove', + flags = bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type = 'ADDR_TABS', + func = 'ex_tabmove', }, { - command='tablast', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_tabnext', + command = 'tablast', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_tabnext', }, { - command='tabnext', - flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type='ADDR_TABS', - func='ex_tabnext', + command = 'tabnext', + flags = bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type = 'ADDR_TABS', + func = 'ex_tabnext', }, { - command='tabnew', - flags=bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_TABS', - func='ex_splitview', + command = 'tabnew', + flags = bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_TABS', + func = 'ex_splitview', }, { - command='tabonly', - flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_TABS', - func='ex_tabonly', + command = 'tabonly', + flags = bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_TABS', + func = 'ex_tabonly', }, { - command='tabprevious', - flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type='ADDR_TABS_RELATIVE', - func='ex_tabnext', + command = 'tabprevious', + flags = bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type = 'ADDR_TABS_RELATIVE', + func = 'ex_tabnext', }, { - command='tabNext', - flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type='ADDR_TABS_RELATIVE', - func='ex_tabnext', + command = 'tabNext', + flags = bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type = 'ADDR_TABS_RELATIVE', + func = 'ex_tabnext', }, { - command='tabrewind', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_tabnext', + command = 'tabrewind', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_tabnext', }, { - command='tabs', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_tabs', + command = 'tabs', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_tabs', }, { - command='tcl', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_script_ni', + command = 'tcl', + flags = bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_script_ni', }, { - command='tcldo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_ni', + command = 'tcldo', + flags = bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_ni', }, { - command='tclfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_ni', + command = 'tclfile', + flags = bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_ni', }, { - command='terminal', - flags=bit.bor(BANG, FILES, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_terminal', + command = 'terminal', + flags = bit.bor(BANG, FILES, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_terminal', }, { - command='tfirst', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_tag', + command = 'tfirst', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_tag', }, { - command='throw', - flags=bit.bor(EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_throw', + command = 'throw', + flags = bit.bor(EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_throw', }, { - command='tjump', - flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type='ADDR_NONE', - func='ex_tag', + command = 'tjump', + flags = bit.bor(BANG, TRLBAR, WORD1), + addr_type = 'ADDR_NONE', + func = 'ex_tag', }, { - command='tlast', - flags=bit.bor(BANG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_tag', + command = 'tlast', + flags = bit.bor(BANG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_tag', }, { - command='tlmenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'tlmenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='tlnoremenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'tlnoremenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='tlunmenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'tlunmenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='tmenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'tmenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='tmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'tmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='tmapclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'tmapclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='tnext', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_tag', + command = 'tnext', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_tag', }, { - command='tnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'tnoremap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='topleft', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'topleft', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='tprevious', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_tag', + command = 'tprevious', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_tag', }, { - command='trewind', - flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), - addr_type='ADDR_OTHER', - func='ex_tag', + command = 'trewind', + flags = bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type = 'ADDR_OTHER', + func = 'ex_tag', }, { - command='trust', - flags=bit.bor(EXTRA, FILE1, TRLBAR, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_trust', + command = 'trust', + flags = bit.bor(EXTRA, FILE1, TRLBAR, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_trust', }, { - command='try', - flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_try', + command = 'try', + flags = bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_try', }, { - command='tselect', - flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type='ADDR_NONE', - func='ex_tag', + command = 'tselect', + flags = bit.bor(BANG, TRLBAR, WORD1), + addr_type = 'ADDR_NONE', + func = 'ex_tag', }, { - command='tunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'tunmenu', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='tunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'tunmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='undo', - flags=bit.bor(BANG, RANGE, COUNT, ZEROR, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_undo', + command = 'undo', + flags = bit.bor(BANG, RANGE, COUNT, ZEROR, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_undo', }, { - command='undojoin', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_undojoin', + command = 'undojoin', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_undojoin', }, { - command='undolist', - flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_undolist', + command = 'undolist', + flags = bit.bor(TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_undolist', }, { - command='unabbreviate', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_abbreviate', + command = 'unabbreviate', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_abbreviate', }, { - command='unhide', - flags=bit.bor(RANGE, COUNT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_buffer_all', + command = 'unhide', + flags = bit.bor(RANGE, COUNT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_buffer_all', }, { - command='unlet', - flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unlet', + command = 'unlet', + flags = bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unlet', }, { - command='unlockvar', - flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_lockvar', + command = 'unlockvar', + flags = bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_lockvar', }, { - command='unmap', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'unmap', + flags = bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='unmenu', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'unmenu', + flags = bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='unsilent', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'unsilent', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='update', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR), - addr_type='ADDR_LINES', - func='ex_update', + command = 'update', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR), + addr_type = 'ADDR_LINES', + func = 'ex_update', }, { - command='vglobal', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, DFLALL, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_global', + command = 'vglobal', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, DFLALL, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_global', }, { - command='version', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_version', + command = 'version', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_version', }, { - command='verbose', - flags=bit.bor(NEEDARG, RANGE, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_wrongmodifier', + command = 'verbose', + flags = bit.bor(NEEDARG, RANGE, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_wrongmodifier', }, { - command='vertical', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_wrongmodifier', + command = 'vertical', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM), + addr_type = 'ADDR_NONE', + func = 'ex_wrongmodifier', }, { - command='visual', - flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_edit', + command = 'visual', + flags = bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_edit', }, { - command='view', - flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='ex_edit', + command = 'view', + flags = bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_edit', }, { - command='vimgrep', - flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_vimgrep', + command = 'vimgrep', + flags = bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_vimgrep', }, { - command='vimgrepadd', - flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_vimgrep', + command = 'vimgrepadd', + flags = bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_vimgrep', }, { - command='viusage', - flags=bit.bor(TRLBAR), - addr_type='ADDR_NONE', - func='ex_viusage', + command = 'viusage', + flags = bit.bor(TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_viusage', }, { - command='vmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'vmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='vmapclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'vmapclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='vmenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'vmenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='vnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'vnoremap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='vnew', - flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_splitview', + command = 'vnew', + flags = bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_splitview', }, { - command='vnoremenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'vnoremenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='vsplit', - flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_splitview', + command = 'vsplit', + flags = bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_splitview', }, { - command='vunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'vunmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='vunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'vunmenu', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='write', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_write', + command = 'write', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_write', }, { - command='wNext', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_wnext', + command = 'wNext', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_wnext', }, { - command='wall', - flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='do_wqall', + command = 'wall', + flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'do_wqall', }, { - command='while', - flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_while', + command = 'while', + flags = bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_while', }, { - command='winsize', - flags=bit.bor(EXTRA, NEEDARG, TRLBAR), - addr_type='ADDR_NONE', - func='ex_winsize', + command = 'winsize', + flags = bit.bor(EXTRA, NEEDARG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'ex_winsize', }, { - command='wincmd', - flags=bit.bor(NEEDARG, WORD1, RANGE, COUNT, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_wincmd', + command = 'wincmd', + flags = bit.bor(NEEDARG, WORD1, RANGE, COUNT, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_wincmd', }, { - command='windo', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), - addr_type='ADDR_WINDOWS', - func='ex_listdo', + command = 'windo', + flags = bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type = 'ADDR_WINDOWS', + func = 'ex_listdo', }, { - command='winpos', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_ni', + command = 'winpos', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_ni', }, { - command='wnext', - flags=bit.bor(RANGE, BANG, FILE1, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_wnext', + command = 'wnext', + flags = bit.bor(RANGE, BANG, FILE1, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_wnext', }, { - command='wprevious', - flags=bit.bor(RANGE, BANG, FILE1, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_wnext', + command = 'wprevious', + flags = bit.bor(RANGE, BANG, FILE1, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_wnext', }, { - command='wq', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR), - addr_type='ADDR_LINES', - func='ex_exit', + command = 'wq', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR), + addr_type = 'ADDR_LINES', + func = 'ex_exit', }, { - command='wqall', - flags=bit.bor(BANG, FILE1, ARGOPT, TRLBAR), - addr_type='ADDR_NONE', - func='do_wqall', + command = 'wqall', + flags = bit.bor(BANG, FILE1, ARGOPT, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'do_wqall', }, { - command='wshada', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_shada', + command = 'wshada', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_shada', }, { - command='wundo', - flags=bit.bor(BANG, NEEDARG, FILE1), - addr_type='ADDR_NONE', - func='ex_wundo', + command = 'wundo', + flags = bit.bor(BANG, NEEDARG, FILE1), + addr_type = 'ADDR_NONE', + func = 'ex_wundo', }, { - command='wviminfo', - flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_shada', + command = 'wviminfo', + flags = bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_shada', }, { - command='xit', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_exit', + command = 'xit', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_exit', }, { - command='xall', - flags=bit.bor(BANG, TRLBAR), - addr_type='ADDR_NONE', - func='do_wqall', + command = 'xall', + flags = bit.bor(BANG, TRLBAR), + addr_type = 'ADDR_NONE', + func = 'do_wqall', }, { - command='xmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'xmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='xmapclear', - flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_mapclear', + command = 'xmapclear', + flags = bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_mapclear', }, { - command='xmenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'xmenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='xnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_map', + command = 'xnoremap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_map', }, { - command='xnoremenu', - flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_OTHER', - func='ex_menu', + command = 'xnoremenu', + flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_OTHER', + func = 'ex_menu', }, { - command='xunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_unmap', + command = 'xunmap', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_unmap', }, { - command='xunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), - addr_type='ADDR_NONE', - func='ex_menu', + command = 'xunmenu', + flags = bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), + addr_type = 'ADDR_NONE', + func = 'ex_menu', }, { - command='yank', - flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_operators', + command = 'yank', + flags = bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_operators', }, { - command='z', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, FLAGS, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_z', + command = 'z', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, FLAGS, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_z', }, -- commands that don't start with a letter { - command='!', - enum='CMD_bang', - flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILES, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_bang', + command = '!', + enum = 'CMD_bang', + flags = bit.bor(RANGE, WHOLEFOLD, BANG, FILES, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_bang', }, { - command='#', - enum='CMD_pound', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_print', + command = '#', + enum = 'CMD_pound', + flags = bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_print', }, { - command='&', - enum='CMD_and', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_substitute', + command = '&', + enum = 'CMD_and', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_substitute', }, { - command='<', - enum='CMD_lshift', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_operators', + command = '<', + enum = 'CMD_lshift', + flags = bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_operators', }, { - command='=', - enum='CMD_equal', - flags=bit.bor(RANGE, EXTRA, DFLALL, ARGOPT, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_equal', + command = '=', + enum = 'CMD_equal', + flags = bit.bor(RANGE, EXTRA, DFLALL, ARGOPT, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_equal', }, { - command='>', - enum='CMD_rshift', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_operators', + command = '>', + enum = 'CMD_rshift', + flags = bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_operators', }, { - command='@', - enum='CMD_at', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK), - addr_type='ADDR_LINES', - func='ex_at', + command = '@', + enum = 'CMD_at', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK), + addr_type = 'ADDR_LINES', + func = 'ex_at', }, { - command='~', - enum='CMD_tilde', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), - addr_type='ADDR_LINES', - func='ex_substitute', + command = '~', + enum = 'CMD_tilde', + flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY), + addr_type = 'ADDR_LINES', + func = 'ex_substitute', }, -- commands that start with an uppercase letter { - command='Next', - flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), - addr_type='ADDR_OTHER', - func='ex_previous', + command = 'Next', + flags = bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type = 'ADDR_OTHER', + func = 'ex_previous', }, } diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 1722b7902b..8016e37ca7 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -11,12 +11,15 @@ #include "nvim/arglist.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/bufwrite.h" #include "nvim/change.h" #include "nvim/channel.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" @@ -24,9 +27,10 @@ #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" #include "nvim/memory.h" @@ -39,6 +43,7 @@ #include "nvim/pos_defs.h" #include "nvim/quickfix.h" #include "nvim/runtime.h" +#include "nvim/types_defs.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -99,7 +104,7 @@ void ex_perldo(exarg_T *eap) /// Careful: autocommands may make "buf" invalid! /// /// @return FAIL for failure, OK otherwise -int autowrite(buf_T *buf, int forceit) +int autowrite(buf_T *buf, bool forceit) { bufref_T bufref; @@ -131,7 +136,7 @@ void autowrite_all(void) if (bufIsChanged(buf) && !buf->b_p_ro && !bt_dontwrite(buf)) { bufref_T bufref; set_bufref(&bufref, buf); - (void)buf_write_all(buf, false); + buf_write_all(buf, false); // an autocommand may have deleted the buffer if (!bufref_valid(&bufref)) { buf = firstbuf; @@ -144,7 +149,7 @@ void autowrite_all(void) /// For flags use the CCGD_ values. bool check_changed(buf_T *buf, int flags) { - int forceit = (flags & CCGD_FORCEIT); + bool forceit = (flags & CCGD_FORCEIT); bufref_T bufref; set_bufref(&bufref, buf); @@ -210,7 +215,7 @@ void dialog_changed(buf_T *buf, bool checkall) if (buf->b_fname != NULL && check_overwrite(&ea, buf, buf->b_fname, buf->b_ffname, false) == OK) { // didn't hit Cancel - (void)buf_write_all(buf, false); + buf_write_all(buf, false); } } else if (ret == VIM_NO) { unchanged(buf, true, false); @@ -226,7 +231,7 @@ void dialog_changed(buf_T *buf, bool checkall) if (buf2->b_fname != NULL && check_overwrite(&ea, buf2, buf2->b_fname, buf2->b_ffname, false) == OK) { // didn't hit Cancel - (void)buf_write_all(buf2, false); + buf_write_all(buf2, false); } // an autocommand may have deleted the buffer if (!bufref_valid(&bufref)) { @@ -260,7 +265,7 @@ bool dialog_close_terminal(buf_T *buf) /// @return true if the buffer "buf" can be abandoned, either by making it /// hidden, autowriting it or unloading it. -bool can_abandon(buf_T *buf, int forceit) +bool can_abandon(buf_T *buf, bool forceit) { return buf_hide(buf) || !bufIsChanged(buf) @@ -400,7 +405,7 @@ buf_found: // Open the changed buffer in the current window. if (buf != curbuf) { - set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO); + set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO, true); } theend: @@ -422,7 +427,7 @@ int check_fname(void) /// Flush the contents of a buffer, unless it has no file name. /// /// @return FAIL for failure, OK otherwise -int buf_write_all(buf_T *buf, int forceit) +int buf_write_all(buf_T *buf, bool forceit) { buf_T *old_curbuf = curbuf; @@ -546,7 +551,7 @@ void ex_listdo(exarg_T *eap) break; } assert(wp); - execute = !wp->w_floating || wp->w_float_config.focusable; + execute = !wp->w_floating || wp->w_config.focusable; if (execute) { win_goto(wp); if (curwin != wp) { @@ -743,7 +748,7 @@ void ex_checktime(exarg_T *eap) } else { buf_T *buf = buflist_findnr((int)eap->line2); if (buf != NULL) { // cannot happen? - (void)buf_check_timestamp(buf); + buf_check_timestamp(buf); } } no_check_timestamps = save_no_check_timestamps; @@ -762,7 +767,7 @@ static void script_host_execute(char *name, exarg_T *eap) tv_list_append_number(args, (int)eap->line1); tv_list_append_number(args, (int)eap->line2); - (void)eval_call_provider(name, "execute", args, true); + eval_call_provider(name, "execute", args, true); } } @@ -778,7 +783,7 @@ static void script_host_execute_file(char *name, exarg_T *eap) // current range tv_list_append_number(args, (int)eap->line1); tv_list_append_number(args, (int)eap->line2); - (void)eval_call_provider(name, "execute_file", args, true); + eval_call_provider(name, "execute_file", args, true); } } @@ -789,13 +794,12 @@ static void script_host_do_range(char *name, exarg_T *eap) tv_list_append_number(args, (int)eap->line1); tv_list_append_number(args, (int)eap->line2); tv_list_append_string(args, eap->arg, -1); - (void)eval_call_provider(name, "do_range", args, true); + eval_call_provider(name, "do_range", args, true); } } /// ":drop" -/// Opens the first argument in a window. When there are two or more arguments -/// the argument list is redefined. +/// Opens the first argument in a window, and the argument list is redefined. void ex_drop(exarg_T *eap) { bool split = false; @@ -820,6 +824,8 @@ void ex_drop(exarg_T *eap) // edited in a window yet. It's like ":tab all" but without closing // windows or tabs. ex_all(eap); + cmdmod.cmod_tab = 0; + ex_rewind(eap); return; } @@ -840,6 +846,7 @@ void ex_drop(exarg_T *eap) buf_check_timestamp(curbuf); curbuf->b_p_ar = save_ar; } + ex_rewind(eap); return; } } diff --git a/src/nvim/ex_cmds2.h b/src/nvim/ex_cmds2.h index 4f41f2cc41..a35a27758c 100644 --- a/src/nvim/ex_cmds2.h +++ b/src/nvim/ex_cmds2.h @@ -1,7 +1,7 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep /// flags for check_changed() enum { diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 00363884ec..827680cbb5 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -5,8 +5,7 @@ #include "nvim/eval/typval_defs.h" #include "nvim/ex_eval_defs.h" -#include "nvim/normal_defs.h" -#include "nvim/pos_defs.h" +#include "nvim/os/time_defs.h" #include "nvim/regexp_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -127,7 +126,7 @@ struct aucmd_executable_t { typedef char *(*LineGetter)(int, void *, int, bool); /// Structure for command definition. -typedef struct cmdname { +typedef struct { char *cmd_name; ///< Name of the command. ex_func_T cmd_func; ///< Function with implementation of this command. ex_preview_func_T cmd_preview_func; ///< Preview callback function of this command. @@ -229,3 +228,10 @@ typedef struct { bool bar; } magic; } CmdParseInfo; + +/// Previous :substitute replacement string definition +typedef struct { + char *sub; ///< Previous replacement string. + Timestamp timestamp; ///< Time when it was last set. + list_T *additional_elements; ///< Additional data left from ShaDa file. +} SubReplacementString; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 0b466bbe4e..2913f6d4e9 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9,12 +9,15 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "auto/config.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" #include "nvim/arglist.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" @@ -28,23 +31,27 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/ex_eval_defs.h" #include "nvim/ex_getln.h" #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/input.h" #include "nvim/keycodes.h" @@ -52,20 +59,25 @@ #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/shell.h" #include "nvim/path.h" #include "nvim/popupmenu.h" @@ -73,16 +85,20 @@ #include "nvim/profile.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/search.h" #include "nvim/shada.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/tag.h" #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/undo_defs.h" #include "nvim/usercmd.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -190,8 +206,8 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp) trylevel = dsp->trylevel; force_abort = dsp->force_abort; caught_stack = dsp->caught_stack; - (void)v_exception(dsp->vv_exception); - (void)v_throwpoint(dsp->vv_throwpoint); + v_exception(dsp->vv_exception); + v_throwpoint(dsp->vv_throwpoint); did_emsg = dsp->did_emsg; got_int = dsp->got_int; did_throw = dsp->did_throw; @@ -293,6 +309,33 @@ static void msg_verbose_cmd(linenr_T lnum, char *cmd) no_wait_return--; } +static int cmdline_call_depth = 0; ///< recursiveness + +/// Start executing an Ex command line. +/// +/// @return FAIL if too recursive, OK otherwise. +static int do_cmdline_start(void) +{ + assert(cmdline_call_depth >= 0); + // It's possible to create an endless loop with ":execute", catch that + // here. The value of 200 allows nested function calls, ":source", etc. + // Allow 200 or 'maxfuncdepth', whatever is larger. + if (cmdline_call_depth >= 200 && cmdline_call_depth >= p_mfd) { + return FAIL; + } + cmdline_call_depth++; + start_batch_changes(); + return OK; +} + +/// End executing an Ex command line. +static void do_cmdline_end(void) +{ + cmdline_call_depth--; + assert(cmdline_call_depth >= 0); + end_batch_changes(); +} + /// Execute a simple command line. Used for translated commands like "*". int do_cmdline_cmd(const char *cmd) { @@ -343,7 +386,6 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) char *(*cmd_getline)(int, void *, int, bool); void *cmd_cookie; struct loop_cookie cmd_loop_cookie; - static int call_depth = 0; // recursiveness // For every pair of do_cmdline()/do_one_cmd() calls, use an extra memory // location for storing error messages to be converted to an exception. @@ -355,10 +397,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) msg_list = &private_msg_list; private_msg_list = NULL; - // It's possible to create an endless loop with ":execute", catch that - // here. The value of 200 allows nested function calls, ":source", etc. - // Allow 200 or 'maxfuncdepth', whatever is larger. - if (call_depth >= 200 && call_depth >= p_mfd) { + if (do_cmdline_start() == FAIL) { emsg(_(e_command_too_recursive)); // When converting to an exception, we do not include the command name // since this is not an error of the specific command. @@ -366,8 +405,6 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) msg_list = saved_msg_list; return FAIL; } - call_depth++; - start_batch_changes(); ga_init(&lines_ga, (int)sizeof(wcmd_T), 10); @@ -710,7 +747,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) } // Convert an interrupt to an exception if appropriate. - (void)do_intthrow(&cstack); + do_intthrow(&cstack); // Continue executing command lines when: // - no CTRL-C typed, no aborting error, no exception thrown or try @@ -868,8 +905,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) did_endif = false; // in case do_cmdline used recursively - call_depth--; - end_batch_changes(); + do_cmdline_end(); return retval; } @@ -1653,9 +1689,13 @@ static int execute_cmd0(int *retv, exarg_T *eap, const char **errormsg, bool pre /// @param preview Execute command preview callback instead of actual command int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) { - const char *errormsg = NULL; int retv = 0; + if (do_cmdline_start() == FAIL) { + emsg(_(e_command_too_recursive)); + return retv; + } + const char *errormsg = NULL; #undef ERROR #define ERROR(msg) \ do { \ @@ -1703,8 +1743,8 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) && eap->addr_type == ADDR_LINES) { // Put the first line at the start of a closed fold, put the last line // at the end of a closed fold. - (void)hasFolding(eap->line1, &eap->line1, NULL); - (void)hasFolding(eap->line2, NULL, &eap->line2); + hasFolding(eap->line1, &eap->line1, NULL); + hasFolding(eap->line2, NULL, &eap->line2); } // Use first argument as count when possible @@ -1712,6 +1752,9 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) goto end; } + cstack_T cstack = { .cs_idx = -1 }; + eap->cstack = &cstack; + // Execute the command execute_cmd0(&retv, eap, &errormsg, preview); @@ -1719,9 +1762,12 @@ end: if (errormsg != NULL && *errormsg != NUL) { emsg(errormsg); } + // Undo command modifiers undo_cmdmod(&cmdmod); cmdmod = save_cmdmod; + + do_cmdline_end(); return retv; #undef ERROR } @@ -1955,7 +2001,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter } if (!ea.skip && got_int) { ea.skip = true; - (void)do_intthrow(cstack); + do_intthrow(cstack); } // 4. Parse a range specifier of the form: addr [,addr] [;addr] .. @@ -2167,8 +2213,8 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter && ea.addr_type == ADDR_LINES) { // Put the first line at the start of a closed fold, put the last line // at the end of a closed fold. - (void)hasFolding(ea.line1, &ea.line1, NULL); - (void)hasFolding(ea.line2, NULL, &ea.line2); + hasFolding(ea.line1, &ea.line1, NULL); + hasFolding(ea.line2, NULL, &ea.line2); } // For the ":make" and ":grep" commands we insert the 'makeprg'/'grepprg' @@ -2651,7 +2697,7 @@ static void apply_cmdmod(cmdmod_T *cmod) // Set 'eventignore' to "all". // First save the existing option value for restoring it later. cmod->cmod_save_ei = xstrdup(p_ei); - set_string_option_direct("ei", -1, "all", OPT_FREE, SID_NONE); + set_string_option_direct(kOptEventignore, "all", 0, SID_NONE); } } @@ -2671,7 +2717,7 @@ void undo_cmdmod(cmdmod_T *cmod) if (cmod->cmod_save_ei != NULL) { // Restore 'eventignore' to the value before ":noautocmd". - set_string_option_direct("ei", -1, cmod->cmod_save_ei, OPT_FREE, SID_NONE); + set_string_option_direct(kOptEventignore, cmod->cmod_save_ei, 0, SID_NONE); free_string_option(cmod->cmod_save_ei); cmod->cmod_save_ei = NULL; } @@ -3256,7 +3302,7 @@ static const char *addr_error(cmd_addr_T addr_type) /// @param errormsg Error message, if any /// /// @return MAXLNUM when no Ex address was found. -static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int skip, bool silent, +static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool skip, bool silent, int to_other_file, int address_count, const char **errormsg) FUNC_ATTR_NONNULL_ALL { @@ -3376,7 +3422,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int fmark_T *fm = mark_get(curbuf, curwin, NULL, flag, *cmd); cmd++; if (fm != NULL && fm->fnum != curbuf->handle) { - (void)mark_move_to(fm, 0); + mark_move_to(fm, 0); // Jumped to another file. lnum = curwin->w_cursor.lnum; } else { @@ -3550,7 +3596,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int // closed fold after the first address. if (addr_type == ADDR_LINES && (i == '-' || i == '+') && address_count >= 2) { - (void)hasFolding(lnum, NULL, &lnum); + hasFolding(lnum, NULL, &lnum); } if (i == '-') { lnum -= n; @@ -3777,12 +3823,12 @@ int expand_filename(exarg_T *eap, char **cmdlinep, const char **errormsgp) // Decide to expand wildcards *before* replacing '%', '#', etc. If // the file name contains a wildcard it should not cause expanding. // (it will be expanded anyway if there is a wildcard before replacing). - int has_wildcards = path_has_wildcard(p); + bool has_wildcards = path_has_wildcard(p); while (*p != NUL) { // Skip over `=expr`, wildcards in it are not expanded. if (p[0] == '`' && p[1] == '=') { p += 2; - (void)skip_expr(&p, NULL); + skip_expr(&p, NULL); if (*p == '`') { p++; } @@ -3857,9 +3903,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, const char **errormsgp) || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal) && strpbrk(repl, "!") != NULL) { - char *l; - - l = vim_strsave_escaped(repl, "!"); + char *l = vim_strsave_escaped(repl, "!"); xfree(repl); repl = l; } @@ -3887,7 +3931,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, const char **errormsgp) p = NULL; } if (p != NULL) { - (void)repl_cmdline(eap, eap->arg, strlen(eap->arg), p, cmdlinep); + repl_cmdline(eap, eap->arg, strlen(eap->arg), p, cmdlinep); } } @@ -3915,7 +3959,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, const char **errormsgp) if (p == NULL) { return FAIL; } - (void)repl_cmdline(eap, eap->arg, strlen(eap->arg), p, cmdlinep); + repl_cmdline(eap, eap->arg, strlen(eap->arg), p, cmdlinep); xfree(p); } } @@ -4001,7 +4045,7 @@ void separate_nextcmd(exarg_T *eap) } else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE)) { // Skip over `=expr` when wildcards are expanded. p += 2; - (void)skip_expr(&p, NULL); + skip_expr(&p, NULL); if (*p == NUL) { // stop at NUL after CTRL-V break; } @@ -4060,7 +4104,7 @@ static char *getargcmd(char **argp) /// Find end of "+command" argument. Skip over "\ " and "\\". /// /// @param rembs true to halve the number of backslashes -char *skip_cmd_arg(char *p, int rembs) +char *skip_cmd_arg(char *p, bool rembs) { while (*p && !ascii_isspace(*p)) { if (*p == '\\' && p[1] != NUL) { @@ -4373,7 +4417,7 @@ static void ex_doautocmd(exarg_T *eap) int call_do_modelines = check_nomodeline(&arg); bool did_aucmd; - (void)do_doautocmd(arg, false, &did_aucmd); + do_doautocmd(arg, false, &did_aucmd); // Only when there is no <nomodeline>. if (call_do_modelines && did_aucmd) { do_modelines(0); @@ -4505,7 +4549,7 @@ char *check_nextcmd(char *p) /// @param message when false check only, no messages /// /// @return FAIL and give error message if 'message' true, return OK otherwise -static int check_more(int message, bool forceit) +static int check_more(bool message, bool forceit) { int n = ARGCOUNT - curwin->w_arg_idx - 1; @@ -5368,9 +5412,9 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) || eap->cmdidx == CMD_vnew) && *eap->arg == NUL) { // ":new" or ":tabnew" without argument: edit a new empty buffer setpcmark(); - (void)do_ecmd(0, NULL, NULL, eap, ECMD_ONE, - ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0), - old_curwin == NULL ? curwin : NULL); + do_ecmd(0, NULL, NULL, eap, ECMD_ONE, + ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0), + old_curwin == NULL ? curwin : NULL); } else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit) || *eap->arg != NUL) { // Can't edit another file when "textlock" or "curbuf->b_ro_locked" is set. @@ -5552,7 +5596,7 @@ static void ex_read(exarg_T *eap) eap->line2, 0, (linenr_T)MAXLNUM, eap, 0, false); } else { if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL) { - (void)setaltfname(eap->arg, eap->arg, 1); + setaltfname(eap->arg, eap->arg, 1); } i = readfile(eap->arg, NULL, eap->line2, 0, (linenr_T)MAXLNUM, eap, 0, false); @@ -5823,7 +5867,7 @@ void do_sleep(int64_t msec) // If CTRL-C was typed to interrupt the sleep, drop the CTRL-C from the // input buffer, otherwise a following call to input() fails. if (got_int) { - (void)vpeekc(); + vpeekc(); } } @@ -5908,7 +5952,7 @@ static void ex_operators(exarg_T *eap) case CMD_yank: oa.op_type = OP_YANK; - (void)op_yank(&oa, true); + op_yank(&oa, true); break; default: // CMD_rshift or CMD_lshift @@ -6050,7 +6094,7 @@ static void ex_at(exarg_T *eap) // Continue until the stuff buffer is empty and all added characters // have been consumed. while (!stuff_empty() || typebuf.tb_len > prev_len) { - (void)do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE); + do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE); } exec_from_reg = save_efr; @@ -6210,7 +6254,7 @@ static void ex_redir(exarg_T *eap) semsg(_(e_invarg2), eap->arg); } } else if (*arg == '=' && arg[1] == '>') { - int append; + bool append; // redirect to a variable close_redir(); @@ -6224,7 +6268,7 @@ static void ex_redir(exarg_T *eap) } if (var_redir_start(skipwhite(arg), append) == OK) { - redir_vname = 1; + redir_vname = true; } } else { // TODO(vim): redirect to a buffer semsg(_(e_invarg2), eap->arg); @@ -6330,7 +6374,7 @@ static void close_redir(void) redir_reg = 0; if (redir_vname) { var_redir_stop(); - redir_vname = 0; + redir_vname = false; } } @@ -7171,7 +7215,7 @@ static void ex_shada(exarg_T *eap) p_shada = "'100"; } if (eap->cmdidx == CMD_rviminfo || eap->cmdidx == CMD_rshada) { - (void)shada_read_everything(eap->arg, eap->forceit, false); + shada_read_everything(eap->arg, eap->forceit, false); } else { shada_write_file(eap->arg, eap->forceit); } @@ -7242,7 +7286,7 @@ static void ex_filetype(exarg_T *eap) } } if (*arg == 'd') { - (void)do_doautocmd("filetypedetect BufRead", true, NULL); + do_doautocmd("filetypedetect BufRead", true, NULL); do_modelines(0); } } else if (strcmp(arg, "off") == 0) { @@ -7304,7 +7348,7 @@ static void ex_setfiletype(exarg_T *eap) arg += 9; } - set_option_value_give_err("filetype", CSTR_AS_OPTVAL(arg), OPT_LOCAL); + set_option_value_give_err(kOptFiletype, CSTR_AS_OPTVAL(arg), OPT_LOCAL); if (arg != eap->arg) { did_filetype = false; } @@ -7383,6 +7427,44 @@ void set_pressedreturn(bool val) ex_pressedreturn = val; } +/// ":checkhealth [plugins]" +static void ex_checkhealth(exarg_T *eap) +{ + Error err = ERROR_INIT; + MAXSIZE_TEMP_ARRAY(args, 2); + + char mods[1024]; + size_t mods_len = 0; + mods[0] = NUL; + + if (cmdmod.cmod_tab > 0 || cmdmod.cmod_split != 0) { + bool multi_mods = false; + mods_len = add_win_cmd_modifiers(mods, &cmdmod, &multi_mods); + assert(mods_len < sizeof(mods)); + } + ADD_C(args, STRING_OBJ(((String){ .data = mods, .size = mods_len }))); + ADD_C(args, CSTR_AS_OBJ(eap->arg)); + + NLUA_EXEC_STATIC("vim.health._check(...)", args, kRetNilBool, NULL, &err); + if (!ERROR_SET(&err)) { + return; + } + + const char *vimruntime_env = os_getenv("VIMRUNTIME"); + if (vimruntime_env == NULL) { + emsg(_("E5009: $VIMRUNTIME is empty or unset")); + } else { + bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env); + if (rtp_ok) { + semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); + } else { + emsg(_("E5009: Invalid 'runtimepath'")); + } + } + semsg_multiline(err.msg); + api_clear_error(&err); +} + static void ex_terminal(exarg_T *eap) { char ex_cmd[1024]; @@ -7390,10 +7472,8 @@ static void ex_terminal(exarg_T *eap) if (cmdmod.cmod_tab > 0 || cmdmod.cmod_split != 0) { bool multi_mods = false; - - // ex_cmd must be a null terminated string before passing to add_win_cmd_modifiers - ex_cmd[0] = '\0'; - + // ex_cmd must be a null-terminated string before passing to add_win_cmd_modifiers + ex_cmd[0] = NUL; len = add_win_cmd_modifiers(ex_cmd, &cmdmod, &multi_mods); assert(len < sizeof(ex_cmd)); int result = snprintf(ex_cmd + len, sizeof(ex_cmd) - len, " new"); @@ -7424,7 +7504,9 @@ static void ex_terminal(exarg_T *eap) char shell_argv[512] = { 0 }; while (*p != NULL) { - snprintf(tempstring, sizeof(tempstring), ",\"%s\"", *p); + char *escaped = vim_strsave_escaped(*p, "\"\\"); + snprintf(tempstring, sizeof(tempstring), ",\"%s\"", escaped); + xfree(escaped); xstrlcat(shell_argv, tempstring, sizeof(shell_argv)); p++; } diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index 698153e8df..ce23b9f464 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -6,8 +6,8 @@ #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/getchar_defs.h" -#include "nvim/globals.h" #include "nvim/types_defs.h" // IWYU pragma: keep +#include "nvim/vim_defs.h" // IWYU pragma: keep /// flags for do_cmdline() enum { diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index d2a1d53b78..472741d537 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -13,19 +13,22 @@ #include "nvim/debugger.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_eval_defs.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option_vars.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/strings.h" #include "nvim/vim_defs.h" @@ -93,7 +96,7 @@ static void discard_pending_return(typval_T *p) // expression evaluation is done without producing any error messages, but all // error messages on parsing errors during the expression evaluation are given // (even if a try conditional is active). -static int cause_abort = false; +static bool cause_abort = false; /// @return true when immediately aborting on error, or when an interrupt /// occurred or an exception was thrown but not caught. @@ -106,7 +109,7 @@ static int cause_abort = false; /// That is, during cancellation of an expression evaluation after an aborting /// function call or due to a parsing error, aborting() always returns the same /// value. "got_int" is also set by calling interrupt(). -int aborting(void) +bool aborting(void) { return (did_emsg && force_abort) || got_int || did_throw; } @@ -126,7 +129,7 @@ void update_force_abort(void) /// abort the script processing. Can be used to suppress an autocommand after /// execution of a failing subcommand as long as the error message has not been /// displayed and actually caused the abortion. -int should_abort(int retcode) +bool should_abort(int retcode) { return (retcode == FAIL && trylevel != 0 && !emsg_silent) || aborting(); } @@ -135,7 +138,7 @@ int should_abort(int retcode) /// ended on an error. This means that parsing commands is continued in order /// to find finally clauses to be executed, and that some errors in skipped /// commands are still reported. -int aborted_in_try(void) +bool aborted_in_try(void) FUNC_ATTR_PURE { // This function is only called after an error. In this case, "force_abort" @@ -224,7 +227,7 @@ bool cause_errthrow(const char *mesg, bool multiline, bool severe, bool *ignore) // catch clause; just finally clauses are executed before the script // is terminated. return false; - } else // NOLINT(readability/braces) + } else #endif { // Prepare the throw of an error exception, so that everything will @@ -327,7 +330,7 @@ void do_errthrow(cstack_T *cstack, char *cmdname) /// /// @return true if the current exception is discarded or, /// false otherwise. -int do_intthrow(cstack_T *cstack) +bool do_intthrow(cstack_T *cstack) { // If no interrupt occurred or no try conditional is active and no exception // is being thrown, do nothing (for compatibility of non-EH scripts). @@ -370,7 +373,7 @@ int do_intthrow(cstack_T *cstack) } /// Get an exception message that is to be stored in current_exception->value. -char *get_exception_string(void *value, except_type_T type, char *cmdname, int *should_free) +char *get_exception_string(void *value, except_type_T type, char *cmdname, bool *should_free) { char *ret; @@ -455,7 +458,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname) excp->messages = (msglist_T *)value; } - int should_free; + bool should_free; excp->value = get_exception_string(value, type, cmdname, &should_free); if (excp->value == NULL && should_free) { goto nomem; @@ -842,10 +845,10 @@ void ex_if(exarg_T *eap) cstack->cs_idx++; cstack->cs_flags[cstack->cs_idx] = 0; - int skip = CHECK_SKIP; + bool skip = CHECK_SKIP; bool error; - int result = eval_to_bool(eap->arg, &error, eap, skip); + bool result = eval_to_bool(eap->arg, &error, eap, skip); if (!skip && !error) { if (result) { @@ -876,7 +879,7 @@ void ex_endif(exarg_T *eap) // discarded by throwing the interrupt exception later on. if (!(eap->cstack->cs_flags[eap->cstack->cs_idx] & CSF_TRUE) && dbg_check_skipped(eap)) { - (void)do_intthrow(eap->cstack); + do_intthrow(eap->cstack); } eap->cstack->cs_idx--; @@ -927,7 +930,7 @@ void ex_else(exarg_T *eap) // for a parsing errors is discarded when throwing the interrupt exception // later on. if (!skip && dbg_check_skipped(eap) && got_int) { - (void)do_intthrow(cstack); + do_intthrow(cstack); skip = true; } @@ -972,7 +975,7 @@ void ex_while(exarg_T *eap) if (cstack->cs_idx == CSTACK_LEN - 1) { eap->errmsg = _("E585: :while/:for nesting too deep"); } else { - int result; + bool result; // The loop flag is set when we have jumped back from the matching // ":endwhile" or ":endfor". When not set, need to initialise this // cstack entry. @@ -1133,7 +1136,7 @@ void ex_endwhile(exarg_T *eap) } } // Cleanup and rewind all contained (and unclosed) conditionals. - (void)cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, false); + cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, false); rewind_conditionals(cstack, idx, CSF_TRY, &cstack->cs_trylevel); } else if (cstack->cs_flags[cstack->cs_idx] & CSF_TRUE && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE) @@ -1146,7 +1149,7 @@ void ex_endwhile(exarg_T *eap) // throw an interrupt exception if appropriate. Doing this here // prevents that an exception for a parsing error is discarded when // throwing the interrupt exception later on. - (void)do_intthrow(cstack); + do_intthrow(cstack); } // Set loop flag, so do_cmdline() will jump back to the matching @@ -1184,7 +1187,7 @@ void ex_throw(exarg_T *eap) /// used for rethrowing an uncaught exception. void do_throw(cstack_T *cstack) { - int inactivate_try = false; + bool inactivate_try = false; // Cleanup and deactivate up to the next surrounding try conditional that // is not in its finally clause. Normally, do not deactivate the try @@ -1477,7 +1480,7 @@ void ex_finally(exarg_T *eap) // occurred before the ":finally". That is, discard the // original exception and replace it by an interrupt // exception. - (void)do_intthrow(cstack); + do_intthrow(cstack); } // If there is a preceding catch clause and it caught the exception, @@ -1618,7 +1621,7 @@ void ex_endtry(exarg_T *eap) // set "skip" and "rethrow". if (got_int) { skip = true; - (void)do_intthrow(cstack); + do_intthrow(cstack); // The do_intthrow() call may have reset did_throw or // cstack->cs_pending[idx]. rethrow = false; @@ -1650,7 +1653,7 @@ void ex_endtry(exarg_T *eap) // was no finally clause, finish the exception now. This happens also // after errors except when this ":endtry" is not within a ":try". // Restore "emsg_silent" if it has been reset by this try conditional. - (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, true); + cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, true); if (cstack->cs_idx >= 0 && (cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { cstack->cs_idx--; @@ -1862,7 +1865,7 @@ void leave_cleanup(cleanup_T *csp) int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive) { int idx; - int stop = false; + bool stop = false; for (idx = cstack->cs_idx; idx >= 0; idx--) { if (cstack->cs_flags[idx] & CSF_TRY) { @@ -1999,7 +2002,7 @@ void ex_endfunction(exarg_T *eap) } /// @return true if the string "p" looks like a ":while" or ":for" command. -int has_loop_cmd(char *p) +bool has_loop_cmd(char *p) { // skip modifiers, white space and ':' while (true) { diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h index 0294acd109..d46d9c695a 100644 --- a/src/nvim/ex_eval.h +++ b/src/nvim/ex_eval.h @@ -1,7 +1,7 @@ #pragma once #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep -#include "nvim/ex_eval_defs.h" // IWYU pragma: export +#include "nvim/ex_eval_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_eval.h.generated.h" diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h index c7231bb315..3f5e510a20 100644 --- a/src/nvim/ex_eval_defs.h +++ b/src/nvim/ex_eval_defs.h @@ -13,9 +13,11 @@ struct eslist_elem { eslist_T *next; ///< next element on the list }; -/// For conditional commands a stack is kept of nested conditionals. -/// When cs_idx < 0, there is no conditional command. -enum { CSTACK_LEN = 50, }; +enum { + /// For conditional commands a stack is kept of nested conditionals. + /// When cs_idx < 0, there is no conditional command. + CSTACK_LEN = 50, +}; typedef struct { int cs_flags[CSTACK_LEN]; ///< CSF_ flags diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 64ef17b157..44a78711d2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -17,9 +17,12 @@ #include "nvim/arabic.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/digraph.h" @@ -34,21 +37,24 @@ #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/extmark.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/map_defs.h" #include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/move.h" @@ -65,16 +71,21 @@ #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/search.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" +#include "nvim/undo_defs.h" #include "nvim/usercmd.h" #include "nvim/vim_defs.h" #include "nvim/viml/parser/expressions.h" #include "nvim/viml/parser/parser.h" +#include "nvim/viml/parser/parser_defs.h" #include "nvim/window.h" /// Last value of prompt_id, incremented when doing new prompt @@ -104,13 +115,13 @@ typedef struct { optmagic_T magic_overruled_save; } incsearch_state_T; -typedef struct command_line_state { +typedef struct { VimState state; int firstc; int count; int indent; int c; - int gotesc; // true when <ESC> just typed + bool gotesc; // true when <ESC> just typed int do_abbr; // when true check for abbr. char *lookfor; // string to match int hiscnt; // current history line in use @@ -133,7 +144,7 @@ typedef struct command_line_state { buf_T *b_im_ptr_buf; ///< buffer where b_im_ptr is valid } CommandLineState; -typedef struct cmdpreview_undo_info { +typedef struct { u_header_T *save_b_u_oldhead; u_header_T *save_b_u_newhead; u_header_T *save_b_u_curhead; @@ -149,15 +160,17 @@ typedef struct cmdpreview_undo_info { colnr_T save_b_u_line_colnr; } CpUndoInfo; -typedef struct cmdpreview_buf_info { +typedef struct { buf_T *buf; OptInt save_b_p_ul; int save_b_changed; + pos_T save_b_op_start; + pos_T save_b_op_end; varnumber_T save_changedtick; CpUndoInfo undo_info; } CpBufInfo; -typedef struct cmdpreview_win_info { +typedef struct { win_T *win; pos_T save_w_cursor; viewstate_T save_viewstate; @@ -165,7 +178,7 @@ typedef struct cmdpreview_win_info { int save_w_p_cuc; } CpWinInfo; -typedef struct cmdpreview_info { +typedef struct { kvec_t(CpWinInfo) win_info; kvec_t(CpBufInfo) buf_info; bool save_hls; @@ -206,6 +219,9 @@ static int cedit_key = -1; ///< key value of 'cedit' option static handle_T cmdpreview_bufnr = 0; static int cmdpreview_ns = 0; +static const char e_active_window_or_buffer_changed_or_deleted[] + = N_("E199: Active window or buffer changed or deleted"); + static void save_viewstate(win_T *wp, viewstate_T *vs) FUNC_ATTR_NONNULL_ALL { @@ -467,7 +483,7 @@ static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state // if interrupted while searching, behave like it failed if (got_int) { - (void)vpeekc(); // remove <C-C> from input stream + vpeekc(); // remove <C-C> from input stream got_int = false; // don't abandon the command line found = 0; } else if (char_avail()) { @@ -583,7 +599,8 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s) return OK; } -static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool call_update_screen) +static void finish_incsearch_highlighting(bool gotesc, incsearch_state_T *s, + bool call_update_screen) { if (!s->did_incsearch) { return; @@ -839,7 +856,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear // error printed below, to avoid redraw issues tl_ret = try_leave(&tstate, &err); if (tv_dict_get_number(dict, "abort") != 0) { - s->gotesc = 1; + s->gotesc = true; } restore_v_event(dict, &save_v_event); } @@ -885,9 +902,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear // not get printed in the middle of it. msg_check(); if (p_ch == 0 && !ui_has(kUIMessages)) { - if (must_redraw < UPD_VALID) { - must_redraw = UPD_VALID; - } + set_must_redraw(UPD_VALID); } msg_scroll = s->save_msg_scroll; redir_off = false; @@ -904,7 +919,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear need_wait_return = false; } - set_string_option_direct("icm", -1, s->save_p_icm, OPT_FREE, SID_NONE); + set_string_option_direct(kOptInccommand, s->save_p_icm, 0, SID_NONE); State = s->save_State; if (cmdpreview != save_cmdpreview) { cmdpreview = save_cmdpreview; // restore preview state @@ -1051,7 +1066,7 @@ static int command_line_wildchar_complete(CommandLineState *s) && !s->did_wild_list && ((wim_flags[s->wim_index] & WIM_LIST) || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0))) { - (void)showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0)); + showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0)); redrawcmd(); s->did_wild_list = true; } @@ -1077,9 +1092,9 @@ static int command_line_wildchar_complete(CommandLineState *s) // if interrupted while completing, behave like it failed if (got_int) { - (void)vpeekc(); // remove <C-C> from input stream + vpeekc(); // remove <C-C> from input stream got_int = false; // don't abandon the command line - (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); + ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); s->xpc.xp_context = EXPAND_NOTHING; return CMDLINE_CHANGED; } @@ -1103,7 +1118,7 @@ static int command_line_wildchar_complete(CommandLineState *s) p_wmnu = p_wmnu_save; } - (void)showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0)); + showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0)); redrawcmd(); s->did_wild_list = true; @@ -1137,7 +1152,7 @@ static void command_line_end_wildmenu(CommandLineState *s) cmdline_pum_remove(); } if (s->xpc.xp_numfiles != -1) { - (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); + ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); } s->did_wild_list = false; if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) { @@ -1249,7 +1264,7 @@ static int command_line_execute(VimState *state, int key) // Ctrl-E: cancel the cmdline popup menu and return the original text. if (s->c == Ctrl_E || s->c == Ctrl_Y) { wild_type = (s->c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY; - (void)nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@'); + nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@'); } } @@ -1418,7 +1433,7 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s if (lt(s->match_start, s->match_end)) { // start searching at the end of the match // not at the beginning of the next column - (void)decl(&t); + decl(&t); } search_flags += SEARCH_COL; } else { @@ -1446,21 +1461,21 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s // when nv_search finishes the cursor will be // put back on the match s->search_start = t; - (void)decl(&s->search_start); + decl(&s->search_start); } else if (next_match && firstc == '?') { // move just after the current match, so that // when nv_search finishes the cursor will be // put back on the match s->search_start = t; - (void)incl(&s->search_start); + incl(&s->search_start); } if (lt(t, s->search_start) && next_match) { // wrap around s->search_start = t; if (firstc == '?') { - (void)incl(&s->search_start); + incl(&s->search_start); } else { - (void)decl(&s->search_start); + decl(&s->search_start); } } @@ -1495,10 +1510,8 @@ static int command_line_erase_chars(CommandLineState *s) if (s->c == K_DEL && ccline.cmdpos != ccline.cmdlen) { ccline.cmdpos++; } - if (s->c == K_DEL) { - ccline.cmdpos += mb_off_next(ccline.cmdbuff, - ccline.cmdbuff + ccline.cmdpos); + ccline.cmdpos += mb_off_next(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos); } if (ccline.cmdpos > 0) { @@ -2175,7 +2188,7 @@ static bool empty_pattern(char *p, int delim) magic_T magic_val = MAGIC_ON; if (n > 0) { - (void)skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val); + skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val); } else { return true; } @@ -2263,6 +2276,7 @@ static buf_T *cmdpreview_open_buf(void) /// /// @return Pointer to command preview window if succeeded, NULL if failed. static win_T *cmdpreview_open_win(buf_T *cmdpreview_buf) + FUNC_ATTR_NONNULL_ALL { win_T *save_curwin = curwin; @@ -2361,6 +2375,8 @@ static void cmdpreview_prepare(CpInfo *cpinfo) cp_bufinfo.buf = buf; cp_bufinfo.save_b_p_ul = buf->b_p_ul; cp_bufinfo.save_b_changed = buf->b_changed; + cp_bufinfo.save_b_op_start = buf->b_op_start; + cp_bufinfo.save_b_op_end = buf->b_op_end; cp_bufinfo.save_changedtick = buf_get_changedtick(buf); cmdpreview_save_undo(&cp_bufinfo.undo_info, buf); kv_push(cpinfo->buf_info, cp_bufinfo); @@ -2439,6 +2455,9 @@ static void cmdpreview_restore_state(CpInfo *cpinfo) u_blockfree(buf); cmdpreview_restore_undo(&cp_bufinfo.undo_info, buf); + buf->b_op_start = cp_bufinfo.save_b_op_start; + buf->b_op_end = cp_bufinfo.save_b_op_end; + if (cp_bufinfo.save_changedtick != buf_get_changedtick(buf)) { buf_set_changedtick(buf, cp_bufinfo.save_changedtick); } @@ -2529,10 +2548,10 @@ static bool cmdpreview_may_show(CommandLineState *s) cmdpreview_prepare(&cpinfo); // Open preview buffer if inccommand=split. - if (!icm_split) { - cmdpreview_bufnr = 0; - } else if ((cmdpreview_buf = cmdpreview_open_buf()) == NULL) { - abort(); + if (icm_split && (cmdpreview_buf = cmdpreview_open_buf()) == NULL) { + // Failed to create preview buffer, so disable preview. + set_string_option_direct(kOptInccommand, "nosplit", 0, SID_NONE); + icm_split = false; } // Setup preview namespace if it's not already set. if (!cmdpreview_ns) { @@ -2627,6 +2646,7 @@ static int command_line_changed(CommandLineState *s) if (s->firstc == ':' && current_sctx.sc_sid == 0 // only if interactive && *p_icm != NUL // 'inccommand' is set + && !exmode_active // not in ex mode && curbuf->b_p_ma // buffer is modifiable && cmdline_star == 0 // not typing a password && !vpeekc_any() @@ -2932,7 +2952,7 @@ char *getexline(int c, void *cookie, int indent, bool do_concat) { // When executing a register, remove ':' that's in front of each line. if (exec_from_reg && vpeekc() == ':') { - (void)vgetc(); + vgetc(); } return getcmdline(c, 1, indent, do_concat); @@ -3447,7 +3467,7 @@ void cmdline_ui_flush(void) // Put a character on the command line. Shifts the following text to the // right when "shift" is true. Used for CTRL-V, CTRL-K, etc. // "c" must be printable (fit in one display cell)! -void putcmdline(char c, int shift) +void putcmdline(char c, bool shift) { if (cmd_silent) { return; @@ -3494,7 +3514,7 @@ void unputcmdline(void) // part will be redrawn, otherwise it will not. If this function is called // twice in a row, then 'redraw' should be false and redrawcmd() should be // called afterwards. -void put_on_cmdline(const char *str, int len, int redraw) +void put_on_cmdline(const char *str, int len, bool redraw) { int i; int m; @@ -3704,7 +3724,7 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) // When "literally" is true, insert literally. // When "literally" is false, insert as typed, but don't leave the command // line. -void cmdline_paste_str(const char *s, int literally) +void cmdline_paste_str(const char *s, bool literally) { if (literally) { put_on_cmdline(s, -1, true); @@ -4200,7 +4220,7 @@ int get_cmdline_firstc(void) int get_list_range(char **str, int *num1, int *num2) { int len; - int first = false; + bool first = false; varnumber_T num; *str = skipwhite((*str)); @@ -4274,7 +4294,7 @@ static int open_cmdwin(void) int save_restart_edit = restart_edit; int save_State = State; bool save_exmode = exmode_active; - int save_cmdmsg_rl = cmdmsg_rl; + bool save_cmdmsg_rl = cmdmsg_rl; // Can't do this when text or buffer is locked. // Can't do this recursively. Can't do it when typing a password. @@ -4302,25 +4322,52 @@ static int open_cmdwin(void) ga_clear(&winsizes); return K_IGNORE; } + // win_split() autocommands may have messed with the old window or buffer. + // Treat it as abandoning this command-line. + if (!win_valid(old_curwin) || curwin == old_curwin || !bufref_valid(&old_curbuf) + || old_curwin->w_buffer != old_curbuf.br_buf) { + beep_flush(); + ga_clear(&winsizes); + return Ctrl_C; + } // Don't let quitting the More prompt make this fail. got_int = false; - // Set "cmdwin_type" before any autocommands may mess things up. + // Set "cmdwin_..." variables before any autocommands may mess things up. cmdwin_type = get_cmdline_type(); cmdwin_level = ccline.level; + cmdwin_win = curwin; cmdwin_old_curwin = old_curwin; - // Create empty command-line buffer. - if (buf_open_scratch(0, _("[Command Line]")) == FAIL) { - // Some autocommand messed it up? - win_close(curwin, true, false); - ga_clear(&winsizes); + // Create empty command-line buffer. Be especially cautious of BufLeave + // autocommands from do_ecmd(), as cmdwin restrictions do not apply to them! + const int newbuf_status = buf_open_scratch(0, NULL); + const bool cmdwin_valid = win_valid(cmdwin_win); + if (newbuf_status == FAIL || !cmdwin_valid || curwin != cmdwin_win || !win_valid(old_curwin) + || !bufref_valid(&old_curbuf) || old_curwin->w_buffer != old_curbuf.br_buf) { + if (newbuf_status == OK) { + set_bufref(&bufref, curbuf); + } + if (cmdwin_valid && !last_window(cmdwin_win)) { + win_close(cmdwin_win, true, false); + } + // win_close() autocommands may have already deleted the buffer. + if (newbuf_status == OK && bufref_valid(&bufref) && bufref.br_buf != curbuf) { + close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false); + } + cmdwin_type = 0; + cmdwin_level = 0; + cmdwin_win = NULL; cmdwin_old_curwin = NULL; + beep_flush(); + ga_clear(&winsizes); return Ctrl_C; } + cmdwin_buf = curbuf; + // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. - set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL); + set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL); curbuf->b_p_ma = true; curwin->w_p_fen = false; curwin->w_p_rl = cmdmsg_rl; @@ -4338,7 +4385,7 @@ static int open_cmdwin(void) add_map("<Tab>", "<C-X><C-V>", MODE_INSERT, true); add_map("<Tab>", "a<C-X><C-V>", MODE_NORMAL, true); } - set_option_value_give_err("ft", STATIC_CSTR_AS_OPTVAL("vim"), OPT_LOCAL); + set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OPTVAL("vim"), OPT_LOCAL); } curbuf->b_ro_locked--; @@ -4413,15 +4460,18 @@ static int open_cmdwin(void) cmdwin_type = 0; cmdwin_level = 0; + cmdwin_buf = NULL; + cmdwin_win = NULL; cmdwin_old_curwin = NULL; exmode_active = save_exmode; - // Safety check: The old window or buffer was deleted: It's a bug when - // this happens! - if (!win_valid(old_curwin) || !bufref_valid(&old_curbuf)) { + // Safety check: The old window or buffer was changed or deleted: It's a bug + // when this happens! + if (!win_valid(old_curwin) || !bufref_valid(&old_curbuf) + || old_curwin->w_buffer != old_curbuf.br_buf) { cmdwin_result = Ctrl_C; - emsg(_("E199: Active window or buffer deleted")); + emsg(_(e_active_window_or_buffer_changed_or_deleted)); } else { win_T *wp; // autocmds may abort script processing diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 93bdd2299f..3e14ea12b8 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -1,76 +1,13 @@ #pragma once -#include <stdbool.h> +#include <stddef.h> // IWYU pragma: keep -#include "klib/kvec.h" -#include "nvim/cmdexpand_defs.h" -#include "nvim/eval/typval_defs.h" +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep +#include "nvim/ex_getln_defs.h" // IWYU pragma: keep #include "nvim/option_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep -/// Command-line colors: one chunk -/// -/// Defines a region which has the same highlighting. -typedef struct { - int start; ///< Colored chunk start. - int end; ///< Colored chunk end (exclusive, > start). - int attr; ///< Highlight attr. -} CmdlineColorChunk; - -/// Command-line colors -/// -/// Holds data about all colors. -typedef kvec_t(CmdlineColorChunk) CmdlineColors; - -/// Command-line coloring -/// -/// Holds both what are the colors and what have been colored. Latter is used to -/// suppress unnecessary calls to coloring callbacks. -typedef struct { - unsigned prompt_id; ///< ID of the prompt which was colored last. - char *cmdbuff; ///< What exactly was colored last time or NULL. - CmdlineColors colors; ///< Last colors. -} ColoredCmdline; - -/// Keeps track how much state must be sent to external ui. -typedef enum { - kCmdRedrawNone, - kCmdRedrawPos, - kCmdRedrawAll, -} CmdRedraw; - -/// Variables shared between getcmdline(), redrawcmdline() and others. -/// These need to be saved when using CTRL-R |, that's why they are in a -/// structure. -typedef struct cmdline_info CmdlineInfo; -struct cmdline_info { - char *cmdbuff; ///< pointer to command line buffer - int cmdbufflen; ///< length of cmdbuff - int cmdlen; ///< number of chars in command line - int cmdpos; ///< current cursor position - int cmdspos; ///< cursor column on screen - int cmdfirstc; ///< ':', '/', '?', '=', '>' or NUL - int cmdindent; ///< number of spaces before cmdline - char *cmdprompt; ///< message in front of cmdline - int cmdattr; ///< attributes for prompt - int overstrike; ///< Typing mode on the command line. Shared by - ///< getcmdline() and put_on_cmdline(). - expand_T *xpc; ///< struct being used for expansion, xp_pattern - ///< may point into cmdbuff - int xp_context; ///< type of expansion - char *xp_arg; ///< user-defined expansion arg - int input_fn; ///< when true Invoked for input() function - unsigned prompt_id; ///< Prompt number, used to disable coloring on errors. - Callback highlight_callback; ///< Callback used for coloring user input. - ColoredCmdline last_colors; ///< Last cmdline colors - int level; ///< current cmdline level - CmdlineInfo *prev_ccline; ///< pointer to saved cmdline state - char special_char; ///< last putcmdline char (used for redraws) - bool special_shift; ///< shift of last putcmdline char - CmdRedraw redraw_state; ///< needed redraw for external cmdline -}; - /// flags used by vim_strsave_fnameescape() enum { VSE_NONE = 0, diff --git a/src/nvim/ex_getln_defs.h b/src/nvim/ex_getln_defs.h new file mode 100644 index 0000000000..daba6cabb8 --- /dev/null +++ b/src/nvim/ex_getln_defs.h @@ -0,0 +1,68 @@ +#pragma once + +#include <stdbool.h> + +#include "klib/kvec.h" +#include "nvim/cmdexpand_defs.h" + +/// Command-line colors: one chunk +/// +/// Defines a region which has the same highlighting. +typedef struct { + int start; ///< Colored chunk start. + int end; ///< Colored chunk end (exclusive, > start). + int attr; ///< Highlight attr. +} CmdlineColorChunk; + +/// Command-line colors +/// +/// Holds data about all colors. +typedef kvec_t(CmdlineColorChunk) CmdlineColors; + +/// Command-line coloring +/// +/// Holds both what are the colors and what have been colored. Latter is used to +/// suppress unnecessary calls to coloring callbacks. +typedef struct { + unsigned prompt_id; ///< ID of the prompt which was colored last. + char *cmdbuff; ///< What exactly was colored last time or NULL. + CmdlineColors colors; ///< Last colors. +} ColoredCmdline; + +/// Keeps track how much state must be sent to external ui. +typedef enum { + kCmdRedrawNone, + kCmdRedrawPos, + kCmdRedrawAll, +} CmdRedraw; + +/// Variables shared between getcmdline(), redrawcmdline() and others. +/// These need to be saved when using CTRL-R |, that's why they are in a +/// structure. +typedef struct cmdline_info CmdlineInfo; +struct cmdline_info { + char *cmdbuff; ///< pointer to command line buffer + int cmdbufflen; ///< length of cmdbuff + int cmdlen; ///< number of chars in command line + int cmdpos; ///< current cursor position + int cmdspos; ///< cursor column on screen + int cmdfirstc; ///< ':', '/', '?', '=', '>' or NUL + int cmdindent; ///< number of spaces before cmdline + char *cmdprompt; ///< message in front of cmdline + int cmdattr; ///< attributes for prompt + int overstrike; ///< Typing mode on the command line. Shared by + ///< getcmdline() and put_on_cmdline(). + expand_T *xpc; ///< struct being used for expansion, xp_pattern + ///< may point into cmdbuff + int xp_context; ///< type of expansion + char *xp_arg; ///< user-defined expansion arg + int input_fn; ///< when true Invoked for input() function + unsigned prompt_id; ///< Prompt number, used to disable coloring on errors. + Callback highlight_callback; ///< Callback used for coloring user input. + ColoredCmdline last_colors; ///< Last cmdline colors + int level; ///< current cmdline level + CmdlineInfo *prev_ccline; ///< pointer to saved cmdline state + char special_char; ///< last putcmdline char (used for redraws) + bool special_shift; ///< shift of last putcmdline char + CmdRedraw redraw_state; ///< needed redraw for external cmdline +}; diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 71c01922bc..fb37bc86f1 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -10,9 +10,13 @@ #include <string.h> #include "nvim/arglist.h" +#include "nvim/arglist_defs.h" #include "nvim/ascii_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -20,21 +24,24 @@ #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" -#include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/mapping.h" +#include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/runtime.h" +#include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -60,7 +67,7 @@ static int put_view_curpos(FILE *fd, const win_T *wp, char *spaces) return r >= 0; } -static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin) +static int ses_winsizes(FILE *fd, bool restore_size, win_T *tab_firstwin) { if (restore_size && (ssop_flags & SSOP_WINSIZE)) { int n = 0; @@ -209,7 +216,7 @@ static int ses_do_win(win_T *wp) /// @param flagp /// /// @returns FAIL if writing fails. -static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, unsigned *flagp) +static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, bool fullname, unsigned *flagp) { char *buf = NULL; @@ -222,7 +229,7 @@ static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, unsigne if (s != NULL) { if (fullname) { buf = xmalloc(MAXPATHL); - (void)vim_FullName(s, buf, MAXPATHL, false); + vim_FullName(s, buf, MAXPATHL, false); s = buf; } char *fname_esc = ses_escape_fname(s, flagp); @@ -315,11 +322,11 @@ static int ses_put_fname(FILE *fd, char *name, unsigned *flagp) static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int current_arg_idx) { int f; - int did_next = false; + bool did_next = false; // Always restore cursor position for ":mksession". For ":mkview" only // when 'viewoptions' contains "cursor". - int do_cursor = (flagp == &ssop_flags || *flagp & SSOP_CURSOR); + bool do_cursor = (flagp == &ssop_flags || *flagp & SSOP_CURSOR); // Local argument list. if (wp->w_alist == &global_alist) { @@ -359,6 +366,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr if (put_line(fd, "enew | setl bt=help") == FAIL || fprintf(fd, "help %s", curtag) < 0 || put_eol(fd) == FAIL) { + xfree(fname_esc); return FAIL; } } else if (wp->w_buffer->b_ffname != NULL @@ -518,6 +526,50 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr return OK; } +static int store_session_globals(FILE *fd) +{ + TV_DICT_ITER(&globvardict, this_var, { + if ((this_var->di_tv.v_type == VAR_NUMBER + || this_var->di_tv.v_type == VAR_STRING) + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + // Escape special characters with a backslash. Turn a LF and + // CR into \n and \r. + char *const p = vim_strsave_escaped(tv_get_string(&this_var->di_tv), "\\\"\n\r"); + for (char *t = p; *t != NUL; t++) { + if (*t == '\n') { + *t = 'n'; + } else if (*t == '\r') { + *t = 'r'; + } + } + if ((fprintf(fd, "let %s = %c%s%c", + this_var->di_key, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' '), + p, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' ')) < 0) + || put_eol(fd) == FAIL) { + xfree(p); + return FAIL; + } + xfree(p); + } else if (this_var->di_tv.v_type == VAR_FLOAT + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + float_T f = this_var->di_tv.vval.v_float; + int sign = ' '; + + if (f < 0) { + f = -f; + sign = '-'; + } + if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0) + || put_eol(fd) == FAIL) { + return FAIL; + } + } + }); + return OK; +} + /// Writes commands for restoring the current buffers, for :mksession. /// /// Legacy 'sessionoptions'/'viewoptions' flags SSOP_UNIX, SSOP_SLASH are @@ -529,8 +581,8 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr /// @return FAIL on error, OK otherwise. static int makeopens(FILE *fd, char *dirnow) { - int only_save_windows = true; - int restore_size = true; + bool only_save_windows = true; + bool restore_size = true; win_T *edited_win = NULL; win_T *tab_firstwin; frame_T *tab_topframe; @@ -628,7 +680,7 @@ static int makeopens(FILE *fd, char *dirnow) } } - int restore_stal = false; + bool restore_stal = false; // When there are two or more tabpages and 'showtabline' is 1 the tabline // will be displayed when creating the next tab. That resizes the windows // in the first tab, which may cause problems. Set 'showtabline' to 2 @@ -887,7 +939,7 @@ void ex_loadview(exarg_T *eap) /// - SSOP_SLASH: filenames are written with "/" slash void ex_mkrc(exarg_T *eap) { - int view_session = false; // :mkview, :mksession + bool view_session = false; // :mkview, :mksession int using_vdir = false; // using 'viewdir'? char *viewFile = NULL; @@ -928,7 +980,7 @@ void ex_mkrc(exarg_T *eap) FILE *fd = open_exfile(fname, eap->forceit, WRITEBIN); if (fd != NULL) { - int failed = false; + bool failed = false; unsigned *flagp; if (eap->cmdidx == CMD_mkview) { flagp = &vop_flags; @@ -938,7 +990,7 @@ void ex_mkrc(exarg_T *eap) // Write the version command for :mkvimrc if (eap->cmdidx == CMD_mkvimrc) { - (void)put_line(fd, "version 6.0"); + put_line(fd, "version 6.0"); } if (eap->cmdidx == CMD_mksession) { diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index d9c1993f32..c4a34f8019 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -32,14 +32,18 @@ #include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/extmark.h" #include "nvim/extmark_defs.h" #include "nvim/globals.h" #include "nvim/map_defs.h" #include "nvim/marktree.h" #include "nvim/memline.h" +#include "nvim/memory.h" #include "nvim/pos_defs.h" +#include "nvim/types_defs.h" #include "nvim/undo.h" +#include "nvim/undo_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "extmark.c.generated.h" @@ -50,12 +54,12 @@ /// must not be used during iteration! void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col, int end_row, colnr_T end_col, DecorInline decor, uint16_t decor_flags, bool right_gravity, - bool end_right_gravity, bool no_undo, bool invalidate, Error *err) + bool end_right_gravity, bool no_undo, bool invalidate, bool scoped, Error *err) { uint32_t *ns = map_put_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, NULL, NULL); uint32_t id = idp ? *idp : 0; - uint16_t flags = mt_flags(right_gravity, no_undo, invalidate, decor.ext) | decor_flags; + uint16_t flags = mt_flags(right_gravity, no_undo, invalidate, decor.ext, scoped) | decor_flags; if (id == 0) { id = ++*ns; } else { @@ -66,19 +70,27 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col extmark_del_id(buf, ns_id, id); } else { assert(marktree_itr_valid(itr)); + bool invalid = mt_invalid(old_mark); if (old_mark.pos.row == row && old_mark.pos.col == col) { - if (mt_decor_any(old_mark)) { - buf_decor_remove(buf, row, row, mt_decor(old_mark), true); - } - // not paired: we can revise in place - mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_EXTERNAL_MASK; - mt_itr_rawkey(itr).flags |= flags; + if (!invalid && mt_decor_any(old_mark)) { + // TODO(bfredl): conflict of concerns: buf_decor_remove() must process + // the buffer as if MT_FLAG_DECOR_SIGNTEXT is already removed, however + // marktree must precisely adjust the set of flags from the old set to the new + uint16_t save_flags = mt_itr_rawkey(itr).flags; + mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_DECOR_SIGNTEXT; + buf_decor_remove(buf, row, row, col, mt_decor(old_mark), true); + mt_itr_rawkey(itr).flags = save_flags; + } + marktree_revise_flags(buf->b_marktree, itr, flags); mt_itr_rawkey(itr).decor_data = decor.data; goto revised; } - buf_decor_remove(buf, old_mark.pos.row, old_mark.pos.row, mt_decor(old_mark), true); marktree_del_itr(buf->b_marktree, itr, false); + if (!invalid) { + buf_decor_remove(buf, old_mark.pos.row, old_mark.pos.row, old_mark.pos.col, + mt_decor(old_mark), true); + } } } else { *ns = MAX(*ns, id); @@ -92,7 +104,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col revised: if (decor_flags || decor.ext) { buf_put_decor(buf, decor, row, end_row > -1 ? end_row : row); - decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); + decor_redraw(buf, row, end_row > -1 ? end_row : row, col, decor); } if (idp) { @@ -100,20 +112,39 @@ revised: } } -static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) +static void extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col, bool invalid) { MarkTreeIter itr[1] = { 0 }; MTKey key = marktree_lookup(buf->b_marktree, mark, itr); - if (key.pos.row == -1) { - return false; + if (key.pos.row < 0 || (key.pos.row == row && key.pos.col == col)) { + return; + } + + // Only the position before undo needs to be redrawn here, + // as the position after undo should be marked as changed. + if (!invalid && mt_decor_any(key) && key.pos.row != row) { + decor_redraw(buf, key.pos.row, key.pos.row, key.pos.col, mt_decor(key)); } - if (key.pos.row == row && key.pos.col == col) { - return true; + int row1 = 0; + int row2 = 0; + if (invalid) { + mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_INVALID; + } else if (key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) { + MTPos end = marktree_get_altpos(buf->b_marktree, key, NULL); + row1 = MIN(end.row, MIN(key.pos.row, row)); + row2 = MAX(end.row, MAX(key.pos.row, row)); + buf_signcols_count_range(buf, row1, row2, 0, kTrue); } marktree_move(buf->b_marktree, itr, row, col); - return true; + + if (invalid) { + MTPos end = marktree_get_altpos(buf->b_marktree, key, NULL); + buf_put_decor(buf, mt_decor(key), row, end.row); + } else if (key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) { + buf_signcols_count_range(buf, row1, row2, 0, kNone); + } } /// Remove an extmark in "ns_id" by "id" @@ -147,7 +178,11 @@ void extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore) } if (mt_decor_any(key)) { - buf_decor_remove(buf, key.pos.row, key2.pos.row, mt_decor(key), true); + if (mt_invalid(key)) { + decor_free(mt_decor(key)); + } else { + buf_decor_remove(buf, key.pos.row, key2.pos.row, key.pos.col, mt_decor(key), true); + } } // TODO(bfredl): delete it from current undo header, opportunistically? @@ -218,7 +253,7 @@ ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_co } else { // Find all the marks beginning with the start position marktree_itr_get_ext(buf->b_marktree, MTPos(l_row, l_col), - itr, reverse, false, NULL); + itr, reverse, false, NULL, NULL); } int order = reverse ? -1 : 1; @@ -302,6 +337,9 @@ void extmark_free_all(buf_T *buf) marktree_clear(buf->b_marktree); + buf->b_signcols.max = 0; + CLEAR_FIELD(buf->b_signcols.count); + map_destroy(uint32_t, buf->b_extmark_ns); *buf->b_extmark_ns = (Map(uint32_t, uint32_t)) MAP_INIT; } @@ -311,9 +349,8 @@ void extmark_free_all(buf_T *buf) /// copying is useful when we cannot simply reverse the operation. This will do /// nothing on redo, enforces correct position when undo. void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, colnr_T u_col, - ExtmarkOp op) + extmark_undo_vec_t *uvp, bool only_copy, ExtmarkOp op) { - u_header_T *uhp = u_force_get_undo_header(buf); MarkTreeIter itr[1] = { 0 }; ExtmarkUndoObject undo; @@ -328,38 +365,37 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln bool invalidated = false; // Invalidate/delete mark - if (!mt_invalid(mark) && mt_invalidate(mark) && !mt_end(mark)) { + if (!only_copy && !mt_invalid(mark) && mt_invalidate(mark) && !mt_end(mark)) { MTPos endpos = marktree_get_altpos(buf->b_marktree, mark, NULL); - if (endpos.row < 0) { - endpos = mark.pos; - } - if ((endpos.col <= u_col || (!u_col && endpos.row == mark.pos.row)) - && mark.pos.col >= l_col - && mark.pos.row >= l_row && endpos.row <= u_row - (u_col ? 0 : 1)) { + // Invalidate unpaired marks in deleted lines and paired marks whose entire + // range has been deleted. + if ((!mt_paired(mark) && mark.pos.row < u_row) + || (mt_paired(mark) + && (endpos.col <= u_col || (!u_col && endpos.row == mark.pos.row)) + && mark.pos.col >= l_col + && mark.pos.row >= l_row && endpos.row <= u_row - (u_col ? 0 : 1))) { if (mt_no_undo(mark)) { extmark_del(buf, itr, mark, true); continue; } else { invalidated = true; mt_itr_rawkey(itr).flags |= MT_FLAG_INVALID; - buf_decor_remove(buf, mark.pos.row, endpos.row, mt_decor(mark), false); + buf_decor_remove(buf, mark.pos.row, endpos.row, mark.pos.col, mt_decor(mark), false); } } } // Push mark to undo header - if (uhp && op == kExtmarkUndo && !mt_no_undo(mark)) { - ExtmarkSavePos pos; - pos.mark = mt_lookup_key(mark); - pos.invalidated = invalidated; - pos.old_row = mark.pos.row; - pos.old_col = mark.pos.col; - pos.row = -1; - pos.col = -1; - + if (only_copy || (uvp != NULL && op == kExtmarkUndo && !mt_no_undo(mark))) { + ExtmarkSavePos pos = { + .mark = mt_lookup_key(mark), + .invalidated = invalidated, + .old_row = mark.pos.row, + .old_col = mark.pos.col + }; undo.data.savepos = pos; undo.type = kExtmarkSavePos; - kv_push(uhp->uh_extmark, undo); + kv_push(*uvp, undo); } marktree_itr_next(buf->b_marktree, itr); @@ -389,23 +425,10 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) // kExtmarkSavePos } else if (undo_info.type == kExtmarkSavePos) { ExtmarkSavePos pos = undo_info.data.savepos; - if (undo) { - if (pos.invalidated) { - MarkTreeIter itr[1] = { 0 }; - MTKey mark = marktree_lookup(curbuf->b_marktree, pos.mark, itr); - mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_INVALID; - MTPos end = marktree_get_altpos(curbuf->b_marktree, mark, itr); - buf_put_decor(curbuf, mt_decor(mark), mark.pos.row, end.row < 0 ? mark.pos.row : end.row); - } - if (pos.old_row >= 0) { - extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col); - } - // Redo - } else { - if (pos.row >= 0) { - extmark_setraw(curbuf, pos.mark, pos.row, pos.col); - } + if (undo && pos.old_row >= 0) { + extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col, pos.invalidated); } + // No Redo since kExtmarkSplice will move marks back } else if (undo_info.type == kExtmarkMove) { ExtmarkMove move = undo_info.data.move; if (undo) { @@ -432,8 +455,10 @@ void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount, return; } bcount_t start_byte = ml_find_line_or_offset(buf, line1, NULL, true); - bcount_t old_byte = 0, new_byte = 0; - int old_row, new_row; + bcount_t old_byte = 0; + bcount_t new_byte = 0; + int old_row; + int new_row; if (amount == MAXLNUM) { old_row = line2 - line1 + 1; // TODO(bfredl): ej kasta? @@ -511,27 +536,24 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t // merge!) int end_row = start_row + old_row; int end_col = (old_row ? 0 : start_col) + old_col; - extmark_splice_delete(buf, start_row, start_col, end_row, end_col, undo); + u_header_T *uhp = u_force_get_undo_header(buf); + extmark_undo_vec_t *uvp = uhp ? &uhp->uh_extmark : NULL; + extmark_splice_delete(buf, start_row, start_col, end_row, end_col, uvp, false, undo); } - // Move the signcolumn sentinel line - if (buf->b_signs_with_text && buf->b_signcols.sentinel) { - linenr_T se_lnum = buf->b_signcols.sentinel; - if (se_lnum >= start_row) { - if (old_row != 0 && se_lnum > old_row + start_row) { - buf->b_signcols.sentinel += new_row - old_row; - } else if (new_row == 0) { - buf->b_signcols.sentinel = 0; - } else { - buf->b_signcols.sentinel += new_row; - } - } + // Remove signs inside edited region from "b_signcols.count", add after splicing. + if (old_row > 0 || new_row > 0) { + buf_signcols_count_range(buf, start_row, start_row + old_row, 0, kTrue); } marktree_splice(buf->b_marktree, (int32_t)start_row, start_col, old_row, old_col, new_row, new_col); + if (old_row > 0 || new_row > 0) { + buf_signcols_count_range(buf, start_row, start_row + new_row, 0, kNone); + } + if (undo == kExtmarkUndo) { u_header_T *uhp = u_force_get_undo_header(buf); if (!uhp) { @@ -610,10 +632,16 @@ void extmark_move_region(buf_T *buf, int start_row, colnr_T start_col, bcount_t extent_row, extent_col, extent_byte, 0, 0, 0); + int row1 = MIN(start_row, new_row); + int row2 = MAX(start_row, new_row) + extent_row; + buf_signcols_count_range(buf, row1, row2, 0, kTrue); + marktree_move_region(buf->b_marktree, start_row, start_col, extent_row, extent_col, new_row, new_col); + buf_signcols_count_range(buf, row1, row2, 0, kNone); + buf_updates_send_splice(buf, new_row, new_col, new_byte, 0, 0, 0, extent_row, extent_col, extent_byte); diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h index 061cd0ed5f..b1ef5cf214 100644 --- a/src/nvim/extmark.h +++ b/src/nvim/extmark.h @@ -1,19 +1,16 @@ #pragma once #include <stdbool.h> -#include <stddef.h> #include <stdint.h> #include "klib/kvec.h" -#include "nvim/buffer_defs.h" -#include "nvim/decoration.h" -#include "nvim/extmark_defs.h" // IWYU pragma: export +#include "nvim/extmark_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" -#include "nvim/marktree.h" +#include "nvim/marktree_defs.h" #include "nvim/pos_defs.h" -#include "nvim/types_defs.h" +#include "nvim/types_defs.h" // IWYU pragma: keep -EXTERN int extmark_splice_pending INIT( = 0); +EXTERN int curbuf_splice_pending INIT( = 0); typedef kvec_t(MTPair) ExtmarkInfoArray; @@ -48,8 +45,6 @@ typedef struct { uint64_t mark; // raw mark id of the marktree int old_row; colnr_T old_col; - int row; - colnr_T col; bool invalidated; } ExtmarkSavePos; diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h index c5a8684545..71150cf07c 100644 --- a/src/nvim/extmark_defs.h +++ b/src/nvim/extmark_defs.h @@ -1,7 +1,6 @@ #pragma once #include "klib/kvec.h" -#include "nvim/types_defs.h" // TODO(bfredl): good enough name for now. typedef ptrdiff_t bcount_t; diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 460cd48fc5..ed4848b402 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -51,10 +51,13 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/file_search.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" @@ -63,8 +66,10 @@ #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/strings.h" #include "nvim/vim_defs.h" @@ -156,7 +161,7 @@ typedef struct ff_visited_list_hdr { // ffsc_stopdirs_v: array of stop directories for upward search // ffsc_find_what: FINDFILE_BOTH, FINDFILE_DIR or FINDFILE_FILE // ffsc_tagfile: searching for tags file, don't use 'suffixesadd' -typedef struct ff_search_ctx_T { +typedef struct { ff_stack_T *ffsc_stack_ptr; ff_visited_list_hdr_T *ffsc_visited_list; ff_visited_list_hdr_T *ffsc_dir_visited_list; @@ -237,7 +242,6 @@ static const char e_path_too_long_for_completion[] void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, int free_visited, int find_what, void *search_ctx_arg, int tagfile, char *rel_fname) { - char *wc_part; ff_stack_T *sptr; ff_search_ctx_T *search_ctx; @@ -308,7 +312,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i goto error_return; } path += 2; - } else // NOLINT(readability/braces) + } else #endif if (os_dirname(ff_expand_buffer, MAXPATHL) == FAIL) { goto error_return; @@ -372,7 +376,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i // split into: // -fix path // -wildcard_stuff (might be NULL) - wc_part = vim_strchr(path, '*'); + char *wc_part = vim_strchr(path, '*'); if (wc_part != NULL) { int64_t llevel; int len; @@ -548,24 +552,22 @@ void vim_findfile_cleanup(void *ctx) /// NULL if nothing found. char *vim_findfile(void *search_ctx_arg) { - char *file_path; char *rest_of_wildcards; char *path_end = NULL; ff_stack_T *stackp = NULL; size_t len; char *p; char *suf; - ff_search_ctx_T *search_ctx; if (search_ctx_arg == NULL) { return NULL; } - search_ctx = (ff_search_ctx_T *)search_ctx_arg; + ff_search_ctx_T *search_ctx = (ff_search_ctx_T *)search_ctx_arg; // filepath is used as buffer for various actions and as the storage to // return a found filename. - file_path = xmalloc(MAXPATHL); + char *file_path = xmalloc(MAXPATHL); // store the end of the start dir -- needed for upward search if (search_ctx->ffsc_start_dir != NULL) { @@ -882,7 +884,7 @@ char *vim_findfile(void *search_ctx_arg) // is the last starting directory in the stop list? if (ff_path_in_stoplist(search_ctx->ffsc_start_dir, (int)(path_end - search_ctx->ffsc_start_dir), - search_ctx->ffsc_stopdirs_v) == true) { + search_ctx->ffsc_stopdirs_v)) { break; } @@ -928,13 +930,11 @@ fail: /// Can handle it if the passed search_context is NULL; void vim_findfile_free_visited(void *search_ctx_arg) { - ff_search_ctx_T *search_ctx; - if (search_ctx_arg == NULL) { return; } - search_ctx = (ff_search_ctx_T *)search_ctx_arg; + ff_search_ctx_T *search_ctx = (ff_search_ctx_T *)search_ctx_arg; vim_findfile_free_visited_list(&search_ctx->ffsc_visited_lists_list); vim_findfile_free_visited_list(&search_ctx->ffsc_dir_visited_lists_list); } @@ -1156,9 +1156,7 @@ static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr) /// @return NULL if stack is empty. static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx) { - ff_stack_T *sptr; - - sptr = search_ctx->ffsc_stack_ptr; + ff_stack_T *sptr = search_ctx->ffsc_stack_ptr; if (search_ctx->ffsc_stack_ptr != NULL) { search_ctx->ffsc_stack_ptr = search_ctx->ffsc_stack_ptr->ffs_prev; } @@ -1221,10 +1219,8 @@ static void ff_clear(ff_search_ctx_T *search_ctx) /// check if the given path is in the stopdirs /// /// @return true if yes else false -static int ff_path_in_stoplist(char *path, int path_len, char **stopdirs_v) +static bool ff_path_in_stoplist(char *path, int path_len, char **stopdirs_v) { - int i = 0; - // eat up trailing path separators, except the first while (path_len > 1 && vim_ispathsep(path[path_len - 1])) { path_len--; @@ -1235,7 +1231,7 @@ static int ff_path_in_stoplist(char *path, int path_len, char **stopdirs_v) return true; } - for (i = 0; stopdirs_v[i] != NULL; i++) { + for (int i = 0; stopdirs_v[i] != NULL; i++) { if ((int)strlen(stopdirs_v[i]) > path_len) { // match for parent directory. So '/home' also matches // '/home/rks'. Check for PATHSEP in stopdirs_v[i], else @@ -1338,11 +1334,9 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch { ff_search_ctx_T **search_ctx = (ff_search_ctx_T **)search_ctx_arg; static char *dir; - static int did_findfile_init = false; - char save_char; + static bool did_findfile_init = false; char *file_name = NULL; char *buf = NULL; - int rel_to_curdir; if (rel_fname != NULL && path_with_url(rel_fname)) { // Do not attempt to search "relative" to a URL. #6009 @@ -1355,7 +1349,7 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch } // copy file name into NameBuff, expanding environment variables - save_char = ptr[len]; + char save_char = ptr[len]; ptr[len] = NUL; expand_env_esc(ptr, NameBuff, MAXPATHL, false, true, NULL); ptr[len] = save_char; @@ -1372,12 +1366,12 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch } } - rel_to_curdir = ((*file_to_find)[0] == '.' - && ((*file_to_find)[1] == NUL - || vim_ispathsep((*file_to_find)[1]) - || ((*file_to_find)[1] == '.' - && ((*file_to_find)[2] == NUL - || vim_ispathsep((*file_to_find)[2]))))); + bool rel_to_curdir = ((*file_to_find)[0] == '.' + && ((*file_to_find)[1] == NUL + || vim_ispathsep((*file_to_find)[1]) + || ((*file_to_find)[1] == '.' + && ((*file_to_find)[2] == NUL + || vim_ispathsep((*file_to_find)[2]))))); if (vim_isAbsName(*file_to_find) // "..", "../path", "." and "./path": don't use the path_option || rel_to_curdir diff --git a/src/nvim/file_search.h b/src/nvim/file_search.h index d4b5c5d352..2450472681 100644 --- a/src/nvim/file_search.h +++ b/src/nvim/file_search.h @@ -1,14 +1,16 @@ #pragma once -#include <stdlib.h> +#include <stddef.h> // IWYU pragma: keep -#include "nvim/globals.h" #include "nvim/types_defs.h" // IWYU pragma: keep +#include "nvim/vim_defs.h" // IWYU pragma: keep -// Flags for find_file_*() functions. -#define FINDFILE_FILE 0 // only files -#define FINDFILE_DIR 1 // only directories -#define FINDFILE_BOTH 2 // files and directories +/// Flags for find_file_*() functions. +enum { + FINDFILE_FILE = 0, ///< only files + FINDFILE_DIR = 1, ///< only directories + FINDFILE_BOTH = 2, ///< files and directories +}; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "file_search.h.generated.h" diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 0bb664bcf5..3b715e2c0b 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -18,6 +18,7 @@ #include "auto/config.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" @@ -31,23 +32,27 @@ #include "nvim/ex_eval.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/iconv_defs.h" #include "nvim/log.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memfile.h" +#include "nvim/memfile_defs.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fs.h" @@ -58,6 +63,7 @@ #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/sha256.h" #include "nvim/shada.h" #include "nvim/state_defs.h" @@ -65,6 +71,7 @@ #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/undo_defs.h" #include "nvim/vim_defs.h" #ifdef BACKSLASH_IN_FILENAME @@ -158,14 +165,12 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, { int retval = FAIL; // jump to "theend" instead of returning int fd = stdin_fd >= 0 ? stdin_fd : 0; - int newfile = (flags & READ_NEW); - int check_readonly; - int filtering = (flags & READ_FILTER); - int read_stdin = (flags & READ_STDIN); - int read_buffer = (flags & READ_BUFFER); - int read_fifo = (flags & READ_FIFO); - int set_options = newfile || read_buffer - || (eap != NULL && eap->read_edit); + bool newfile = (flags & READ_NEW); + bool filtering = (flags & READ_FILTER); + bool read_stdin = (flags & READ_STDIN); + bool read_buffer = (flags & READ_BUFFER); + bool read_fifo = (flags & READ_FIFO); + bool set_options = newfile || read_buffer || (eap != NULL && eap->read_edit); linenr_T read_buf_lnum = 1; // next line to read from curbuf colnr_T read_buf_col = 0; // next char to read from this line char c; @@ -181,7 +186,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, off_T filesize = 0; bool skip_read = false; context_sha256_T sha_ctx; - int read_undo_file = false; + bool read_undo_file = false; int split = 0; // number of split lines linenr_T linecnt; bool error = false; // errors encountered @@ -200,7 +205,6 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, linenr_T read_no_eol_lnum = 0; // non-zero lnum when last line of // last read was missing the eol bool file_rewind = false; - int can_retry; linenr_T conv_error = 0; // line nr with conversion error linenr_T illegal_byte = 0; // line nr with illegal byte bool keep_dest_enc = false; // don't retry when char doesn't fit @@ -388,7 +392,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, // Default is r/w, can be set to r/o below. // Don't reset it when in readonly mode // Only set/reset b_p_ro when BF_CHECK_RO is set. - check_readonly = (newfile && (curbuf->b_flags & BF_CHECK_RO)); + bool check_readonly = (newfile && (curbuf->b_flags & BF_CHECK_RO)); if (check_readonly && !readonlymode) { curbuf->b_p_ro = false; } @@ -559,7 +563,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, } } - (void)os_setperm(swap_fname, swap_mode); + os_setperm(swap_fname, swap_mode); } #endif } @@ -853,7 +857,7 @@ retry: // Set "can_retry" when it's possible to rewind the file and try with // another "fenc" value. It's false when no other "fenc" to try, reading // stdin or fixed at a specific encoding. - can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc && !read_fifo); + bool can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc && !read_fifo); if (!skip_read) { linerest = 0; @@ -1151,7 +1155,6 @@ retry: if (fio_flags != 0) { unsigned u8c; - char *dest; char *tail = NULL; // Convert Unicode or Latin1 to UTF-8. @@ -1159,7 +1162,7 @@ retry: // of bytes may increase. // "dest" points to after where the UTF-8 bytes go, "p" points // to after the next character to convert. - dest = ptr + real_size; + char *dest = ptr + real_size; if (fio_flags == FIO_LATIN1 || fio_flags == FIO_UTF8) { p = (uint8_t *)ptr + size; if (fio_flags == FIO_UTF8) { @@ -1315,7 +1318,7 @@ retry: assert(u8c <= INT_MAX); // produce UTF-8 dest -= utf_char2len((int)u8c); - (void)utf_char2bytes((int)u8c, dest); + utf_char2bytes((int)u8c, dest); } // move the linerest to before the converted characters @@ -1532,8 +1535,7 @@ rewind_retry: // Otherwise give an error message later. if (try_unix && !read_stdin - && (read_buffer - || vim_lseek(fd, 0, SEEK_SET) == 0)) { + && (read_buffer || vim_lseek(fd, 0, SEEK_SET) == 0)) { fileformat = EOL_UNIX; if (set_options) { set_fileformat(EOL_UNIX, OPT_LOCAL); @@ -1618,7 +1620,7 @@ failed: save_file_ff(curbuf); // If editing a new file: set 'fenc' for the current buffer. // Also for ":read ++edit file". - set_string_option_direct("fenc", -1, fenc, OPT_FREE | OPT_LOCAL, 0); + set_string_option_direct(kOptFileencoding, fenc, OPT_LOCAL, 0); } if (fenc_alloced) { xfree(fenc); @@ -1630,7 +1632,7 @@ failed: if (!read_buffer && !read_stdin) { close(fd); // errors are ignored } else { - (void)os_set_cloexec(fd); + os_set_cloexec(fd); } xfree(buffer); @@ -1938,7 +1940,7 @@ void prep_exarg(exarg_T *eap, const buf_T *buf) } /// Set default or forced 'fileformat' and 'binary'. -void set_file_options(int set_options, exarg_T *eap) +void set_file_options(bool set_options, exarg_T *eap) { // set default 'fileformat' if (set_options) { @@ -1966,7 +1968,7 @@ void set_forced_fenc(exarg_T *eap) } char *fenc = enc_canonize(eap->cmd + eap->force_enc); - set_string_option_direct("fenc", -1, fenc, OPT_FREE|OPT_LOCAL, 0); + set_string_option_direct(kOptFileencoding, fenc, OPT_LOCAL, 0); xfree(fenc); } @@ -2098,7 +2100,7 @@ int set_rw_fname(char *fname, char *sfname) // Do filetype detection now if 'filetype' is empty. if (*curbuf->b_p_ft == NUL) { if (augroup_exists("filetypedetect")) { - (void)do_doautocmd("filetypedetect BufRead", false, NULL); + do_doautocmd("filetypedetect BufRead", false, NULL); } do_modelines(0); } @@ -2199,7 +2201,7 @@ bool time_differs(const FileInfo *file_info, int64_t mtime, int64_t mtime_ns) bool need_conversion(const char *fenc) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - int same_encoding; + bool same_encoding; int fenc_flags; if (*fenc == NUL || strcmp(p_enc, fenc) == 0) { @@ -2627,7 +2629,7 @@ static int rename_with_tmp(const char *const from, const char *const to) STRCPY(tempname, from); for (int n = 123; n < 99999; n++) { char *tail = path_tail(tempname); - snprintf(tail, (size_t)((MAXPATHL + 1) - (tail - tempname - 1)), "%d", n); + snprintf(tail, (size_t)((MAXPATHL + 1) - (tail - tempname)), "%d", n); if (!os_path_exists(tempname)) { if (os_rename(from, tempname) == OK) { @@ -2636,7 +2638,7 @@ static int rename_with_tmp(const char *const from, const char *const to) } // Strange, the second step failed. Try moving the // file back and return failure. - (void)os_rename(tempname, from); + os_rename(tempname, from); return -1; } // If it fails for one temp name it will most likely fail @@ -2755,7 +2757,7 @@ int vim_rename(const char *from, const char *to) return 0; } -static int already_warned = false; +static bool already_warned = false; /// Check if any not hidden buffer has been changed. /// Postpone the check if there are characters in the stuff buffer, a global @@ -3047,7 +3049,7 @@ int buf_check_timestamp(buf_T *buf) msg_puts_attr(mesg2, HL_ATTR(HLF_W) + MSG_HIST); } msg_clr_eos(); - (void)msg_end(); + msg_end(); if (emsg_silent == 0 && !in_assert_fails) { ui_flush(); // give the user some time to think about it @@ -3078,7 +3080,7 @@ int buf_check_timestamp(buf_T *buf) // Trigger FileChangedShell when the file was changed in any way. if (bufref_valid(&bufref) && retval != 0) { - (void)apply_autocmds(EVENT_FILECHANGEDSHELLPOST, buf->b_fname, buf->b_fname, false, buf); + apply_autocmds(EVENT_FILECHANGEDSHELLPOST, buf->b_fname, buf->b_fname, false, buf); } return retval; } @@ -3151,7 +3153,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) curbuf->b_flags |= BF_CHECK_RO; // check for RO again keep_filetype = true; // don't detect 'filetype' if (readfile(buf->b_ffname, buf->b_fname, 0, 0, - (linenr_T)MAXLNUM, &ea, flags, false) != OK) { + (linenr_T)MAXLNUM, &ea, flags, shortmess(SHM_FILEINFO)) != OK) { if (!aborting()) { semsg(_("E321: Could not reload \"%s\""), buf->b_fname); } @@ -3163,14 +3165,13 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) break; } } - (void)move_lines(savebuf, buf); + move_lines(savebuf, buf); } } else if (buf == curbuf) { // "buf" still valid. // Mark the buffer as unmodified and free undo info. unchanged(buf, true, true); if ((flags & READ_KEEP_UNDO) == 0) { - u_blockfree(buf); - u_clearall(buf); + u_clearallandblockfree(buf); } else { // Mark all undo states as changed. u_unchanged(curbuf); @@ -3278,7 +3279,7 @@ static void vim_mktempdir(void) char user[40] = { 0 }; char appname[40] = { 0 }; - (void)os_get_username(user, sizeof(user)); + os_get_username(user, sizeof(user)); // Usernames may contain slashes! #19240 memchrsub(user, '/', '_', sizeof(user)); memchrsub(user, '\\', '_', sizeof(user)); @@ -3304,7 +3305,7 @@ static void vim_mktempdir(void) xstrlcat(tmp, appname, sizeof(tmp)); xstrlcat(tmp, ".", sizeof(tmp)); xstrlcat(tmp, user, sizeof(tmp)); - (void)os_mkdir(tmp, 0700); // Always create, to avoid a race. + os_mkdir(tmp, 0700); // Always create, to avoid a race. bool owned = os_file_owned(tmp); bool isdir = os_isdir(tmp); #ifdef UNIX @@ -3346,7 +3347,7 @@ static void vim_mktempdir(void) // Couldn't set `vim_tempdir` to `path` so remove created directory. os_rmdir(path); } - (void)umask(umask_save); + umask(umask_save); } /// Core part of "readdir()" function. diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h index d1f6561507..26161c03ba 100644 --- a/src/nvim/fileio.h +++ b/src/nvim/fileio.h @@ -4,48 +4,54 @@ #include <stdio.h> // IWYU pragma: keep #include <time.h> // IWYU pragma: keep -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/garray_defs.h" // IWYU pragma: keep -#include "nvim/globals.h" #include "nvim/os/fs_defs.h" // IWYU pragma: keep -#include "nvim/pos_defs.h" - -// Values for readfile() flags -#define READ_NEW 0x01 // read a file into a new buffer -#define READ_FILTER 0x02 // read filter output -#define READ_STDIN 0x04 // read from stdin -#define READ_BUFFER 0x08 // read from curbuf (converting stdin) -#define READ_DUMMY 0x10 // reading into a dummy buffer -#define READ_KEEP_UNDO 0x20 // keep undo info -#define READ_FIFO 0x40 // read from fifo or socket -#define READ_NOWINENTER 0x80 // do not trigger BufWinEnter -#define READ_NOFILE 0x100 // do not read a file, do trigger BufReadCmd +#include "nvim/os/os_defs.h" // IWYU pragma: keep +#include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep + +/// Values for readfile() flags +enum { + READ_NEW = 0x01, ///< read a file into a new buffer + READ_FILTER = 0x02, ///< read filter output + READ_STDIN = 0x04, ///< read from stdin + READ_BUFFER = 0x08, ///< read from curbuf (converting stdin) + READ_DUMMY = 0x10, ///< reading into a dummy buffer + READ_KEEP_UNDO = 0x20, ///< keep undo info + READ_FIFO = 0x40, ///< read from fifo or socket + READ_NOWINENTER = 0x80, ///< do not trigger BufWinEnter + READ_NOFILE = 0x100, ///< do not read a file, do trigger BufReadCmd +}; typedef varnumber_T (*CheckItem)(void *expr, const char *name); enum { - FIO_LATIN1 = 0x01, // convert Latin1 - FIO_UTF8 = 0x02, // convert UTF-8 - FIO_UCS2 = 0x04, // convert UCS-2 - FIO_UCS4 = 0x08, // convert UCS-4 - FIO_UTF16 = 0x10, // convert UTF-16 - FIO_ENDIAN_L = 0x80, // little endian - FIO_NOCONVERT = 0x2000, // skip encoding conversion - FIO_UCSBOM = 0x4000, // check for BOM at start of file - FIO_ALL = -1, // allow all formats + FIO_LATIN1 = 0x01, ///< convert Latin1 + FIO_UTF8 = 0x02, ///< convert UTF-8 + FIO_UCS2 = 0x04, ///< convert UCS-2 + FIO_UCS4 = 0x08, ///< convert UCS-4 + FIO_UTF16 = 0x10, ///< convert UTF-16 + FIO_ENDIAN_L = 0x80, ///< little endian + FIO_NOCONVERT = 0x2000, ///< skip encoding conversion + FIO_UCSBOM = 0x4000, ///< check for BOM at start of file + FIO_ALL = -1, ///< allow all formats }; -// When converting, a read() or write() may leave some bytes to be converted -// for the next call. The value is guessed... -#define CONV_RESTLEN 30 +enum { + /// When converting, a read() or write() may leave some bytes to be converted + /// for the next call. The value is guessed... + CONV_RESTLEN = 30, +}; -#define WRITEBUFSIZE 8192 // size of normal write buffer +enum { WRITEBUFSIZE = 8192, }; ///< size of normal write buffer -// We have to guess how much a sequence of bytes may expand when converting -// with iconv() to be able to allocate a buffer. -#define ICONV_MULT 8 +enum { + /// We have to guess how much a sequence of bytes may expand when converting + /// with iconv() to be able to allocate a buffer. + ICONV_MULT = 8, +}; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "fileio.h.generated.h" diff --git a/src/nvim/fold.c b/src/nvim/fold.c index c905b2d3ed..c571aaf0a4 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -26,20 +26,22 @@ #include "nvim/eval/typval.h" #include "nvim/ex_session.h" #include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/indent.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/ops.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/os/input.h" #include "nvim/plines.h" @@ -320,42 +322,42 @@ foldinfo_T fold_info(win_T *win, linenr_T lnum) // foldmethodIsManual() {{{2 /// @return true if 'foldmethod' is "manual" -int foldmethodIsManual(win_T *wp) +bool foldmethodIsManual(win_T *wp) { return wp->w_p_fdm[3] == 'u'; } // foldmethodIsIndent() {{{2 /// @return true if 'foldmethod' is "indent" -int foldmethodIsIndent(win_T *wp) +bool foldmethodIsIndent(win_T *wp) { return wp->w_p_fdm[0] == 'i'; } // foldmethodIsExpr() {{{2 /// @return true if 'foldmethod' is "expr" -int foldmethodIsExpr(win_T *wp) +bool foldmethodIsExpr(win_T *wp) { return wp->w_p_fdm[1] == 'x'; } // foldmethodIsMarker() {{{2 /// @return true if 'foldmethod' is "marker" -int foldmethodIsMarker(win_T *wp) +bool foldmethodIsMarker(win_T *wp) { return wp->w_p_fdm[2] == 'r'; } // foldmethodIsSyntax() {{{2 /// @return true if 'foldmethod' is "syntax" -int foldmethodIsSyntax(win_T *wp) +bool foldmethodIsSyntax(win_T *wp) { return wp->w_p_fdm[0] == 's'; } // foldmethodIsDiff() {{{2 /// @return true if 'foldmethod' is "diff" -int foldmethodIsDiff(win_T *wp) +bool foldmethodIsDiff(win_T *wp) { return wp->w_p_fdm[0] == 'd'; } @@ -372,7 +374,7 @@ void closeFold(pos_T pos, int count) /// Close fold for current window at position `pos` recursively. void closeFoldRecurse(pos_T pos) { - (void)setManualFold(pos, false, true, NULL); + setManualFold(pos, false, true, NULL); } // opFoldRange() {{{2 @@ -383,7 +385,7 @@ void closeFoldRecurse(pos_T pos) /// @param opening true to open, false to close /// @param recurse true to do it recursively /// @param had_visual true when Visual selection used -void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int had_visual) +void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, bool had_visual) { int done = DONE_NOTHING; // avoid error messages linenr_T first = firstpos.lnum; @@ -396,13 +398,13 @@ void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int ha // Opening one level only: next fold to open is after the one going to // be opened. if (opening && !recurse) { - (void)hasFolding(lnum, NULL, &lnum_next); + hasFolding(lnum, NULL, &lnum_next); } - (void)setManualFold(temp, opening, recurse, &done); + setManualFold(temp, opening, recurse, &done); // Closing one level only: next line to close a fold is after just // closed fold. if (!opening && !recurse) { - (void)hasFolding(lnum, NULL, &lnum_next); + hasFolding(lnum, NULL, &lnum_next); } } if (done == DONE_NOTHING) { @@ -426,7 +428,7 @@ void openFold(pos_T pos, int count) /// Open fold for current window at position `pos` recursively. void openFoldRecurse(pos_T pos) { - (void)setManualFold(pos, true, true, NULL); + setManualFold(pos, true, true, NULL); } // foldOpenCursor() {{{2 @@ -437,7 +439,7 @@ void foldOpenCursor(void) if (hasAnyFolding(curwin)) { while (true) { int done = DONE_NOTHING; - (void)setManualFold(curwin->w_cursor, true, false, &done); + setManualFold(curwin->w_cursor, true, false, &done); if (!(done & DONE_ACTION)) { break; } @@ -537,8 +539,8 @@ int foldManualAllowed(bool create) /// window. void foldCreate(win_T *wp, pos_T start, pos_T end) { - int use_level = false; - int closed = false; + bool use_level = false; + bool closed = false; int level = 0; pos_T start_rel = start; pos_T end_rel = end; @@ -794,7 +796,7 @@ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) maybe_small_end = top; } fold_T *fp; - (void)foldFind(&wp->w_folds, maybe_small_start, &fp); + foldFind(&wp->w_folds, maybe_small_start, &fp); while (fp < (fold_T *)wp->w_folds.ga_data + wp->w_folds.ga_len && fp->fd_top <= maybe_small_end) { fp->fd_small = kNone; @@ -850,7 +852,6 @@ void foldUpdateAll(win_T *win) int foldMoveTo(const bool updown, const int dir, const int count) { int retval = FAIL; - linenr_T lnum; fold_T *fp; checkupdate(curwin); @@ -909,7 +910,7 @@ int foldMoveTo(const bool updown, const int dir, const int count) if (dir == FORWARD) { // to start of next fold if there is one if (fp + 1 - (fold_T *)gap->ga_data < gap->ga_len) { - lnum = fp[1].fd_top + lnum_off; + linenr_T lnum = fp[1].fd_top + lnum_off; if (lnum > curwin->w_cursor.lnum) { lnum_found = lnum; } @@ -917,7 +918,7 @@ int foldMoveTo(const bool updown, const int dir, const int count) } else { // to end of previous fold if there is one if (fp > (fold_T *)gap->ga_data) { - lnum = fp[-1].fd_top + lnum_off + fp[-1].fd_len - 1; + linenr_T lnum = fp[-1].fd_top + lnum_off + fp[-1].fd_len - 1; if (lnum < curwin->w_cursor.lnum) { lnum_found = lnum; } @@ -927,12 +928,12 @@ int foldMoveTo(const bool updown, const int dir, const int count) // Open fold found, set cursor to its start/end and then check // nested folds. if (dir == FORWARD) { - lnum = fp->fd_top + lnum_off + fp->fd_len - 1; + linenr_T lnum = fp->fd_top + lnum_off + fp->fd_len - 1; if (lnum > curwin->w_cursor.lnum) { lnum_found = lnum; } } else { - lnum = fp->fd_top + lnum_off; + linenr_T lnum = fp->fd_top + lnum_off; if (lnum < curwin->w_cursor.lnum) { lnum_found = lnum; } @@ -1000,7 +1001,6 @@ void foldAdjustVisual(void) } pos_T *start, *end; - char *ptr; if (ltoreq(VIsual, curwin->w_cursor)) { start = &VIsual; @@ -1017,7 +1017,7 @@ void foldAdjustVisual(void) return; } - ptr = ml_get(end->lnum); + char *ptr = ml_get(end->lnum); end->col = (colnr_T)strlen(ptr); if (end->col > 0 && *p_sel == 'o') { end->col--; @@ -1030,7 +1030,7 @@ void foldAdjustVisual(void) /// Move the cursor to the first line of a closed fold. void foldAdjustCursor(void) { - (void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); + hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); } // Internal functions for "fold_T" {{{1 @@ -1140,7 +1140,7 @@ static void setFoldRepeat(pos_T pos, int count, int do_open) { for (int n = 0; n < count; n++) { int done = DONE_NOTHING; - (void)setManualFold(pos, do_open, false, &done); + setManualFold(pos, do_open, false, &done); if (!(done & DONE_ACTION)) { // Only give an error message when no fold could be opened. if (n == 0 && !(done & DONE_FOLD)) { @@ -1157,7 +1157,7 @@ static void setFoldRepeat(pos_T pos, int count, int do_open) /// /// @param opening true when opening, false when closing /// @param recurse true when closing/opening recursive -static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep) +static linenr_T setManualFold(pos_T pos, bool opening, bool recurse, int *donep) { if (foldmethodIsDiff(curwin) && curwin->w_p_scb) { linenr_T dlnum; @@ -1168,7 +1168,7 @@ static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep) if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { dlnum = diff_lnum_win(curwin->w_cursor.lnum, wp); if (dlnum != 0) { - (void)setManualFoldWin(wp, dlnum, opening, recurse, NULL); + setManualFoldWin(wp, dlnum, opening, recurse, NULL); } } } @@ -1189,7 +1189,7 @@ static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep) /// /// @return the line number of the next line that could be closed. /// It's only valid when "opening" is true! -static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recurse, int *donep) +static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, bool opening, bool recurse, int *donep) { fold_T *fp; fold_T *fp2; @@ -1390,7 +1390,7 @@ static void foldMarkAdjustRecurse(win_T *wp, garray_T *gap, linenr_T line1, line // Find the fold containing or just below "line1". fold_T *fp; - (void)foldFind(gap, line1, &fp); + foldFind(gap, line1, &fp); // Adjust all folds below "line1" that are affected. for (int i = (int)(fp - (fold_T *)gap->ga_data); i < gap->ga_len; i++, fp++) { @@ -1626,7 +1626,7 @@ static void foldAddMarker(buf_T *buf, pos_T pos, const char *marker, size_t mark STRCPY(newline + line_len + (p - cms) + markerlen, p + 2); added = markerlen + strlen(cms) - 2; } - ml_replace_buf(buf, lnum, newline, false); + ml_replace_buf(buf, lnum, newline, false, false); if (added) { extmark_splice_cols(buf, (int)lnum - 1, (int)line_len, 0, (int)added, kExtmarkUndo); @@ -1637,7 +1637,7 @@ static void foldAddMarker(buf_T *buf, pos_T pos, const char *marker, size_t mark /// Delete the markers for a fold, causing it to be deleted. /// /// @param lnum_off offset for fp->fd_top -static void deleteFoldMarkers(win_T *wp, fold_T *fp, int recursive, linenr_T lnum_off) +static void deleteFoldMarkers(win_T *wp, fold_T *fp, bool recursive, linenr_T lnum_off) { if (recursive) { for (int i = 0; i < fp->fd_nested.ga_len; i++) { @@ -1690,7 +1690,7 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char *marker, size_t marker assert(p >= line); memcpy(newline, line, (size_t)(p - line)); STRCPY(newline + (p - line), p + len); - ml_replace_buf(buf, lnum, newline, false); + ml_replace_buf(buf, lnum, newline, false, false); extmark_splice_cols(buf, (int)lnum - 1, (int)(p - line), (int)len, 0, kExtmarkUndo); } @@ -2172,7 +2172,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level, // startlnum, it must be deleted. if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level && flp->lvl > 0) { - (void)foldFind(gap, startlnum - 1, &fp); + foldFind(gap, startlnum - 1, &fp); if (fp != NULL && (fp >= ((fold_T *)gap->ga_data) + gap->ga_len || fp->fd_top >= startlnum)) { @@ -2238,7 +2238,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level, } } if (lvl < level + i) { - (void)foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2); + foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2); if (fp2 != NULL) { bot = fp2->fd_top + fp2->fd_len - 1 + fp->fd_top; } @@ -2604,7 +2604,7 @@ static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr // any between top and bot, they have been removed by the caller. garray_T *const gap1 = &fp->fd_nested; garray_T *const gap2 = &fp[1].fd_nested; - (void)foldFind(gap1, bot + 1 - fp->fd_top, &fp2); + foldFind(gap1, bot + 1 - fp->fd_top, &fp2); if (fp2 != NULL) { const int len = (int)((fold_T *)gap1->ga_data + gap1->ga_len - fp2); if (len > 0) { @@ -2803,7 +2803,8 @@ void foldMoveRange(win_T *const wp, garray_T *gap, const linenr_T line1, const l // Case 5 or 6: changes rely on whether there are folds between the end of // this fold and "dest". size_t move_start = FOLD_INDEX(fp, gap); - size_t move_end = 0, dest_index = 0; + size_t move_end = 0; + size_t dest_index = 0; for (; VALID_FOLD(fp, gap) && fp->fd_top <= dest; fp++) { if (fp->fd_top <= line2) { // 5, or 6 @@ -2982,7 +2983,9 @@ static void foldlevelExpr(fline_T *flp) // "<1", "<2", .. : end a fold with a certain level case '<': - flp->lvl_next = n - 1; + // To prevent an unexpected start of a new fold, the next + // level must not exceed the level of the current fold. + flp->lvl_next = MIN(flp->lvl, n - 1); flp->end = n; break; @@ -3234,7 +3237,7 @@ static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off) // }}}1 /// "foldclosed()" and "foldclosedend()" functions -static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end) +static void foldclosed_both(typval_T *argvars, typval_T *rettv, bool end) { const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { diff --git a/src/nvim/fold.h b/src/nvim/fold.h index 3a70c11792..f9044d247f 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -1,13 +1,14 @@ #pragma once -#include <stdio.h> +#include <stdio.h> // IWYU pragma: keep -#include "nvim/buffer_defs.h" // IWYU pragma: keep -#include "nvim/fold_defs.h" // IWYU pragma: export +#include "nvim/decoration_defs.h" // IWYU pragma: keep +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep +#include "nvim/fold_defs.h" // IWYU pragma: keep #include "nvim/garray_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" -#include "nvim/pos_defs.h" -#include "nvim/types_defs.h" +#include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep EXTERN int disable_fold_update INIT( = 0); diff --git a/src/nvim/fold_defs.h b/src/nvim/fold_defs.h index 68ecd9cc7e..453819d6f1 100644 --- a/src/nvim/fold_defs.h +++ b/src/nvim/fold_defs.h @@ -4,7 +4,7 @@ /// Info used to pass info about a fold from the fold-detection code to the /// code that displays the foldcolumn. -typedef struct foldinfo { +typedef struct { linenr_T fi_lnum; ///< line number where fold starts int fi_level; ///< level of the fold; when this is zero the ///< other fields are invalid diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index 15370dcb3e..43af880767 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -1,24 +1,16 @@ -// If DEFINE_FUNC_ATTRIBUTES macro is not defined then all function attributes -// are defined as empty values. -// -// If DO_NOT_DEFINE_EMPTY_ATTRIBUTES then empty macros are not defined. Thus -// undefined DEFINE_FUNC_ATTRIBUTES and defined DO_NOT_DEFINE_EMPTY_ATTRIBUTES +// Undefined DEFINE_FUNC_ATTRIBUTES and undefined DEFINE_EMPTY_ATTRIBUTES // leaves file with untouched FUNC_ATTR_* macros. This variant is used for -// scripts/gendeclarations.lua. +// scripts/gen_declarations.lua. // -// Empty macros are used for *.c files. (undefined DEFINE_FUNC_ATTRIBUTES and -// undefined DO_NOT_DEFINE_EMPTY_ATTRIBUTES) +// Empty macros are used for *.c files. +// (undefined DEFINE_FUNC_ATTRIBUTES and defined DEFINE_EMPTY_ATTRIBUTES) // // Macros defined as __attribute__((*)) are used by generated header files. -// (defined DEFINE_FUNC_ATTRIBUTES and undefined -// DO_NOT_DEFINE_EMPTY_ATTRIBUTES) -// -// Defined DEFINE_FUNC_ATTRIBUTES and defined DO_NOT_DEFINE_EMPTY_ATTRIBUTES is -// not used by anything. +// (defined DEFINE_FUNC_ATTRIBUTES and undefined DEFINE_EMPTY_ATTRIBUTES) // FUNC_ATTR_* macros should be in *.c files for declarations generator. If you // define a function for which declaration is not generated by -// gendeclarations.lua (e.g. template hash implementation) then you should use +// gen_declarations.lua (e.g. template hash implementation) then you should use // REAL_FATTR_* macros. // gcc and clang expose their version as follows: @@ -217,9 +209,11 @@ # endif #endif -#ifdef DEFINE_FUNC_ATTRIBUTES +#if defined(DEFINE_FUNC_ATTRIBUTES) || defined(DEFINE_EMPTY_ATTRIBUTES) /// Fast (non-deferred) API function. # define FUNC_API_FAST +/// Return value needs to be freed +# define FUNC_API_RET_ALLOC /// Internal C function not exposed in the RPC API. # define FUNC_API_NOEXPORT /// API function not exposed in Vimscript/eval. @@ -234,6 +228,9 @@ # define FUNC_API_SINCE(X) /// API function deprecated since the given API level. # define FUNC_API_DEPRECATED_SINCE(X) +#endif + +#if defined(DEFINE_FUNC_ATTRIBUTES) # define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC # define FUNC_ATTR_ALLOC_SIZE(x) REAL_FATTR_ALLOC_SIZE(x) # define FUNC_ATTR_ALLOC_SIZE_PROD(x, y) REAL_FATTR_ALLOC_SIZE_PROD(x, y) @@ -250,7 +247,7 @@ # define FUNC_ATTR_NO_SANITIZE_UNDEFINED REAL_FATTR_NO_SANITIZE_UNDEFINED # define FUNC_ATTR_NO_SANITIZE_ADDRESS REAL_FATTR_NO_SANITIZE_ADDRESS # define FUNC_ATTR_PRINTF(x, y) REAL_FATTR_PRINTF(x, y) -#elif !defined(DO_NOT_DEFINE_EMPTY_ATTRIBUTES) +#elif defined(DEFINE_EMPTY_ATTRIBUTES) # define FUNC_ATTR_MALLOC # define FUNC_ATTR_ALLOC_SIZE(x) # define FUNC_ATTR_ALLOC_SIZE_PROD(x, y) diff --git a/src/nvim/garray.c b/src/nvim/garray.c index 24b6fb0007..f87a196361 100644 --- a/src/nvim/garray.c +++ b/src/nvim/garray.c @@ -5,7 +5,6 @@ #include <stdint.h> #include <string.h> -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/log.h" #include "nvim/memory.h" @@ -13,7 +12,7 @@ #include "nvim/strings.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "garray.c.generated.h" // IWYU pragma: export +# include "garray.c.generated.h" #endif /// Clear an allocated growing array. @@ -218,3 +217,12 @@ void ga_append(garray_T *gap, uint8_t c) { GA_APPEND(uint8_t, gap, c); } + +void *ga_append_via_ptr(garray_T *gap, size_t item_size) +{ + if ((int)item_size != gap->ga_itemsize) { + WLOG("wrong item size (%zu), should be %d", item_size, gap->ga_itemsize); + } + ga_grow(gap, 1); + return ((char *)gap->ga_data) + (item_size * (size_t)gap->ga_len++); +} diff --git a/src/nvim/garray.h b/src/nvim/garray.h index a96deda759..7a766f988a 100644 --- a/src/nvim/garray.h +++ b/src/nvim/garray.h @@ -3,10 +3,8 @@ #include <stdbool.h> #include <stddef.h> -#include "nvim/garray_defs.h" // IWYU pragma: export -#include "nvim/log.h" +#include "nvim/garray_defs.h" // IWYU pragma: keep #include "nvim/memory.h" -#include "nvim/types_defs.h" #define GA_EMPTY(ga_ptr) ((ga_ptr)->ga_len <= 0) @@ -24,15 +22,6 @@ # include "garray.h.generated.h" #endif -static inline void *ga_append_via_ptr(garray_T *gap, size_t item_size) -{ - if ((int)item_size != gap->ga_itemsize) { - WLOG("wrong item size (%zu), should be %d", item_size, gap->ga_itemsize); - } - ga_grow(gap, 1); - return ((char *)gap->ga_data) + (item_size * (size_t)gap->ga_len++); -} - /// Deep free a garray of specific type using a custom free function. /// Items in the array as well as the array itself are freed. /// diff --git a/src/nvim/garray_defs.h b/src/nvim/garray_defs.h index 5f4032884e..4db9667a43 100644 --- a/src/nvim/garray_defs.h +++ b/src/nvim/garray_defs.h @@ -5,7 +5,7 @@ /// Structure used for growing arrays. /// This is used to store information that only grows, is deleted all at /// once, and needs to be accessed by index. See ga_clear() and ga_grow(). -typedef struct growarray { +typedef struct { int ga_len; // current number of items used int ga_maxlen; // maximum number of items possible int ga_itemsize; // sizeof(item) diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua index f33da452ff..9ce9f3d7a6 100644 --- a/src/nvim/generators/c_grammar.lua +++ b/src/nvim/generators/c_grammar.lua @@ -6,61 +6,112 @@ local lpeg = vim.lpeg local P, R, S = lpeg.P, lpeg.R, lpeg.S local C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg +--- @param pat vim.lpeg.Pattern +local function rep(pat) + return pat ^ 0 +end + +--- @param pat vim.lpeg.Pattern +local function rep1(pat) + return pat ^ 1 +end + +--- @param pat vim.lpeg.Pattern +local function opt(pat) + return pat ^ -1 +end + local any = P(1) -- (consume one character) local letter = R('az', 'AZ') + S('_$') local num = R('09') local alpha = letter + num local nl = P('\r\n') + P('\n') local not_nl = any - nl -local ws = S(' \t') + nl -local fill = ws ^ 0 -local c_comment = P('//') * (not_nl ^ 0) -local c_preproc = P('#') * (not_nl ^ 0) -local dllexport = P('DLLEXPORT') * (ws ^ 1) -local typed_container = - (P('ArrayOf(') + P('DictionaryOf(') + P('Dict(')) * ((any - P(')')) ^ 1) * P(')') -local c_id = ( - typed_container + - (letter * (alpha ^ 0)) +local space = S(' \t') +local ws = space + nl +local fill = rep(ws) +local c_comment = P('//') * rep(not_nl) +local cdoc_comment = P('///') * opt(Ct(Cg(rep(space) * rep(not_nl), 'comment'))) +local c_preproc = P('#') * rep(not_nl) +local dllexport = P('DLLEXPORT') * rep1(ws) + +local typed_container = ( + (P('ArrayOf(') + P('DictionaryOf(') + P('Dict(')) + * rep1(any - P(')')) + * P(')') ) + +local c_id = (typed_container + (letter * rep(alpha))) local c_void = P('void') + local c_param_type = ( - ((P('Error') * fill * P('*') * fill) * Cc('error')) + - ((P('Arena') * fill * P('*') * fill) * Cc('arena')) + - ((P('lua_State') * fill * P('*') * fill) * Cc('lstate')) + - C((P('const ') ^ -1) * (c_id) * (ws ^ 1) * P('*')) + - (C(c_id) * (ws ^ 1)) - ) + ((P('Error') * fill * P('*') * fill) * Cc('error')) + + ((P('Arena') * fill * P('*') * fill) * Cc('arena')) + + ((P('lua_State') * fill * P('*') * fill) * Cc('lstate')) + + C(opt(P('const ')) * c_id * rep1(ws) * rep1(P('*'))) + + (C(c_id) * rep1(ws)) +) + local c_type = (C(c_void) * (ws ^ 1)) + c_param_type local c_param = Ct(c_param_type * C(c_id)) local c_param_list = c_param * (fill * (P(',') * fill * c_param) ^ 0) local c_params = Ct(c_void + c_param_list) + +local impl_line = (any - P('}')) * opt(rep(not_nl)) * nl + +local ignore_line = rep1(not_nl) * nl + +local empty_line = Ct(Cc('empty') * nl * nl) + local c_proto = Ct( - (dllexport ^ -1) * - Cg(c_type, 'return_type') * Cg(c_id, 'name') * - fill * P('(') * fill * Cg(c_params, 'parameters') * fill * P(')') * - Cg(Cc(false), 'fast') * - (fill * Cg((P('FUNC_API_SINCE(') * C(num ^ 1)) * P(')'), 'since') ^ -1) * - (fill * Cg((P('FUNC_API_DEPRECATED_SINCE(') * C(num ^ 1)) * P(')'), - 'deprecated_since') ^ -1) * - (fill * Cg((P('FUNC_API_FAST') * Cc(true)), 'fast') ^ -1) * - (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) * - (fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1) * - (fill * Cg((P('FUNC_API_LUA_ONLY') * Cc(true)), 'lua_only') ^ -1) * - (fill * (Cg(P('FUNC_API_TEXTLOCK_ALLOW_CMDWIN') * Cc(true), 'textlock_allow_cmdwin') + - Cg(P('FUNC_API_TEXTLOCK') * Cc(true), 'textlock')) ^ -1) * - (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) * - (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) * - (fill * Cg((P('FUNC_API_CLIENT_IMPL') * Cc(true)), 'client_impl') ^ -1) * - (fill * Cg((P('FUNC_API_CLIENT_IGNORE') * Cc(true)), 'client_ignore') ^ -1) * - fill * P(';') - ) + Cc('proto') + * opt(dllexport) + * opt(Cg(P('static') * fill * Cc(true), 'static')) + * Cg(c_type, 'return_type') + * Cg(c_id, 'name') + * fill + * (P('(') * fill * Cg(c_params, 'parameters') * fill * P(')')) + * Cg(Cc(false), 'fast') + * (fill * Cg((P('FUNC_API_SINCE(') * C(rep1(num))) * P(')'), 'since') ^ -1) + * (fill * Cg((P('FUNC_API_DEPRECATED_SINCE(') * C(rep1(num))) * P(')'), 'deprecated_since') ^ -1) + * (fill * Cg((P('FUNC_API_FAST') * Cc(true)), 'fast') ^ -1) + * (fill * Cg((P('FUNC_API_RET_ALLOC') * Cc(true)), 'ret_alloc') ^ -1) + * (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) + * (fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1) + * (fill * Cg((P('FUNC_API_LUA_ONLY') * Cc(true)), 'lua_only') ^ -1) + * (fill * (Cg(P('FUNC_API_TEXTLOCK_ALLOW_CMDWIN') * Cc(true), 'textlock_allow_cmdwin') + Cg( + P('FUNC_API_TEXTLOCK') * Cc(true), + 'textlock' + )) ^ -1) + * (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) + * (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) + * (fill * Cg((P('FUNC_API_CLIENT_IMPL') * Cc(true)), 'client_impl') ^ -1) + * (fill * Cg((P('FUNC_API_CLIENT_IGNORE') * Cc(true)), 'client_ignore') ^ -1) + * fill + * (P(';') + (P('{') * nl + (impl_line ^ 0) * P('}'))) +) local c_field = Ct(Cg(c_id, 'type') * ws * Cg(c_id, 'name') * fill * P(';') * fill) local c_keyset = Ct( - P('typedef') * ws * P('struct') * fill * P('{') * fill * - Cg(Ct(c_field ^ 1), 'fields') * - P('}') * fill * P('Dict') * fill * P('(') * Cg(c_id, 'keyset_name') * fill * P(')') * P(';')) + P('typedef') + * ws + * P('struct') + * fill + * P('{') + * fill + * Cg(Ct(c_field ^ 1), 'fields') + * P('}') + * fill + * P('Dict') + * fill + * P('(') + * Cg(c_id, 'keyset_name') + * fill + * P(')') + * P(';') +) -local grammar = Ct((c_proto + c_comment + c_preproc + ws + c_keyset) ^ 1) -return {grammar=grammar, typed_container=typed_container} +local grammar = Ct( + rep1(empty_line + c_proto + cdoc_comment + c_comment + c_preproc + ws + c_keyset + ignore_line) +) +return { grammar = grammar, typed_container = typed_container } diff --git a/src/nvim/generators/dump_bin_array.lua b/src/nvim/generators/dump_bin_array.lua index bee5aba73f..c6cda25e73 100644 --- a/src/nvim/generators/dump_bin_array.lua +++ b/src/nvim/generators/dump_bin_array.lua @@ -1,10 +1,10 @@ local function dump_bin_array(output, name, data) output:write([[ - static const uint8_t ]]..name..[[[] = { + static const uint8_t ]] .. name .. [[[] = { ]]) for i = 1, #data do - output:write(string.byte(data, i)..', ') + output:write(string.byte(data, i) .. ', ') if i % 10 == 0 then output:write('\n ') end diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 9720cca477..04b4363e42 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -1,16 +1,19 @@ local mpack = vim.mpack -local hashy = require'generators.hashy' +local hashy = require 'generators.hashy' -assert(#arg >= 5) +local pre_args = 7 +assert(#arg >= pre_args) -- output h file with generated dispatch functions (dispatch_wrappers.generated.h) local dispatch_outputf = arg[1] --- output h file with packed metadata (funcs_metadata.generated.h) -local funcs_metadata_outputf = arg[2] +-- output h file with packed metadata (api_metadata.generated.h) +local api_metadata_outputf = arg[2] -- output metadata mpack file, for use by other build scripts (api_metadata.mpack) local mpack_outputf = arg[3] local lua_c_bindings_outputf = arg[4] -- lua_api_c_bindings.generated.c local keysets_outputf = arg[5] -- keysets_defs.generated.h +local ui_metadata_inputf = arg[6] -- ui events metadata +local git_version_inputf = arg[7] -- git version header local functions = {} @@ -23,16 +26,18 @@ local function_names = {} local c_grammar = require('generators.c_grammar') -local function startswith(String,Start) - return string.sub(String,1,string.len(Start))==Start -end +local startswith = vim.startswith local function add_function(fn) - local public = startswith(fn.name, "nvim_") or fn.deprecated_since + local public = startswith(fn.name, 'nvim_') or fn.deprecated_since if public and not fn.noexport then functions[#functions + 1] = fn function_names[fn.name] = true - if #fn.parameters >= 2 and fn.parameters[2][1] == 'Array' and fn.parameters[2][2] == 'uidata' then + if + #fn.parameters >= 2 + and fn.parameters[2][1] == 'Array' + and fn.parameters[2][2] == 'uidata' + then -- function receives the "args" as a parameter fn.receives_array_args = true -- remove the args parameter @@ -51,15 +56,14 @@ local function add_function(fn) -- for specifying errors fn.parameters[#fn.parameters] = nil end - if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'arena' then - -- return value is allocated in an arena - fn.arena_return = true - fn.parameters[#fn.parameters] = nil - end if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'lstate' then fn.has_lua_imp = true fn.parameters[#fn.parameters] = nil end + if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'arena' then + fn.receives_arena = true + fn.parameters[#fn.parameters] = nil + end end end @@ -70,7 +74,7 @@ local function add_keyset(val) local types = {} local is_set_name = 'is_set__' .. val.keyset_name .. '_' local has_optional = false - for i,field in ipairs(val.fields) do + for i, field in ipairs(val.fields) do if field.type ~= 'Object' then types[field.name] = field.type end @@ -80,36 +84,46 @@ local function add_keyset(val) if i > 1 then error("'is_set__{type}_' must be first if present") elseif field.name ~= is_set_name then - error(val.keyset_name..": name of first key should be "..is_set_name) + error(val.keyset_name .. ': name of first key should be ' .. is_set_name) elseif field.type ~= 'OptionalKeys' then - error("'"..is_set_name.."' must have type 'OptionalKeys'") + error("'" .. is_set_name .. "' must have type 'OptionalKeys'") end has_optional = true end end - table.insert(keysets, {name=val.keyset_name, keys=keys, types=types, has_optional=has_optional}) + table.insert(keysets, { + name = val.keyset_name, + keys = keys, + types = types, + has_optional = has_optional, + }) end +local ui_options_text = nil + -- read each input file, parse and append to the api metadata -for i = 6, #arg do +for i = pre_args + 1, #arg do local full_path = arg[i] local parts = {} for part in string.gmatch(full_path, '[^/]+') do parts[#parts + 1] = part end - headers[#headers + 1] = parts[#parts - 1]..'/'..parts[#parts] + headers[#headers + 1] = parts[#parts - 1] .. '/' .. parts[#parts] - local input = io.open(full_path, 'rb') + local input = assert(io.open(full_path, 'rb')) - local tmp = c_grammar.grammar:match(input:read('*all')) + local text = input:read('*all') + local tmp = c_grammar.grammar:match(text) for j = 1, #tmp do local val = tmp[j] if val.keyset_name then add_keyset(val) - else + elseif val.name then add_function(val) end end + + ui_options_text = ui_options_text or string.match(text, 'ui_ext_names%[][^{]+{([^}]+)}') input:close() end @@ -123,14 +137,14 @@ end -- Export functions under older deprecated names. -- These will be removed eventually. -local deprecated_aliases = require("api.dispatch_deprecated") -for _,f in ipairs(shallowcopy(functions)) do +local deprecated_aliases = require('api.dispatch_deprecated') +for _, f in ipairs(shallowcopy(functions)) do local ismethod = false - if startswith(f.name, "nvim_") then - if startswith(f.name, "nvim__") or f.name == "nvim_error_event" then + if startswith(f.name, 'nvim_') then + if startswith(f.name, 'nvim__') or f.name == 'nvim_error_event' then f.since = -1 elseif f.since == nil then - print("Function "..f.name.." lacks since field.\n") + print('Function ' .. f.name .. ' lacks since field.\n') os.exit(1) end f.since = tonumber(f.since) @@ -138,16 +152,16 @@ for _,f in ipairs(shallowcopy(functions)) do f.deprecated_since = tonumber(f.deprecated_since) end - if startswith(f.name, "nvim_buf_") then + if startswith(f.name, 'nvim_buf_') then ismethod = true - elseif startswith(f.name, "nvim_win_") then + elseif startswith(f.name, 'nvim_win_') then ismethod = true - elseif startswith(f.name, "nvim_tabpage_") then + elseif startswith(f.name, 'nvim_tabpage_') then ismethod = true end f.remote = f.remote_only or not f.lua_only f.lua = f.lua_only or not f.remote_only - f.eval = (not f.lua_only) and (not f.remote_only) + f.eval = (not f.lua_only) and not f.remote_only else f.deprecated_since = tonumber(f.deprecated_since) assert(f.deprecated_since == 1) @@ -159,63 +173,127 @@ for _,f in ipairs(shallowcopy(functions)) do if newname ~= nil then if function_names[newname] then -- duplicate - print("Function "..f.name.." has deprecated alias\n" - ..newname.." which has a separate implementation.\n".. - "Please remove it from src/nvim/api/dispatch_deprecated.lua") + print( + 'Function ' + .. f.name + .. ' has deprecated alias\n' + .. newname + .. ' which has a separate implementation.\n' + .. 'Please remove it from src/nvim/api/dispatch_deprecated.lua' + ) os.exit(1) end local newf = shallowcopy(f) newf.name = newname - if newname == "ui_try_resize" then + if newname == 'ui_try_resize' then -- The return type was incorrectly set to Object in 0.1.5. -- Keep it that way for clients that rely on this. - newf.return_type = "Object" + newf.return_type = 'Object' end newf.impl_name = f.name newf.lua = false newf.eval = false newf.since = 0 newf.deprecated_since = 1 - functions[#functions+1] = newf + functions[#functions + 1] = newf end end -- don't expose internal attributes like "impl_name" in public metadata -local exported_attributes = {'name', 'return_type', 'method', - 'since', 'deprecated_since'} +local exported_attributes = { 'name', 'return_type', 'method', 'since', 'deprecated_since' } local exported_functions = {} -for _,f in ipairs(functions) do - if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event" or f.name == "redraw") then +for _, f in ipairs(functions) do + if not (startswith(f.name, 'nvim__') or f.name == 'nvim_error_event' or f.name == 'redraw') then local f_exported = {} - for _,attr in ipairs(exported_attributes) do + for _, attr in ipairs(exported_attributes) do f_exported[attr] = f[attr] end f_exported.parameters = {} - for i,param in ipairs(f.parameters) do - if param[1] == "DictionaryOf(LuaRef)" then - param = {"Dictionary", param[2]} - elseif startswith(param[1], "Dict(") then - param = {"Dictionary", param[2]} + for i, param in ipairs(f.parameters) do + if param[1] == 'DictionaryOf(LuaRef)' then + param = { 'Dictionary', param[2] } + elseif startswith(param[1], 'Dict(') then + param = { 'Dictionary', param[2] } end f_exported.parameters[i] = param end - exported_functions[#exported_functions+1] = f_exported + if startswith(f.return_type, 'Dict(') then + f_exported.return_type = 'Dictionary' + end + exported_functions[#exported_functions + 1] = f_exported end end +local ui_options = { 'rgb' } +for x in string.gmatch(ui_options_text, '"([a-z][a-z_]+)"') do + table.insert(ui_options, x) +end + +local version = require 'nvim_version' +local git_version = io.open(git_version_inputf):read '*a' +local version_build = string.match(git_version, '#define NVIM_VERSION_BUILD "([^"]+)"') or vim.NIL -- serialize the API metadata using msgpack and embed into the resulting -- binary for easy querying by clients -local funcs_metadata_output = io.open(funcs_metadata_outputf, 'wb') -local packed = mpack.encode(exported_functions) -local dump_bin_array = require("generators.dump_bin_array") -dump_bin_array(funcs_metadata_output, 'funcs_metadata', packed) -funcs_metadata_output:close() +local api_metadata_output = assert(io.open(api_metadata_outputf, 'wb')) +local pieces = {} + +-- Naively using mpack.encode({foo=x, bar=y}) will make the build +-- "non-reproducible". Emit maps directly as FIXDICT(2) "foo" x "bar" y instead +local function fixdict(num) + if num > 15 then + error 'implement more dict codes' + end + table.insert(pieces, string.char(128 + num)) +end +local function put(item, item2) + table.insert(pieces, mpack.encode(item)) + if item2 ~= nil then + table.insert(pieces, mpack.encode(item2)) + end +end + +fixdict(6) + +put('version') +fixdict(1 + #version) +for _, item in ipairs(version) do + -- NB: all items are mandatory. But any error will be less confusing + -- with placholder vim.NIL (than invalid mpack data) + put(item[1], item[2] or vim.NIL) +end +put('build', version_build) + +put('functions', exported_functions) +put('ui_events') +table.insert(pieces, io.open(ui_metadata_inputf, 'rb'):read('*all')) +put('ui_options', ui_options) + +put('error_types') +fixdict(2) +put('Exception', { id = 0 }) +put('Validation', { id = 1 }) + +put('types') +local types = + { { 'Buffer', 'nvim_buf_' }, { 'Window', 'nvim_win_' }, { 'Tabpage', 'nvim_tabpage_' } } +fixdict(#types) +for i, item in ipairs(types) do + put(item[1]) + fixdict(2) + put('id', i - 1) + put('prefix', item[2]) +end + +local packed = table.concat(pieces) +local dump_bin_array = require('generators.dump_bin_array') +dump_bin_array(api_metadata_output, 'packed_api_metadata', packed) +api_metadata_output:close() -- start building the dispatch wrapper output -local output = io.open(dispatch_outputf, 'wb') +local output = assert(io.open(dispatch_outputf, 'wb')) -local keysets_defs = io.open(keysets_outputf, 'wb') +local keysets_defs = assert(io.open(keysets_outputf, 'wb')) -- =========================================================================== -- NEW API FILES MUST GO HERE. @@ -226,9 +304,9 @@ local keysets_defs = io.open(keysets_outputf, 'wb') output:write([[ #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" +#include "nvim/globals.h" #include "nvim/log.h" #include "nvim/map_defs.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/api/autocmd.h" #include "nvim/api/buffer.h" @@ -246,67 +324,84 @@ output:write([[ ]]) -for _,k in ipairs(keysets) do +keysets_defs:write('// IWYU pragma: private, include "nvim/api/private/dispatch.h"\n\n') + +for _, k in ipairs(keysets) do local c_name = {} - for i = 1,#k.keys do + for i = 1, #k.keys do -- some keys, like "register" are c keywords and get -- escaped with a trailing _ in the struct. - if vim.endswith(k.keys[i], "_") then + if vim.endswith(k.keys[i], '_') then local orig = k.keys[i] - k.keys[i] = string.sub(k.keys[i],1, #(k.keys[i]) - 1) + k.keys[i] = string.sub(k.keys[i], 1, #k.keys[i] - 1) c_name[k.keys[i]] = orig k.types[k.keys[i]] = k.types[orig] end end - local neworder, hashfun = hashy.hashy_hash(k.name, k.keys, function (idx) - return k.name.."_table["..idx.."].str" + local neworder, hashfun = hashy.hashy_hash(k.name, k.keys, function(idx) + return k.name .. '_table[' .. idx .. '].str' end) - keysets_defs:write("extern KeySetLink "..k.name.."_table[];\n") + keysets_defs:write('extern KeySetLink ' .. k.name .. '_table[' .. (1 + #neworder) .. '];\n') local function typename(type) - if type ~= nil then - return "kObjectType"..type + if type == 'HLGroupID' then + return 'kObjectTypeInteger' + elseif type ~= nil then + return 'kObjectType' .. type else - return "kObjectTypeNil" + return 'kObjectTypeNil' end end - output:write("KeySetLink "..k.name.."_table[] = {\n") + output:write('KeySetLink ' .. k.name .. '_table[] = {\n') for i, key in ipairs(neworder) do local ind = -1 if k.has_optional then ind = i - keysets_defs:write("#define KEYSET_OPTIDX_"..k.name.."__"..key.." "..ind.."\n") - end - output:write(' {"'..key..'", offsetof(KeyDict_'..k.name..", "..(c_name[key] or key).."), "..typename(k.types[key])..", "..ind.."},\n") + keysets_defs:write('#define KEYSET_OPTIDX_' .. k.name .. '__' .. key .. ' ' .. ind .. '\n') + end + output:write( + ' {"' + .. key + .. '", offsetof(KeyDict_' + .. k.name + .. ', ' + .. (c_name[key] or key) + .. '), ' + .. typename(k.types[key]) + .. ', ' + .. ind + .. ', ' + .. (k.types[key] == 'HLGroupID' and 'true' or 'false') + .. '},\n' + ) end - output:write(' {NULL, 0, kObjectTypeNil, -1},\n') - output:write("};\n\n") + output:write(' {NULL, 0, kObjectTypeNil, -1, false},\n') + output:write('};\n\n') output:write(hashfun) output:write([[ -KeySetLink *KeyDict_]]..k.name..[[_get_field(const char *str, size_t len) +KeySetLink *KeyDict_]] .. k.name .. [[_get_field(const char *str, size_t len) { - int hash = ]]..k.name..[[_hash(str, len); + int hash = ]] .. k.name .. [[_hash(str, len); if (hash == -1) { return NULL; } - return &]]..k.name..[[_table[hash]; + return &]] .. k.name .. [[_table[hash]; } ]]) - keysets_defs:write("#define api_free_keydict_"..k.name.."(x) api_free_keydict(x, "..k.name.."_table)\n") end local function real_type(type) local rv = type - local rmatch = string.match(type, "Dict%(([_%w]+)%)") + local rmatch = string.match(type, 'Dict%(([_%w]+)%)') if rmatch then - return "KeyDict_"..rmatch + return 'KeyDict_' .. rmatch elseif c_grammar.typed_container:match(rv) then if rv:match('Array') then rv = 'Array' @@ -333,24 +428,30 @@ for i = 1, #functions do if fn.impl_name == nil and fn.remote then local args = {} - output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Arena* arena, Error *error)') + output:write( + 'Object handle_' .. fn.name .. '(uint64_t channel_id, Array args, Arena* arena, Error *error)' + ) output:write('\n{') output:write('\n#ifdef NVIM_LOG_DEBUG') - output:write('\n DLOG("RPC: ch %" PRIu64 ": invoke '..fn.name..'", channel_id);') + output:write('\n DLOG("RPC: ch %" PRIu64 ": invoke ' .. fn.name .. '", channel_id);') output:write('\n#endif') output:write('\n Object ret = NIL;') -- Declare/initialize variables that will hold converted arguments for j = 1, #fn.parameters do local param = fn.parameters[j] local rt = real_type(param[1]) - local converted = 'arg_'..j - output:write('\n '..rt..' '..converted..';') + local converted = 'arg_' .. j + output:write('\n ' .. rt .. ' ' .. converted .. ';') end output:write('\n') if not fn.receives_array_args then - output:write('\n if (args.size != '..#fn.parameters..') {') - output:write('\n api_set_error(error, kErrorTypeException, \ - "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);') + output:write('\n if (args.size != ' .. #fn.parameters .. ') {') + output:write( + '\n api_set_error(error, kErrorTypeException, \ + "Wrong number of arguments: expecting ' + .. #fn.parameters + .. ' but got %zu", args.size);' + ) output:write('\n goto cleanup;') output:write('\n }\n') end @@ -359,55 +460,121 @@ for i = 1, #functions do for j = 1, #fn.parameters do local converted, param param = fn.parameters[j] - converted = 'arg_'..j + converted = 'arg_' .. j local rt = real_type(param[1]) if rt == 'Object' then - output:write('\n '..converted..' = args.items['..(j - 1)..'];\n') + output:write('\n ' .. converted .. ' = args.items[' .. (j - 1) .. '];\n') elseif rt:match('^KeyDict_') then converted = '&' .. converted - output:write('\n if (args.items['..(j - 1)..'].type == kObjectTypeDictionary) {') --luacheck: ignore 631 - output:write('\n memset('..converted..', 0, sizeof(*'..converted..'));') -- TODO: neeeee - output:write('\n if (!api_dict_to_keydict('..converted..', '..rt..'_get_field, args.items['..(j - 1)..'].data.dictionary, error)) {') + output:write('\n if (args.items[' .. (j - 1) .. '].type == kObjectTypeDictionary) {') --luacheck: ignore 631 + output:write('\n memset(' .. converted .. ', 0, sizeof(*' .. converted .. '));') -- TODO: neeeee + output:write( + '\n if (!api_dict_to_keydict(' + .. converted + .. ', ' + .. rt + .. '_get_field, args.items[' + .. (j - 1) + .. '].data.dictionary, error)) {' + ) output:write('\n goto cleanup;') output:write('\n }') - output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeArray && args.items['..(j - 1)..'].data.array.size == 0) {') --luacheck: ignore 631 - output:write('\n memset('..converted..', 0, sizeof(*'..converted..'));') + output:write( + '\n } else if (args.items[' + .. (j - 1) + .. '].type == kObjectTypeArray && args.items[' + .. (j - 1) + .. '].data.array.size == 0) {' + ) --luacheck: ignore 631 + output:write('\n memset(' .. converted .. ', 0, sizeof(*' .. converted .. '));') output:write('\n } else {') - output:write('\n api_set_error(error, kErrorTypeException, \ - "Wrong type for argument '..j..' when calling '..fn.name..', expecting '..param[1]..'");') + output:write( + '\n api_set_error(error, kErrorTypeException, \ + "Wrong type for argument ' + .. j + .. ' when calling ' + .. fn.name + .. ', expecting ' + .. param[1] + .. '");' + ) output:write('\n goto cleanup;') output:write('\n }\n') else if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') then -- Buffer, Window, and Tabpage have a specific type, but are stored in integer - output:write('\n if (args.items['.. - (j - 1)..'].type == kObjectType'..rt..' && args.items['..(j - 1)..'].data.integer >= 0) {') - output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') + output:write( + '\n if (args.items[' + .. (j - 1) + .. '].type == kObjectType' + .. rt + .. ' && args.items[' + .. (j - 1) + .. '].data.integer >= 0) {' + ) + output:write( + '\n ' .. converted .. ' = (handle_T)args.items[' .. (j - 1) .. '].data.integer;' + ) else - output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') - output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..attr_name(rt)..';') + output:write('\n if (args.items[' .. (j - 1) .. '].type == kObjectType' .. rt .. ') {') + output:write( + '\n ' + .. converted + .. ' = args.items[' + .. (j - 1) + .. '].data.' + .. attr_name(rt) + .. ';' + ) end - if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then + if + rt:match('^Buffer$') + or rt:match('^Window$') + or rt:match('^Tabpage$') + or rt:match('^Boolean$') + then -- accept nonnegative integers for Booleans, Buffers, Windows and Tabpages - output:write('\n } else if (args.items['.. - (j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer >= 0) {') - output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') + output:write( + '\n } else if (args.items[' + .. (j - 1) + .. '].type == kObjectTypeInteger && args.items[' + .. (j - 1) + .. '].data.integer >= 0) {' + ) + output:write( + '\n ' .. converted .. ' = (handle_T)args.items[' .. (j - 1) .. '].data.integer;' + ) end if rt:match('^Float$') then -- accept integers for Floats - output:write('\n } else if (args.items['.. - (j - 1)..'].type == kObjectTypeInteger) {') - output:write('\n '..converted..' = (Float)args.items['..(j - 1)..'].data.integer;') + output:write('\n } else if (args.items[' .. (j - 1) .. '].type == kObjectTypeInteger) {') + output:write( + '\n ' .. converted .. ' = (Float)args.items[' .. (j - 1) .. '].data.integer;' + ) end -- accept empty lua tables as empty dictionaries if rt:match('^Dictionary') then - output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeArray && args.items['..(j - 1)..'].data.array.size == 0) {') --luacheck: ignore 631 - output:write('\n '..converted..' = (Dictionary)ARRAY_DICT_INIT;') + output:write( + '\n } else if (args.items[' + .. (j - 1) + .. '].type == kObjectTypeArray && args.items[' + .. (j - 1) + .. '].data.array.size == 0) {' + ) --luacheck: ignore 631 + output:write('\n ' .. converted .. ' = (Dictionary)ARRAY_DICT_INIT;') end output:write('\n } else {') - output:write('\n api_set_error(error, kErrorTypeException, \ - "Wrong type for argument '..j..' when calling '..fn.name..', expecting '..param[1]..'");') + output:write( + '\n api_set_error(error, kErrorTypeException, \ + "Wrong type for argument ' + .. j + .. ' when calling ' + .. fn.name + .. ', expecting ' + .. param[1] + .. '");' + ) output:write('\n goto cleanup;') output:write('\n }\n') end @@ -427,127 +594,127 @@ for i = 1, #functions do end -- function call - local call_args = table.concat(args, ', ') output:write('\n ') if fn.return_type ~= 'void' then -- has a return value, prefix the call with a declaration - output:write(fn.return_type..' rv = ') + output:write(fn.return_type .. ' rv = ') end -- write the function name and the opening parenthesis - output:write(fn.name..'(') + output:write(fn.name .. '(') + local call_args = {} if fn.receives_channel_id then - -- if the function receives the channel id, pass it as first argument - if #args > 0 or fn.can_fail then - output:write('channel_id, ') - if fn.receives_array_args then - -- if the function receives the array args, pass it the second argument - output:write('args, ') - end - output:write(call_args) - else - output:write('channel_id') - if fn.receives_array_args then - output:write(', args') - end - end - else - if fn.receives_array_args then - if #args > 0 or fn.call_fail then - output:write('args, '..call_args) - else - output:write('args') - end - else - output:write(call_args) - end + table.insert(call_args, 'channel_id') + end + + if fn.receives_array_args then + table.insert(call_args, 'args') end - if fn.arena_return then - output:write(', arena') + for _, a in ipairs(args) do + table.insert(call_args, a) + end + + if fn.receives_arena then + table.insert(call_args, 'arena') end if fn.has_lua_imp then - if #args > 0 then - output:write(', NULL') - else - output:write('NULL') - end + table.insert(call_args, 'NULL') + end + + if fn.can_fail then + table.insert(call_args, 'error') end + output:write(table.concat(call_args, ', ')) + output:write(');\n') + if fn.can_fail then -- if the function can fail, also pass a pointer to the local error object - if #args > 0 then - output:write(', error);\n') - else - output:write('error);\n') - end -- and check for the error output:write('\n if (ERROR_SET(error)) {') output:write('\n goto cleanup;') output:write('\n }\n') - else - output:write(');\n') end - if fn.return_type ~= 'void' then - output:write('\n ret = '..string.upper(real_type(fn.return_type))..'_OBJ(rv);') + local ret_type = real_type(fn.return_type) + if string.match(ret_type, '^KeyDict_') then + local table = string.sub(ret_type, 9) .. '_table' + output:write( + '\n ret = DICTIONARY_OBJ(api_keydict_to_dict(&rv, ' + .. table + .. ', ARRAY_SIZE(' + .. table + .. '), arena));' + ) + elseif ret_type ~= 'void' then + output:write('\n ret = ' .. string.upper(real_type(fn.return_type)) .. '_OBJ(rv);') end - output:write('\n\ncleanup:'); + output:write('\n\ncleanup:') - output:write('\n return ret;\n}\n\n'); + output:write('\n return ret;\n}\n\n') end end local remote_fns = {} -for _,fn in ipairs(functions) do +for _, fn in ipairs(functions) do if fn.remote then remote_fns[fn.name] = fn end end -remote_fns.redraw = {impl_name="ui_client_redraw", fast=true} +remote_fns.redraw = { impl_name = 'ui_client_redraw', fast = true } local names = vim.tbl_keys(remote_fns) table.sort(names) -local hashorder, hashfun = hashy.hashy_hash("msgpack_rpc_get_handler_for", names, function (idx) - return "method_handlers["..idx.."].name" +local hashorder, hashfun = hashy.hashy_hash('msgpack_rpc_get_handler_for', names, function(idx) + return 'method_handlers[' .. idx .. '].name' end) -output:write("const MsgpackRpcRequestHandler method_handlers[] = {\n") +output:write('const MsgpackRpcRequestHandler method_handlers[] = {\n') for n, name in ipairs(hashorder) do local fn = remote_fns[name] - fn.handler_id = n-1 - output:write(' { .name = "'..name..'", .fn = handle_'.. (fn.impl_name or fn.name).. - ', .fast = '..tostring(fn.fast)..', .arena_return = '..tostring(not not fn.arena_return)..'},\n') + fn.handler_id = n - 1 + output:write( + ' { .name = "' + .. name + .. '", .fn = handle_' + .. (fn.impl_name or fn.name) + .. ', .fast = ' + .. tostring(fn.fast) + .. ', .ret_alloc = ' + .. tostring(not not fn.ret_alloc) + .. '},\n' + ) end -output:write("};\n\n") +output:write('};\n\n') output:write(hashfun) output:close() functions.keysets = keysets -local mpack_output = io.open(mpack_outputf, 'wb') +local mpack_output = assert(io.open(mpack_outputf, 'wb')) mpack_output:write(mpack.encode(functions)) mpack_output:close() local function include_headers(output_handle, headers_to_include) for i = 1, #headers_to_include do if headers_to_include[i]:sub(-12) ~= '.generated.h' then - output_handle:write('\n#include "nvim/'..headers_to_include[i]..'"') + output_handle:write('\n#include "nvim/' .. headers_to_include[i] .. '"') end end end -local function write_shifted_output(_, str) +local function write_shifted_output(str, ...) str = str:gsub('\n ', '\n') str = str:gsub('^ ', '') str = str:gsub(' +$', '') - output:write(str) + output:write(string.format(str, ...)) end -- start building lua output -output = io.open(lua_c_bindings_outputf, 'wb') +output = assert(io.open(lua_c_bindings_outputf, 'wb')) output:write([[ #include <lua.h> @@ -557,6 +724,7 @@ output:write([[ #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/func_attr.h" +#include "nvim/globals.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/dispatch.h" @@ -572,42 +740,51 @@ local lua_c_functions = {} local function process_function(fn) local lua_c_function_name = ('nlua_api_%s'):format(fn.name) - write_shifted_output(output, string.format([[ + write_shifted_output( + [[ static int %s(lua_State *lstate) { Error err = ERROR_INIT; + Arena arena = ARENA_EMPTY; char *err_param = 0; if (lua_gettop(lstate) != %i) { api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s"); goto exit_0; } - ]], lua_c_function_name, #fn.parameters, #fn.parameters, - (#fn.parameters == 1) and '' or 's')) + ]], + lua_c_function_name, + #fn.parameters, + #fn.parameters, + (#fn.parameters == 1) and '' or 's' + ) lua_c_functions[#lua_c_functions + 1] = { - binding=lua_c_function_name, - api=fn.name + binding = lua_c_function_name, + api = fn.name, } if not fn.fast then - write_shifted_output(output, string.format([[ + write_shifted_output( + [[ if (!nlua_is_deferred_safe()) { return luaL_error(lstate, e_luv_api_disabled, "%s"); } - ]], fn.name)) + ]], + fn.name + ) end if fn.textlock then - write_shifted_output(output, [[ + write_shifted_output([[ if (text_locked()) { - api_set_error(&err, kErrorTypeException, "%s", get_text_locked_msg()); + api_set_error(&err, kErrorTypeException, "%%s", get_text_locked_msg()); goto exit_0; } ]]) elseif fn.textlock_allow_cmdwin then - write_shifted_output(output, [[ + write_shifted_output([[ if (textlock != 0 || expr_map_locked()) { - api_set_error(&err, kErrorTypeException, "%s", e_textlock); + api_set_error(&err, kErrorTypeException, "%%s", e_textlock); goto exit_0; } ]]) @@ -615,48 +792,69 @@ local function process_function(fn) local cparams = '' local free_code = {} - for j = #fn.parameters,1,-1 do + for j = #fn.parameters, 1, -1 do local param = fn.parameters[j] local cparam = string.format('arg%u', j) local param_type = real_type(param[1]) - local lc_param_type = real_type(param[1]):lower() - local extra = param_type == "Dictionary" and "false, " or "" - if param[1] == "Object" or param[1] == "DictionaryOf(LuaRef)" then - extra = "true, " + local extra = param_type == 'Dictionary' and 'false, ' or '' + local arg_free_code = '' + if param[1] == 'Object' then + extra = 'true, ' + arg_free_code = 'api_luarefs_free_object(' .. cparam .. ');' + elseif param[1] == 'DictionaryOf(LuaRef)' then + extra = 'true, ' + arg_free_code = 'api_luarefs_free_dict(' .. cparam .. ');' + elseif param[1] == 'LuaRef' then + arg_free_code = 'api_free_luaref(' .. cparam .. ');' end local errshift = 0 local seterr = '' if string.match(param_type, '^KeyDict_') then - write_shifted_output(output, string.format([[ - %s %s = { 0 }; nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &err);]], param_type, cparam, cparam, param_type)) - cparam = '&'..cparam + write_shifted_output( + [[ + %s %s = KEYDICT_INIT; + nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &arena, &err); + ]], + param_type, + cparam, + cparam, + param_type + ) + cparam = '&' .. cparam errshift = 1 -- free incomplete dict on error + arg_free_code = 'api_luarefs_free_keydict(' + .. cparam + .. ', ' + .. string.sub(param_type, 9) + .. '_table);' else - write_shifted_output(output, string.format([[ - const %s %s = nlua_pop_%s(lstate, %s&err);]], param[1], cparam, param_type, extra)) - seterr = [[ - err_param = "]]..param[2]..[[";]] + write_shifted_output( + [[ + const %s %s = nlua_pop_%s(lstate, %s&arena, &err);]], + param[1], + cparam, + param_type, + extra + ) + seterr = '\n err_param = "' .. param[2] .. '";' end - write_shifted_output(output, string.format([[ + write_shifted_output([[ + + if (ERROR_SET(&err)) {]] .. seterr .. [[ - if (ERROR_SET(&err)) {]]..seterr..[[ goto exit_%u; } - ]], #fn.parameters - j + errshift)) - free_code[#free_code + 1] = ('api_free_%s(%s);'):format( - lc_param_type, cparam) + ]], #fn.parameters - j + errshift) + free_code[#free_code + 1] = arg_free_code cparams = cparam .. ', ' .. cparams end if fn.receives_channel_id then cparams = 'LUA_INTERNAL_CALL, ' .. cparams end - if fn.arena_return then + if fn.receives_arena then cparams = cparams .. '&arena, ' - write_shifted_output(output, [[ - Arena arena = ARENA_EMPTY; - ]]) end if fn.has_lua_imp then @@ -673,28 +871,28 @@ local function process_function(fn) local rev_i = #free_code - i + 1 local code = free_code[rev_i] if i == 1 and not string.match(real_type(fn.parameters[1][1]), '^KeyDict_') then - free_at_exit_code = free_at_exit_code .. ('\n %s'):format(code) + free_at_exit_code = free_at_exit_code .. ('\n %s'):format(code) else - free_at_exit_code = free_at_exit_code .. ('\n exit_%u:\n %s'):format( - rev_i, code) + free_at_exit_code = free_at_exit_code .. ('\nexit_%u:\n %s'):format(rev_i, code) end end local err_throw_code = [[ - exit_0: - if (ERROR_SET(&err)) { - luaL_where(lstate, 1); - if (err_param) { - lua_pushstring(lstate, "Invalid '"); - lua_pushstring(lstate, err_param); - lua_pushstring(lstate, "': "); - } - lua_pushstring(lstate, err.msg); - api_clear_error(&err); - lua_concat(lstate, err_param ? 5 : 2); - return lua_error(lstate); +exit_0: + arena_mem_free(arena_finish(&arena)); + if (ERROR_SET(&err)) { + luaL_where(lstate, 1); + if (err_param) { + lua_pushstring(lstate, "Invalid '"); + lua_pushstring(lstate, err_param); + lua_pushstring(lstate, "': "); } - ]] + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + lua_concat(lstate, err_param ? 5 : 2); + return lua_error(lstate); + } +]] local return_type if fn.return_type ~= 'void' then if fn.return_type:match('^ArrayOf') then @@ -702,45 +900,68 @@ local function process_function(fn) else return_type = fn.return_type end - local free_retval - if fn.arena_return then - free_retval = "arena_mem_free(arena_finish(&arena));" - else - free_retval = "api_free_"..return_type:lower().."(ret);" + local free_retval = '' + if fn.ret_alloc then + free_retval = ' api_free_' .. return_type:lower() .. '(ret);' end - write_shifted_output(output, string.format([[ - const %s ret = %s(%s); - ]], fn.return_type, fn.name, cparams)) + write_shifted_output(' %s ret = %s(%s);\n', fn.return_type, fn.name, cparams) + local ret_type = real_type(fn.return_type) + local ret_mode = (ret_type == 'Object') and '&' or '' if fn.has_lua_imp then -- only push onto the Lua stack if we haven't already - write_shifted_output(output, string.format([[ + write_shifted_output(string.format( + [[ if (lua_gettop(lstate) == 0) { - nlua_push_%s(lstate, ret, true); + nlua_push_%s(lstate, %sret, true); } - ]], return_type)) + ]], + return_type, + ret_mode + )) + elseif string.match(ret_type, '^KeyDict_') then + write_shifted_output( + ' nlua_push_keydict(lstate, &ret, %s_table);\n', + string.sub(ret_type, 9) + ) else local special = (fn.since ~= nil and fn.since < 11) - write_shifted_output(output, string.format([[ - nlua_push_%s(lstate, ret, %s); - ]], return_type, tostring(special))) + write_shifted_output( + ' nlua_push_%s(lstate, %sret, %s);\n', + return_type, + ret_mode, + tostring(special) + ) end - write_shifted_output(output, string.format([[ + -- NOTE: we currently assume err_throw needs nothing from arena + write_shifted_output( + + [[ %s %s %s return 1; - ]], free_retval, free_at_exit_code, err_throw_code)) + ]], + free_retval, + free_at_exit_code, + err_throw_code + ) else - write_shifted_output(output, string.format([[ + write_shifted_output( + [[ %s(%s); %s %s return 0; - ]], fn.name, cparams, free_at_exit_code, err_throw_code)) + ]], + fn.name, + cparams, + free_at_exit_code, + err_throw_code + ) end - write_shifted_output(output, [[ + write_shifted_output([[ } ]]) end @@ -751,18 +972,25 @@ for _, fn in ipairs(functions) do end end -output:write(string.format([[ -void nlua_add_api_functions(lua_State *lstate); // silence -Wmissing-prototypes +output:write(string.format( + [[ +void nlua_add_api_functions(lua_State *lstate) + REAL_FATTR_NONNULL_ALL; void nlua_add_api_functions(lua_State *lstate) - FUNC_ATTR_NONNULL_ALL { lua_createtable(lstate, 0, %u); -]], #lua_c_functions)) +]], + #lua_c_functions +)) for _, func in ipairs(lua_c_functions) do - output:write(string.format([[ + output:write(string.format( + [[ lua_pushcfunction(lstate, &%s); - lua_setfield(lstate, -2, "%s");]], func.binding, func.api)) + lua_setfield(lstate, -2, "%s");]], + func.binding, + func.api + )) end output:write([[ diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index e2af5f8d44..0808f71daa 100644 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -10,11 +10,11 @@ local client_output = io.open(arg[5], 'wb') local c_grammar = require('generators.c_grammar') local events = c_grammar.grammar:match(input:read('*all')) -local hashy = require'generators.hashy' +local hashy = require 'generators.hashy' local function write_signature(output, ev, prefix, notype) - output:write('('..prefix) - if prefix == "" and #ev.parameters == 0 then + output:write('(' .. prefix) + if prefix == '' and #ev.parameters == 0 then output:write('void') end for j = 1, #ev.parameters do @@ -23,7 +23,7 @@ local function write_signature(output, ev, prefix, notype) end local param = ev.parameters[j] if not notype then - output:write(param[1]..' ') + output:write(param[1] .. ' ') end output:write(param[2]) end @@ -35,26 +35,28 @@ local function write_arglist(output, ev) local param = ev.parameters[j] local kind = string.upper(param[1]) output:write(' ADD_C(args, ') - output:write(kind..'_OBJ('..param[2]..')') + output:write(kind .. '_OBJ(' .. param[2] .. ')') output:write(');\n') end end local function call_ui_event_method(output, ev) - output:write('void ui_client_event_'..ev.name..'(Array args)\n{\n') + output:write('void ui_client_event_' .. ev.name .. '(Array args)\n{\n') local hlattrs_args_count = 0 if #ev.parameters > 0 then - output:write(' if (args.size < '..(#ev.parameters)) + output:write(' if (args.size < ' .. #ev.parameters) for j = 1, #ev.parameters do local kind = ev.parameters[j][1] - if kind ~= "Object" then - if kind == 'HlAttrs' then kind = 'Dictionary' end - output:write('\n || args.items['..(j-1)..'].type != kObjectType'..kind..'') + if kind ~= 'Object' then + if kind == 'HlAttrs' then + kind = 'Dictionary' + end + output:write('\n || args.items[' .. (j - 1) .. '].type != kObjectType' .. kind .. '') end end output:write(') {\n') - output:write(' ELOG("Error handling ui event \''..ev.name..'\'");\n') + output:write(' ELOG("Error handling ui event \'' .. ev.name .. '\'");\n') output:write(' return;\n') output:write(' }\n') end @@ -62,106 +64,118 @@ local function call_ui_event_method(output, ev) for j = 1, #ev.parameters do local param = ev.parameters[j] local kind = param[1] - output:write(' '..kind..' arg_'..j..' = ') + output:write(' ' .. kind .. ' arg_' .. j .. ' = ') if kind == 'HlAttrs' then -- The first HlAttrs argument is rgb_attrs and second is cterm_attrs - output:write('ui_client_dict2hlattrs(args.items['..(j-1)..'].data.dictionary, '..(hlattrs_args_count == 0 and 'true' or 'false')..');\n') + output:write( + 'ui_client_dict2hlattrs(args.items[' + .. (j - 1) + .. '].data.dictionary, ' + .. (hlattrs_args_count == 0 and 'true' or 'false') + .. ');\n' + ) hlattrs_args_count = hlattrs_args_count + 1 elseif kind == 'Object' then - output:write('args.items['..(j-1)..'];\n') + output:write('args.items[' .. (j - 1) .. '];\n') elseif kind == 'Window' then - output:write('(Window)args.items['..(j-1)..'].data.integer;\n') + output:write('(Window)args.items[' .. (j - 1) .. '].data.integer;\n') else - output:write('args.items['..(j-1)..'].data.'..string.lower(kind)..';\n') + output:write('args.items[' .. (j - 1) .. '].data.' .. string.lower(kind) .. ';\n') end end - output:write(' tui_'..ev.name..'(tui') + output:write(' tui_' .. ev.name .. '(tui') for j = 1, #ev.parameters do - output:write(', arg_'..j) + output:write(', arg_' .. j) end output:write(');\n') output:write('}\n\n') end +events = vim.tbl_filter(function(ev) + return ev[1] ~= 'empty' +end, events) + for i = 1, #events do local ev = events[i] assert(ev.return_type == 'void') if ev.since == nil and not ev.noexport then - print("Ui event "..ev.name.." lacks since field.\n") + print('Ui event ' .. ev.name .. ' lacks since field.\n') os.exit(1) end ev.since = tonumber(ev.since) if not ev.remote_only then - if not ev.remote_impl and not ev.noexport then - remote_output:write('void remote_ui_'..ev.name) - write_signature(remote_output, ev, 'UI *ui') + remote_output:write('void remote_ui_' .. ev.name) + write_signature(remote_output, ev, 'RemoteUI *ui') remote_output:write('\n{\n') - remote_output:write(' UIData *data = ui->data;\n') - remote_output:write(' Array args = data->call_buf;\n') + remote_output:write(' Array args = ui->call_buf;\n') write_arglist(remote_output, ev) - remote_output:write(' push_call(ui, "'..ev.name..'", args);\n') + remote_output:write(' push_call(ui, "' .. ev.name .. '", args);\n') remote_output:write('}\n\n') end end if not (ev.remote_only and ev.remote_impl) then - call_output:write('void ui_call_'..ev.name) + call_output:write('void ui_call_' .. ev.name) write_signature(call_output, ev, '') call_output:write('\n{\n') if ev.remote_only then call_output:write(' Array args = call_buf;\n') write_arglist(call_output, ev) - call_output:write(' ui_call_event("'..ev.name..'", args);\n') + call_output:write(' ui_call_event("' .. ev.name .. '", args);\n') elseif ev.compositor_impl then - call_output:write(' ui_comp_'..ev.name) + call_output:write(' ui_comp_' .. ev.name) write_signature(call_output, ev, '', true) - call_output:write(";\n") + call_output:write(';\n') call_output:write(' UI_CALL') - write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true) - call_output:write(";\n") + write_signature(call_output, ev, '!ui->composed, ' .. ev.name .. ', ui', true) + call_output:write(';\n') else call_output:write(' UI_CALL') - write_signature(call_output, ev, 'true, '..ev.name..', ui', true) - call_output:write(";\n") + write_signature(call_output, ev, 'true, ' .. ev.name .. ', ui', true) + call_output:write(';\n') end - call_output:write("}\n\n") + call_output:write('}\n\n') end if ev.compositor_impl then - call_output:write('void ui_composed_call_'..ev.name) + call_output:write('void ui_composed_call_' .. ev.name) write_signature(call_output, ev, '') call_output:write('\n{\n') call_output:write(' UI_CALL') - write_signature(call_output, ev, 'ui->composed, '..ev.name..', ui', true) - call_output:write(";\n") - call_output:write("}\n\n") + write_signature(call_output, ev, 'ui->composed, ' .. ev.name .. ', ui', true) + call_output:write(';\n') + call_output:write('}\n\n') end - if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) and (not ev.client_ignore) then + if (not ev.remote_only) and not ev.noexport and not ev.client_impl and not ev.client_ignore then call_ui_event_method(client_output, ev) end end local client_events = {} -for _,ev in ipairs(events) do - if (not ev.noexport) and ((not ev.remote_only) or ev.client_impl) and (not ev.client_ignore) then +for _, ev in ipairs(events) do + if (not ev.noexport) and ((not ev.remote_only) or ev.client_impl) and not ev.client_ignore then client_events[ev.name] = ev end end -local hashorder, hashfun = hashy.hashy_hash("ui_client_handler", vim.tbl_keys(client_events), function (idx) - return "event_handlers["..idx.."].name" -end) +local hashorder, hashfun = hashy.hashy_hash( + 'ui_client_handler', + vim.tbl_keys(client_events), + function(idx) + return 'event_handlers[' .. idx .. '].name' + end +) -client_output:write("static const UIClientHandler event_handlers[] = {\n") +client_output:write('static const UIClientHandler event_handlers[] = {\n') for _, name in ipairs(hashorder) do - client_output:write(' { .name = "'..name..'", .fn = ui_client_event_'..name..'},\n') + client_output:write(' { .name = "' .. name .. '", .fn = ui_client_event_' .. name .. '},\n') end client_output:write('\n};\n\n') @@ -172,25 +186,22 @@ remote_output:close() client_output:close() -- don't expose internal attributes like "impl_name" in public metadata -local exported_attributes = {'name', 'parameters', - 'since', 'deprecated_since'} +local exported_attributes = { 'name', 'parameters', 'since', 'deprecated_since' } local exported_events = {} -for _,ev in ipairs(events) do +for _, ev in ipairs(events) do local ev_exported = {} - for _,attr in ipairs(exported_attributes) do + for _, attr in ipairs(exported_attributes) do ev_exported[attr] = ev[attr] end - for _,p in ipairs(ev_exported.parameters) do + for _, p in ipairs(ev_exported.parameters) do if p[1] == 'HlAttrs' then p[1] = 'Dictionary' end end if not ev.noexport then - exported_events[#exported_events+1] = ev_exported + exported_events[#exported_events + 1] = ev_exported end end -local packed = mpack.encode(exported_events) -local dump_bin_array = require("generators.dump_bin_array") -dump_bin_array(metadata_output, 'ui_events_metadata', packed) +metadata_output:write(mpack.encode(exported_events)) metadata_output:close() diff --git a/src/nvim/generators/gen_char_blob.lua b/src/nvim/generators/gen_char_blob.lua index 11f6cbcc13..c40e0d6e82 100644 --- a/src/nvim/generators/gen_char_blob.lua +++ b/src/nvim/generators/gen_char_blob.lua @@ -1,6 +1,6 @@ if arg[1] == '--help' then print('Usage:') - print(' '..arg[0]..' [-c] target source varname [source varname]...') + print(' ' .. arg[0] .. ' [-c] target source varname [source varname]...') print('') print('Generates C file with big uint8_t blob.') print('Blob will be stored in a static const array named varname.') @@ -12,7 +12,7 @@ end local options = {} while true do - local opt = string.match(arg[1], "^-(%w)") + local opt = string.match(arg[1], '^-(%w)') if not opt then break end @@ -36,33 +36,33 @@ for argi = 2, #arg, 2 do local source_file = arg[argi] local modname = arg[argi + 1] if modnames[modname] then - error(string.format("modname %q is already specified for file %q", modname, modnames[modname])) + error(string.format('modname %q is already specified for file %q', modname, modnames[modname])) end modnames[modname] = source_file - local varname = string.gsub(modname,'%.','_dot_').."_module" + local varname = string.gsub(modname, '%.', '_dot_') .. '_module' target:write(('static const uint8_t %s[] = {\n'):format(varname)) local output if options.c then - local luac = os.getenv("LUAC_PRG") - if luac and luac ~= "" then - output = io.popen(luac:format(source_file), "r"):read("*a") + local luac = os.getenv('LUAC_PRG') + if luac and luac ~= '' then + output = io.popen(luac:format(source_file), 'r'):read('*a') elseif warn_on_missing_compiler then - print("LUAC_PRG is missing, embedding raw source") + print('LUAC_PRG is missing, embedding raw source') warn_on_missing_compiler = false end end if not output then - local source = io.open(source_file, "r") - or error(string.format("source_file %q doesn't exist", source_file)) - output = source:read("*a") + local source = io.open(source_file, 'r') + or error(string.format("source_file %q doesn't exist", source_file)) + output = source:read('*a') source:close() end local num_bytes = 0 - local MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line + local MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line target:write(' ') local increase_num_bytes @@ -81,8 +81,11 @@ for argi = 2, #arg, 2 do end target:write(' 0};\n') - if modname ~= "_" then - table.insert(index_items, ' { "'..modname..'", '..varname..', sizeof '..varname..' },\n\n') + if modname ~= '_' then + table.insert( + index_items, + ' { "' .. modname .. '", ' .. varname .. ', sizeof ' .. varname .. ' },\n\n' + ) end end diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua index f9e9c6b0a8..5d1e586fe6 100644 --- a/src/nvim/generators/gen_declarations.lua +++ b/src/nvim/generators/gen_declarations.lua @@ -5,9 +5,9 @@ local preproc_fname = arg[4] local lpeg = vim.lpeg -local fold = function (func, ...) +local fold = function(func, ...) local result = nil - for _, v in ipairs({...}) do + for _, v in ipairs({ ... }) do if result == nil then result = v else @@ -17,144 +17,112 @@ local fold = function (func, ...) return result end -local folder = function (func) - return function (...) +local folder = function(func) + return function(...) return fold(func, ...) end end local lit = lpeg.P local set = function(...) - return lpeg.S(fold(function (a, b) return a .. b end, ...)) + return lpeg.S(fold(function(a, b) + return a .. b + end, ...)) end local any_character = lpeg.P(1) -local rng = function(s, e) return lpeg.R(s .. e) end -local concat = folder(function (a, b) return a * b end) -local branch = folder(function (a, b) return a + b end) -local one_or_more = function(v) return v ^ 1 end -local two_or_more = function(v) return v ^ 2 end -local any_amount = function(v) return v ^ 0 end -local one_or_no = function(v) return v ^ -1 end +local rng = function(s, e) + return lpeg.R(s .. e) +end +local concat = folder(function(a, b) + return a * b +end) +local branch = folder(function(a, b) + return a + b +end) +local one_or_more = function(v) + return v ^ 1 +end +local two_or_more = function(v) + return v ^ 2 +end +local any_amount = function(v) + return v ^ 0 +end +local one_or_no = function(v) + return v ^ -1 +end local look_behind = lpeg.B -local look_ahead = function(v) return #v end -local neg_look_ahead = function(v) return -v end -local neg_look_behind = function(v) return -look_behind(v) end +local look_ahead = function(v) + return #v +end +local neg_look_ahead = function(v) + return -v +end +local neg_look_behind = function(v) + return -look_behind(v) +end -local w = branch( - rng('a', 'z'), - rng('A', 'Z'), - lit('_') -) -local aw = branch( - w, - rng('0', '9') -) +local w = branch(rng('a', 'z'), rng('A', 'Z'), lit('_')) +local aw = branch(w, rng('0', '9')) local s = set(' ', '\n', '\t') local raw_word = concat(w, any_amount(aw)) -local right_word = concat( - raw_word, - neg_look_ahead(aw) -) +local right_word = concat(raw_word, neg_look_ahead(aw)) local word = branch( concat( branch(lit('ArrayOf('), lit('DictionaryOf('), lit('Dict(')), -- typed container macro one_or_more(any_character - lit(')')), lit(')') ), - concat( - neg_look_behind(aw), - right_word - ) -) -local inline_comment = concat( - lit('/*'), - any_amount(concat( - neg_look_ahead(lit('*/')), - any_character - )), - lit('*/') + concat(neg_look_behind(aw), right_word) ) +local inline_comment = + concat(lit('/*'), any_amount(concat(neg_look_ahead(lit('*/')), any_character)), lit('*/')) local spaces = any_amount(branch( s, -- Comments are really handled by preprocessor, so the following is not needed inline_comment, - concat( - lit('//'), - any_amount(concat( - neg_look_ahead(lit('\n')), - any_character - )), - lit('\n') - ), + concat(lit('//'), any_amount(concat(neg_look_ahead(lit('\n')), any_character)), lit('\n')), -- Linemarker inserted by preprocessor - concat( - lit('# '), - any_amount(concat( - neg_look_ahead(lit('\n')), - any_character - )), - lit('\n') - ) + concat(lit('# '), any_amount(concat(neg_look_ahead(lit('\n')), any_character)), lit('\n')) )) -local typ_part = concat( - word, - any_amount(concat( - spaces, - lit('*') - )), - spaces -) +local typ_part = concat(word, any_amount(concat(spaces, lit('*'))), spaces) local typ_id = two_or_more(typ_part) -local arg = typ_id -- argument name is swallowed by typ +local arg = typ_id -- argument name is swallowed by typ local pattern = concat( any_amount(branch(set(' ', '\t'), inline_comment)), - typ_id, -- return type with function name + typ_id, -- return type with function name spaces, lit('('), spaces, - one_or_no(branch( -- function arguments + one_or_no(branch( -- function arguments concat( - arg, -- first argument, does not require comma - any_amount(concat( -- following arguments, start with a comma + arg, -- first argument, does not require comma + any_amount(concat( -- following arguments, start with a comma spaces, lit(','), spaces, arg, - any_amount(concat( - lit('['), - spaces, - any_amount(aw), - spaces, - lit(']') - )) + any_amount(concat(lit('['), spaces, any_amount(aw), spaces, lit(']'))) )), - one_or_no(concat( - spaces, - lit(','), - spaces, - lit('...') - )) + one_or_no(concat(spaces, lit(','), spaces, lit('...'))) ), - lit('void') -- also accepts just void + lit('void') -- also accepts just void )), spaces, lit(')'), - any_amount(concat( -- optional attributes + any_amount(concat( -- optional attributes spaces, lit('FUNC_'), any_amount(aw), - one_or_no(concat( -- attribute argument + one_or_no(concat( -- attribute argument spaces, lit('('), - any_amount(concat( - neg_look_ahead(lit(')')), - any_character - )), + any_amount(concat(neg_look_ahead(lit(')')), any_character)), lit(')') )) )), - look_ahead(concat( -- definition must be followed by "{" + look_ahead(concat( -- definition must be followed by "{" spaces, lit('{') )) @@ -164,7 +132,7 @@ if fname == '--help' then print([[ Usage: - gendeclarations.lua definitions.c static.h non-static.h definitions.i + gen_declarations.lua definitions.c static.h non-static.h definitions.i Generates declarations for a C file definitions.c, putting declarations for static functions into static.h and declarations for non-static functions into @@ -198,21 +166,13 @@ Additionally uses the following environment variables: end local preproc_f = io.open(preproc_fname) -local text = preproc_f:read("*all") +local text = preproc_f:read('*all') preproc_f:close() - -local header = [[ +local non_static = [[ #define DEFINE_FUNC_ATTRIBUTES #include "nvim/func_attr.h" #undef DEFINE_FUNC_ATTRIBUTES -]] - -local footer = [[ -#include "nvim/func_attr.h" -]] - -local non_static = header .. [[ #ifndef DLLEXPORT # ifdef MSWIN # define DLLEXPORT __declspec(dllexport) @@ -222,7 +182,20 @@ local non_static = header .. [[ #endif ]] -local static = header +local static = [[ +#define DEFINE_FUNC_ATTRIBUTES +#include "nvim/func_attr.h" +#undef DEFINE_FUNC_ATTRIBUTES +]] + +local non_static_footer = [[ +#include "nvim/func_attr.h" +]] + +local static_footer = [[ +#define DEFINE_EMPTY_ATTRIBUTES +#include "nvim/func_attr.h" // IWYU pragma: export +]] if fname:find('.*/src/nvim/.*%.c$') then -- Add an IWYU pragma comment if the corresponding .h file exists. @@ -234,6 +207,22 @@ if fname:find('.*/src/nvim/.*%.c$') then // IWYU pragma: private, include "%s" ]]):format(header_fname:gsub('.*/src/nvim/', 'nvim/')) .. non_static end +elseif non_static_fname:find('/include/api/private/dispatch_wrappers%.h%.generated%.h$') then + non_static = [[ +// IWYU pragma: private, include "nvim/api/private/dispatch.h" +]] .. non_static +elseif non_static_fname:find('/include/ui_events_call%.h%.generated%.h$') then + non_static = [[ +// IWYU pragma: private, include "nvim/ui.h" +]] .. non_static +elseif non_static_fname:find('/include/ui_events_client%.h%.generated%.h$') then + non_static = [[ +// IWYU pragma: private, include "nvim/ui_client.h" +]] .. non_static +elseif non_static_fname:find('/include/ui_events_remote%.h%.generated%.h$') then + non_static = [[ +// IWYU pragma: private, include "nvim/api/ui.h" +]] .. non_static end local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"' @@ -283,8 +272,7 @@ while init ~= nil do declaration = declaration .. ';' if os.getenv('NVIM_GEN_DECLARATIONS_LINE_NUMBERS') == '1' then - declaration = declaration .. (' // %s/%s:%u'):format( - curdir, curfile, declline) + declaration = declaration .. (' // %s/%s:%u'):format(curdir, curfile, declline) end declaration = declaration .. '\n' if declaration:sub(1, 6) == 'static' then @@ -307,8 +295,8 @@ while init ~= nil do end end -non_static = non_static .. footer -static = static .. footer +non_static = non_static .. non_static_footer +static = static .. static_footer local F F = io.open(static_fname, 'w') diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua index 7b272c337e..1ce7f1af9d 100644 --- a/src/nvim/generators/gen_eval.lua +++ b/src/nvim/generators/gen_eval.lua @@ -8,7 +8,7 @@ local funcsfname = autodir .. '/funcs.generated.h' --Will generate funcs.generated.h with definition of functions static const array. -local hashy = require'generators.hashy' +local hashy = require 'generators.hashy' local hashpipe = assert(io.open(funcsfname, 'wb')) @@ -48,18 +48,18 @@ hashpipe:write([[ local funcs = require('eval').funcs for _, func in pairs(funcs) do if func.float_func then - func.func = "float_op_wrapper" - func.data = "{ .float_func = &"..func.float_func.." }" + func.func = 'float_op_wrapper' + func.data = '{ .float_func = &' .. func.float_func .. ' }' end end -local metadata = mpack.decode(io.open(metadata_file, 'rb'):read("*all")) -for _,fun in ipairs(metadata) do +local metadata = mpack.decode(io.open(metadata_file, 'rb'):read('*all')) +for _, fun in ipairs(metadata) do if fun.eval then funcs[fun.name] = { - args=#fun.parameters, - func='api_wrapper', - data='{ .api_handler = &method_handlers['..fun.handler_id..'] }' + args = #fun.parameters, + func = 'api_wrapper', + data = '{ .api_handler = &method_handlers[' .. fun.handler_id .. '] }', } end end @@ -74,28 +74,37 @@ local funcsdata = assert(io.open(funcs_file, 'w')) funcsdata:write(mpack.encode(func_names)) funcsdata:close() -local neworder, hashfun = hashy.hashy_hash("find_internal_func", func_names, function (idx) - return "functions["..idx.."].name" +local neworder, hashfun = hashy.hashy_hash('find_internal_func', func_names, function(idx) + return 'functions[' .. idx .. '].name' end) -hashpipe:write("static const EvalFuncDef functions[] = {\n") +hashpipe:write('static const EvalFuncDef functions[] = {\n') for _, name in ipairs(neworder) do local def = funcs[name] local args = def.args or 0 if type(args) == 'number' then - args = {args, args} + args = { args, args } elseif #args == 1 then args[2] = 'MAX_FUNC_ARGS' end - local base = def.base or "BASE_NONE" + local base = def.base or 'BASE_NONE' local func = def.func or ('f_' .. name) - local data = def.data or "{ .null = NULL }" + local data = def.data or '{ .null = NULL }' local fast = def.fast and 'true' or 'false' - hashpipe:write((' { "%s", %s, %s, %s, %s, &%s, %s },\n') - :format(name, args[1], args[2], base, fast, func, data)) + hashpipe:write( + (' { "%s", %s, %s, %s, %s, &%s, %s },\n'):format( + name, + args[1], + args[2], + base, + fast, + func, + data + ) + ) end hashpipe:write(' { NULL, 0, 0, BASE_NONE, false, NULL, { .null = NULL } },\n') -hashpipe:write("};\n\n") +hashpipe:write('};\n\n') hashpipe:write(hashfun) hashpipe:close() diff --git a/src/nvim/generators/gen_events.lua b/src/nvim/generators/gen_events.lua index 4763a2f463..ee48e918e8 100644 --- a/src/nvim/generators/gen_events.lua +++ b/src/nvim/generators/gen_events.lua @@ -22,7 +22,7 @@ static const struct event_name { for i, event in ipairs(events) do enum_tgt:write(('\n EVENT_%s = %u,'):format(event:upper(), i - 1)) names_tgt:write(('\n {%u, "%s", EVENT_%s},'):format(#event, event, event:upper())) - if i == #events then -- Last item. + if i == #events then -- Last item. enum_tgt:write(('\n NUM_EVENTS = %u,'):format(i)) end end @@ -41,15 +41,15 @@ names_tgt:write('\n};\n') do names_tgt:write('\nstatic AutoCmdVec autocmds[NUM_EVENTS] = {\n ') local line_len = 1 - for _ = 1,((#events) - 1) do - line_len = line_len + #(' KV_INITIAL_VALUE,') + for _ = 1, (#events - 1) do + line_len = line_len + #' KV_INITIAL_VALUE,' if line_len > 80 then names_tgt:write('\n ') - line_len = 1 + #(' KV_INITIAL_VALUE,') + line_len = 1 + #' KV_INITIAL_VALUE,' end names_tgt:write(' KV_INITIAL_VALUE,') end - if line_len + #(' KV_INITIAL_VALUE') > 80 then + if line_len + #' KV_INITIAL_VALUE' > 80 then names_tgt:write('\n KV_INITIAL_VALUE') else names_tgt:write(' KV_INITIAL_VALUE') diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua index ae8c952648..e8d1aac182 100644 --- a/src/nvim/generators/gen_ex_cmds.lua +++ b/src/nvim/generators/gen_ex_cmds.lua @@ -21,24 +21,32 @@ local a_to_z = byte_z - byte_a + 1 -- Table giving the index of the first command in cmdnames[] to lookup -- based on the first letter of a command. -local cmdidxs1_out = string.format([[ +local cmdidxs1_out = string.format( + [[ static const uint16_t cmdidxs1[%u] = { -]], a_to_z) +]], + a_to_z +) -- Table giving the index of the first command in cmdnames[] to lookup -- based on the first 2 letters of a command. -- Values in cmdidxs2[c1][c2] are relative to cmdidxs1[c1] so that they -- fit in a byte. -local cmdidxs2_out = string.format([[ +local cmdidxs2_out = string.format( + [[ static const uint8_t cmdidxs2[%u][%u] = { /* a b c d e f g h i j k l m n o p q r s t u v w x y z */ -]], a_to_z, a_to_z) +]], + a_to_z, + a_to_z +) enumfile:write([[ // IWYU pragma: private, include "nvim/ex_cmds_defs.h" typedef enum CMD_index { ]]) -defsfile:write(string.format([[ +defsfile:write(string.format( + [[ #include "nvim/arglist.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" @@ -79,23 +87,34 @@ defsfile:write(string.format([[ static const int command_count = %u; static CommandDefinition cmdnames[%u] = { -]], #defs, #defs)) +]], + #defs, + #defs +)) local cmds, cmdidxs1, cmdidxs2 = {}, {}, {} for _, cmd in ipairs(defs) do if bit.band(cmd.flags, flags.RANGE) == flags.RANGE then - assert(cmd.addr_type ~= 'ADDR_NONE', - string.format('ex_cmds.lua:%s: Using RANGE with ADDR_NONE\n', cmd.command)) + assert( + cmd.addr_type ~= 'ADDR_NONE', + string.format('ex_cmds.lua:%s: Using RANGE with ADDR_NONE\n', cmd.command) + ) else - assert(cmd.addr_type == 'ADDR_NONE', - string.format('ex_cmds.lua:%s: Missing ADDR_NONE\n', cmd.command)) + assert( + cmd.addr_type == 'ADDR_NONE', + string.format('ex_cmds.lua:%s: Missing ADDR_NONE\n', cmd.command) + ) end if bit.band(cmd.flags, flags.DFLALL) == flags.DFLALL then - assert(cmd.addr_type ~= 'ADDR_OTHER' and cmd.addr_type ~= 'ADDR_NONE', - string.format('ex_cmds.lua:%s: Missing misplaced DFLALL\n', cmd.command)) + assert( + cmd.addr_type ~= 'ADDR_OTHER' and cmd.addr_type ~= 'ADDR_NONE', + string.format('ex_cmds.lua:%s: Missing misplaced DFLALL\n', cmd.command) + ) end if bit.band(cmd.flags, flags.PREVIEW) == flags.PREVIEW then - assert(cmd.preview_func ~= nil, - string.format('ex_cmds.lua:%s: Missing preview_func\n', cmd.command)) + assert( + cmd.preview_func ~= nil, + string.format('ex_cmds.lua:%s: Missing preview_func\n', cmd.command) + ) end local enumname = cmd.enum or ('CMD_' .. cmd.command) local byte_cmd = cmd.command:sub(1, 1):byte() @@ -104,12 +123,13 @@ for _, cmd in ipairs(defs) do end local preview_func if cmd.preview_func then - preview_func = string.format("&%s", cmd.preview_func) + preview_func = string.format('&%s', cmd.preview_func) else - preview_func = "NULL" + preview_func = 'NULL' end enumfile:write(' ' .. enumname .. ',\n') - defsfile:write(string.format([[ + defsfile:write(string.format( + [[ [%s] = { .cmd_name = "%s", .cmd_func = (ex_func_T)&%s, @@ -117,7 +137,14 @@ for _, cmd in ipairs(defs) do .cmd_argt = %uL, .cmd_addr_type = %s }, -]], enumname, cmd.command, cmd.func, preview_func, cmd.flags, cmd.addr_type)) +]], + enumname, + cmd.command, + cmd.func, + preview_func, + cmd.flags, + cmd.addr_type + )) end for i = #cmds, 1, -1 do local cmd = cmds[i] @@ -141,10 +168,12 @@ for i = byte_a, byte_z do cmdidxs2_out = cmdidxs2_out .. ' /* ' .. c1 .. ' */ {' for j = byte_a, byte_z do local c2 = string.char(j) - cmdidxs2_out = cmdidxs2_out .. - ((cmdidxs2[c1] and cmdidxs2[c1][c2]) - and string.format('%3d', cmdidxs2[c1][c2] - cmdidxs1[c1]) - or ' 0') .. ',' + cmdidxs2_out = cmdidxs2_out + .. ((cmdidxs2[c1] and cmdidxs2[c1][c2]) and string.format( + '%3d', + cmdidxs2[c1][c2] - cmdidxs1[c1] + ) or ' 0') + .. ',' end cmdidxs2_out = cmdidxs2_out .. ' },\n' end @@ -154,8 +183,12 @@ enumfile:write([[ CMD_USER_BUF = -2 } cmdidx_T; ]]) -defsfile:write(string.format([[ +defsfile:write(string.format( + [[ }; %s}; %s}; -]], cmdidxs1_out, cmdidxs2_out)) +]], + cmdidxs1_out, + cmdidxs2_out +)) diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index 26ade2745d..749844e658 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -15,40 +15,43 @@ local options = require('options') local cstr = options.cstr -local type_flags={ - bool='P_BOOL', - number='P_NUM', - string='P_STRING', +local redraw_flags = { + ui_option = 'P_UI_OPTION', + tabline = 'P_RTABL', + statuslines = 'P_RSTAT', + current_window = 'P_RWIN', + current_buffer = 'P_RBUF', + all_windows = 'P_RALL', + curswant = 'P_CURSWANT', + highlight_only = 'P_HLONLY', } -local redraw_flags={ - ui_option='P_UI_OPTION', - tabline='P_RTABL', - statuslines='P_RSTAT', - current_window='P_RWIN', - current_window_only='P_RWINONLY', - current_buffer='P_RBUF', - all_windows='P_RALL', - curswant='P_CURSWANT', +local list_flags = { + comma = 'P_COMMA', + onecomma = 'P_ONECOMMA', + commacolon = 'P_COMMA|P_COLON', + onecommacolon = 'P_ONECOMMA|P_COLON', + flags = 'P_FLAGLIST', + flagscomma = 'P_COMMA|P_FLAGLIST', } -local list_flags={ - comma='P_COMMA', - onecomma='P_ONECOMMA', - commacolon='P_COMMA|P_COLON', - onecommacolon='P_ONECOMMA|P_COLON', - flags='P_FLAGLIST', - flagscomma='P_COMMA|P_FLAGLIST', -} +--- @param s string +--- @return string +local lowercase_to_titlecase = function(s) + return s:sub(1, 1):upper() .. s:sub(2) +end --- @param o vim.option_meta --- @return string local function get_flags(o) - --- @type string[] - local ret = {type_flags[o.type]} + --- @type string + local flags = '0' + + --- @param f string local add_flag = function(f) - ret[1] = ret[1] .. '|' .. f + flags = flags .. '|' .. f end + if o.list then add_flag(list_flags[o.list]) end @@ -64,19 +67,19 @@ local function get_flags(o) end end for _, flag_desc in ipairs({ - {'alloced'}, - {'nodefault'}, - {'no_mkrc'}, - {'secure'}, - {'gettext'}, - {'noglob'}, - {'normal_fname_chars', 'P_NFNAME'}, - {'normal_dname_chars', 'P_NDNAME'}, - {'pri_mkrc'}, - {'deny_in_modelines', 'P_NO_ML'}, - {'deny_duplicates', 'P_NODUP'}, - {'modelineexpr', 'P_MLE'}, - {'func'} + { 'alloced' }, + { 'nodefault' }, + { 'no_mkrc' }, + { 'secure' }, + { 'gettext' }, + { 'noglob' }, + { 'normal_fname_chars', 'P_NFNAME' }, + { 'normal_dname_chars', 'P_NDNAME' }, + { 'pri_mkrc' }, + { 'deny_in_modelines', 'P_NO_ML' }, + { 'deny_duplicates', 'P_NODUP' }, + { 'modelineexpr', 'P_MLE' }, + { 'func' }, }) do local key_name = flag_desc[1] local def_name = flag_desc[2] or ('P_' .. key_name:upper()) @@ -84,7 +87,22 @@ local function get_flags(o) add_flag(def_name) end end - return ret[1] + return flags +end + +--- @param o vim.option_meta +--- @return string +local function get_type_flags(o) + local opt_types = (type(o.type) == 'table') and o.type or { o.type } + local type_flags = '0' + assert(type(opt_types) == 'table') + + for _, opt_type in ipairs(opt_types) do + assert(type(opt_type) == 'string') + type_flags = ('%s | (1 << kOptValType%s)'):format(type_flags, lowercase_to_titlecase(opt_type)) + end + + return type_flags end --- @param c string|string[] @@ -107,23 +125,27 @@ local function get_cond(c, base_string) return cond_string end -local value_dumpers = { - ['function']=function(v) return v() end, - string=cstr, - boolean=function(v) return v and 'true' or 'false' end, - number=function(v) return ('%iL'):format(v) end, - ['nil']=function(_) return '0' end, -} - -local get_value = function(v) - return '(void *) ' .. value_dumpers[type(v)](v) -end - -local get_defaults = function(d,n) +local get_defaults = function(d, n) if d == nil then - error("option '"..n.."' should have a default value") - end - return get_value(d) + error("option '" .. n .. "' should have a default value") + end + + local value_dumpers = { + ['function'] = function(v) + return v() + end, + string = function(v) + return '.string=' .. cstr(v) + end, + boolean = function(v) + return '.boolean=' .. (v and 'true' or 'false') + end, + number = function(v) + return ('.number=%iL'):format(v) + end, + } + + return value_dumpers[type(d)](d) end --- @type {[1]:string,[2]:string}[] @@ -138,12 +160,13 @@ local function dump_option(i, o) w(' .shortname=' .. cstr(o.abbreviation)) end w(' .flags=' .. get_flags(o)) + w(' .type_flags=' .. get_type_flags(o)) if o.enable_if then w(get_cond(o.enable_if)) end if o.varname then w(' .var=&' .. o.varname) - -- Immutable options should directly point to the default value + -- Immutable options can directly point to the default value. elseif o.immutable then w((' .var=&options[%u].def_val'):format(i - 1)) elseif #o.scope == 1 and o.scope[1] == 'window' then @@ -153,20 +176,21 @@ local function dump_option(i, o) if #o.scope == 1 and o.scope[1] == 'global' then w(' .indir=PV_NONE') else - assert (#o.scope == 1 or #o.scope == 2) - assert (#o.scope == 1 or o.scope[1] == 'global') + assert(#o.scope == 1 or #o.scope == 2) + assert(#o.scope == 1 or o.scope[1] == 'global') local min_scope = o.scope[#o.scope] - local varname = o.pv_name or o.varname or ( - 'p_' .. (o.abbreviation or o.full_name)) + local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name)) local pv_name = ( - 'OPT_' .. min_scope:sub(1, 3):upper() .. '(' .. ( - min_scope:sub(1, 1):upper() .. 'V_' .. varname:sub(3):upper() - ) .. ')' + 'OPT_' + .. min_scope:sub(1, 3):upper() + .. '(' + .. (min_scope:sub(1, 1):upper() .. 'V_' .. varname:sub(3):upper()) + .. ')' ) if #o.scope == 2 then pv_name = 'OPT_BOTH(' .. pv_name .. ')' end - table.insert(defines, { 'PV_' .. varname:sub(3):upper() , pv_name}) + table.insert(defines, { 'PV_' .. varname:sub(3):upper(), pv_name }) w(' .indir=' .. pv_name) end if o.cb then @@ -185,11 +209,11 @@ local function dump_option(i, o) if o.defaults.condition then w(get_cond(o.defaults.condition)) end - w(' .def_val=' .. get_defaults(o.defaults.if_true, o.full_name)) + w(' .def_val' .. get_defaults(o.defaults.if_true, o.full_name)) if o.defaults.condition then if o.defaults.if_false then w('#else') - w(' .def_val=' .. get_defaults(o.defaults.if_false, o.full_name)) + w(' .def_val' .. get_defaults(o.defaults.if_false, o.full_name)) end w('#endif') end @@ -213,11 +237,9 @@ static vimoption_T options[] = {]]) for i, o in ipairs(options.options) do dump_option(i, o) end -w(' [' .. ('%u'):format(#options.options) .. ']={.fullname=NULL}') w('};') w('') for _, v in ipairs(defines) do w('#define ' .. v[1] .. ' ' .. v[2]) end -opt_fd:close() diff --git a/src/nvim/generators/gen_options_enum.lua b/src/nvim/generators/gen_options_enum.lua new file mode 100644 index 0000000000..9a3953fcbc --- /dev/null +++ b/src/nvim/generators/gen_options_enum.lua @@ -0,0 +1,129 @@ +-- Generates option index enum and map of option name to option index. +-- Handles option full name, short name and aliases. +-- Also generates BV_ and WV_ enum constants. + +local options_enum_file = arg[1] +local options_map_file = arg[2] +local options_enum_fd = assert(io.open(options_enum_file, 'w')) +local options_map_fd = assert(io.open(options_map_file, 'w')) + +--- @param s string +local function enum_w(s) + options_enum_fd:write(s .. '\n') +end + +--- @param s string +local function map_w(s) + options_map_fd:write(s .. '\n') +end + +enum_w('// IWYU pragma: private, include "nvim/option_defs.h"') +enum_w('') + +--- @param s string +--- @return string +local lowercase_to_titlecase = function(s) + return s:sub(1, 1):upper() .. s:sub(2) +end + +--- @type vim.option_meta[] +local options = require('options').options + +-- Generate BV_ enum constants. +enum_w('/// "indir" values for buffer-local options.') +enum_w('/// These need to be defined globally, so that the BV_COUNT can be used with') +enum_w('/// b_p_script_stx[].') +enum_w('enum {') + +local bv_val = 0 + +for _, o in ipairs(options) do + assert(#o.scope == 1 or #o.scope == 2) + assert(#o.scope == 1 or o.scope[1] == 'global') + local min_scope = o.scope[#o.scope] + if min_scope == 'buffer' then + local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name)) + local bv_name = 'BV_' .. varname:sub(3):upper() + enum_w((' %s = %u,'):format(bv_name, bv_val)) + bv_val = bv_val + 1 + end +end + +enum_w((' BV_COUNT = %u, ///< must be the last one'):format(bv_val)) +enum_w('};') +enum_w('') + +-- Generate WV_ enum constants. +enum_w('/// "indir" values for window-local options.') +enum_w('/// These need to be defined globally, so that the WV_COUNT can be used in the') +enum_w('/// window structure.') +enum_w('enum {') + +local wv_val = 0 + +for _, o in ipairs(options) do + assert(#o.scope == 1 or #o.scope == 2) + assert(#o.scope == 1 or o.scope[1] == 'global') + local min_scope = o.scope[#o.scope] + if min_scope == 'window' then + local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name)) + local wv_name = 'WV_' .. varname:sub(3):upper() + enum_w((' %s = %u,'):format(wv_name, wv_val)) + wv_val = wv_val + 1 + end +end + +enum_w((' WV_COUNT = %u, ///< must be the last one'):format(wv_val)) +enum_w('};') +enum_w('') + +--- @type { [string]: string } +local option_index = {} + +-- Generate option index enum and populate the `option_index` dictionary. +enum_w('typedef enum {') +enum_w(' kOptInvalid = -1,') + +for i, o in ipairs(options) do + local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name) + enum_w((' %s = %u,'):format(enum_val_name, i - 1)) + + option_index[o.full_name] = enum_val_name + + if o.abbreviation then + option_index[o.abbreviation] = enum_val_name + end + + if o.alias then + o.alias = type(o.alias) == 'string' and { o.alias } or o.alias + + for _, v in ipairs(o.alias) do + option_index[v] = enum_val_name + end + end +end + +enum_w(' // Option count, used when iterating through options') +enum_w('#define kOptIndexCount ' .. tostring(#options)) +enum_w('} OptIndex;') +enum_w('') + +options_enum_fd:close() + +--- Generate option index map. +local hashy = require('generators.hashy') +local neworder, hashfun = hashy.hashy_hash('find_option', vim.tbl_keys(option_index), function(idx) + return ('option_hash_elems[%s].name'):format(idx) +end) + +map_w('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {') + +for _, name in ipairs(neworder) do + assert(option_index[name] ~= nil) + map_w((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name])) +end + +map_w('};\n') +map_w('static ' .. hashfun) + +options_map_fd:close() diff --git a/src/nvim/generators/gen_unicode_tables.lua b/src/nvim/generators/gen_unicode_tables.lua index 9ad99c8029..6cedb5db50 100644 --- a/src/nvim/generators/gen_unicode_tables.lua +++ b/src/nvim/generators/gen_unicode_tables.lua @@ -60,12 +60,10 @@ local fp_lines_to_lists = function(fp, n, has_comments) if not line then break end - if (not has_comments - or (line:sub(1, 1) ~= '#' and not line:match('^%s*$'))) then + if not has_comments or (line:sub(1, 1) ~= '#' and not line:match('^%s*$')) then local l = split_on_semicolons(line) if #l ~= n then - io.stderr:write(('Found %s items in line %u, expected %u\n'):format( - #l, i, n)) + io.stderr:write(('Found %s items in line %u, expected %u\n'):format(#l, i, n)) io.stderr:write('Line: ' .. line .. '\n') return nil end @@ -93,15 +91,13 @@ end local make_range = function(start, end_, step, add) if step and add then - return (' {0x%x, 0x%x, %d, %d},\n'):format( - start, end_, step == 0 and -1 or step, add) + return (' {0x%x, 0x%x, %d, %d},\n'):format(start, end_, step == 0 and -1 or step, add) else return (' {0x%04x, 0x%04x},\n'):format(start, end_) end end -local build_convert_table = function(ut_fp, props, cond_func, nl_index, - table_name) +local build_convert_table = function(ut_fp, props, cond_func, nl_index, table_name) ut_fp:write('static const convertStruct ' .. table_name .. '[] = {\n') local start = -1 local end_ = -1 @@ -137,8 +133,7 @@ local build_case_table = function(ut_fp, dataprops, table_name, index) local cond_func = function(p) return p[index] ~= '' end - return build_convert_table(ut_fp, dataprops, cond_func, index, - 'to' .. table_name) + return build_convert_table(ut_fp, dataprops, cond_func, index, 'to' .. table_name) end local build_fold_table = function(ut_fp, foldprops) @@ -154,7 +149,7 @@ local build_combining_table = function(ut_fp, dataprops) local end_ = -1 for _, p in ipairs(dataprops) do -- The 'Mc' property was removed, it does take up space. - if (({Mn=true, Me=true})[p[3]]) then + if ({ Mn = true, Me = true })[p[3]] then local n = tonumber(p[1], 16) if start >= 0 and end_ + 1 == n then -- Continue with the same range. @@ -175,8 +170,7 @@ local build_combining_table = function(ut_fp, dataprops) ut_fp:write('};\n') end -local build_width_table = function(ut_fp, dataprops, widthprops, widths, - table_name) +local build_width_table = function(ut_fp, dataprops, widthprops, widths, table_name) ut_fp:write('static const struct interval ' .. table_name .. '[] = {\n') local start = -1 local end_ = -1 @@ -208,13 +202,13 @@ local build_width_table = function(ut_fp, dataprops, widthprops, widths, -- Only use the char when it’s not a composing char. -- But use all chars from a range. local dp = dataprops[dataidx] - if (n_last > n) or (not (({Mn=true, Mc=true, Me=true})[dp[3]])) then + if (n_last > n) or not ({ Mn = true, Mc = true, Me = true })[dp[3]] then if start >= 0 and end_ + 1 == n then -- luacheck: ignore 542 -- Continue with the same range. else if start >= 0 then ut_fp:write(make_range(start, end_)) - table.insert(ret, {start, end_}) + table.insert(ret, { start, end_ }) end start = n end @@ -224,7 +218,7 @@ local build_width_table = function(ut_fp, dataprops, widthprops, widths, end if start >= 0 then ut_fp:write(make_range(start, end_)) - table.insert(ret, {start, end_}) + table.insert(ret, { start, end_ }) end ut_fp:write('};\n') return ret @@ -316,10 +310,9 @@ local eaw_fp = io.open(eastasianwidth_fname, 'r') local widthprops = parse_width_props(eaw_fp) eaw_fp:close() -local doublewidth = build_width_table(ut_fp, dataprops, widthprops, - {W=true, F=true}, 'doublewidth') -local ambiwidth = build_width_table(ut_fp, dataprops, widthprops, - {A=true}, 'ambiguous') +local doublewidth = + build_width_table(ut_fp, dataprops, widthprops, { W = true, F = true }, 'doublewidth') +local ambiwidth = build_width_table(ut_fp, dataprops, widthprops, { A = true }, 'ambiguous') local emoji_fp = io.open(emoji_fname, 'r') local emojiprops = parse_emoji_props(emoji_fp) diff --git a/src/nvim/generators/gen_vimvim.lua b/src/nvim/generators/gen_vimvim.lua index 29355d3cda..fcdc5bddc2 100644 --- a/src/nvim/generators/gen_vimvim.lua +++ b/src/nvim/generators/gen_vimvim.lua @@ -41,12 +41,14 @@ end -- Exclude these from the vimCommand keyword list, they are handled specially -- in syntax/vim.vim (vimAugroupKey, vimAutoCmd, vimGlobal, vimSubst). #9327 local function is_special_cased_cmd(cmd) - return (cmd == 'augroup' - or cmd == 'autocmd' - or cmd == 'doautocmd' - or cmd == 'doautoall' - or cmd == 'global' - or cmd == 'substitute') + return ( + cmd == 'augroup' + or cmd == 'autocmd' + or cmd == 'doautocmd' + or cmd == 'doautoall' + or cmd == 'global' + or cmd == 'substitute' + ) end local vimcmd_start = 'syn keyword vimCommand contained ' @@ -81,7 +83,7 @@ local vimopt_start = 'syn keyword vimOption contained ' w('\n\n' .. vimopt_start) for _, opt_desc in ipairs(options.options) do - if not opt_desc.varname or opt_desc.varname:sub(1, 7) ~= 'p_force' then + if not opt_desc.immutable then if lld.line_length > 850 then w('\n' .. vimopt_start) end @@ -89,7 +91,7 @@ for _, opt_desc in ipairs(options.options) do if opt_desc.abbreviation then w(' ' .. opt_desc.abbreviation) end - if opt_desc.type == 'bool' then + if opt_desc.type == 'boolean' then w(' inv' .. opt_desc.full_name) w(' no' .. opt_desc.full_name) if opt_desc.abbreviation then @@ -133,7 +135,7 @@ end w('\n\nsyn case match') local vimfun_start = 'syn keyword vimFuncName contained ' w('\n\n' .. vimfun_start) -local funcs = mpack.decode(io.open(funcs_file, 'rb'):read("*all")) +local funcs = mpack.decode(io.open(funcs_file, 'rb'):read('*all')) for _, name in ipairs(funcs) do if name then if lld.line_length > 850 then diff --git a/src/nvim/generators/hashy.lua b/src/nvim/generators/hashy.lua index b10bafb9f9..711e695742 100644 --- a/src/nvim/generators/hashy.lua +++ b/src/nvim/generators/hashy.lua @@ -3,7 +3,6 @@ local M = {} _G.d = M - local function setdefault(table, key) local val = table[key] if val == nil then @@ -16,28 +15,30 @@ end function M.build_pos_hash(strings) local len_buckets = {} local maxlen = 0 - for _,s in ipairs(strings) do - table.insert(setdefault(len_buckets, #s),s) - if #s > maxlen then maxlen = #s end + for _, s in ipairs(strings) do + table.insert(setdefault(len_buckets, #s), s) + if #s > maxlen then + maxlen = #s + end end local len_pos_buckets = {} local worst_buck_size = 0 - for len = 1,maxlen do + for len = 1, maxlen do local strs = len_buckets[len] if strs then -- the best position so far generates `best_bucket` -- with `minsize` worst case collisions - local bestpos, minsize, best_bucket = nil, #strs*2, nil - for pos = 1,len do + local bestpos, minsize, best_bucket = nil, #strs * 2, nil + for pos = 1, len do local try_bucket = {} - for _,str in ipairs(strs) do + for _, str in ipairs(strs) do local poschar = string.sub(str, pos, pos) table.insert(setdefault(try_bucket, poschar), str) end local maxsize = 1 - for _,pos_strs in pairs(try_bucket) do + for _, pos_strs in pairs(try_bucket) do maxsize = math.max(maxsize, #pos_strs) end if maxsize < minsize then @@ -46,7 +47,7 @@ function M.build_pos_hash(strings) best_bucket = try_bucket end end - len_pos_buckets[len] = {bestpos, best_bucket} + len_pos_buckets[len] = { bestpos, best_bucket } worst_buck_size = math.max(worst_buck_size, minsize) end end @@ -55,73 +56,79 @@ end function M.switcher(put, tab, maxlen, worst_buck_size) local neworder = {} - put " switch (len) {\n" + put ' switch (len) {\n' local bucky = worst_buck_size > 1 - for len = 1,maxlen do + for len = 1, maxlen do local vals = tab[len] if vals then - put(" case "..len..": ") + put(' case ' .. len .. ': ') local pos, posbuck = unpack(vals) local keys = vim.tbl_keys(posbuck) if #keys > 1 then table.sort(keys) - put("switch (str["..(pos-1).."]) {\n") - for _,c in ipairs(keys) do + put('switch (str[' .. (pos - 1) .. ']) {\n') + for _, c in ipairs(keys) do local buck = posbuck[c] local startidx = #neworder vim.list_extend(neworder, buck) local endidx = #neworder - put(" case '"..c.."': ") - put("low = "..startidx.."; ") - if bucky then put("high = "..endidx.."; ") end - put "break;\n" + put(" case '" .. c .. "': ") + put('low = ' .. startidx .. '; ') + if bucky then + put('high = ' .. endidx .. '; ') + end + put 'break;\n' end - put " default: break;\n" - put " }\n " + put ' default: break;\n' + put ' }\n ' else - local startidx = #neworder - table.insert(neworder, posbuck[keys[1]][1]) - local endidx = #neworder - put("low = "..startidx.."; ") - if bucky then put("high = "..endidx.."; ") end + local startidx = #neworder + table.insert(neworder, posbuck[keys[1]][1]) + local endidx = #neworder + put('low = ' .. startidx .. '; ') + if bucky then + put('high = ' .. endidx .. '; ') + end end - put "break;\n" + put 'break;\n' end end - put " default: break;\n" - put " }\n" + put ' default: break;\n' + put ' }\n' return neworder end function M.hashy_hash(name, strings, access) local stats = {} - local put = function(str) table.insert(stats, str) end + local put = function(str) + table.insert(stats, str) + end local len_pos_buckets, maxlen, worst_buck_size = M.build_pos_hash(strings) - put("int "..name.."_hash(const char *str, size_t len)\n{\n") + put('int ' .. name .. '_hash(const char *str, size_t len)\n{\n') if worst_buck_size > 1 then - put(" int low = 0, high = 0;\n") + put(' int low = 0, high = 0;\n') else - put(" int low = -1;\n") + put(' int low = -1;\n') end local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size) if worst_buck_size > 1 then - put ([[ + put([[ for (int i = low; i < high; i++) { - if (!memcmp(str, ]]..access("i")..[[, len)) { + if (!memcmp(str, ]] .. access('i') .. [[, len)) { return i; } } return -1; ]]) else - put ([[ - if (low < 0 || memcmp(str, ]]..access("low")..[[, len)) { + put([[ + if (low < 0 || memcmp(str, ]] .. access('low') .. [[, len)) { return -1; } return low; ]]) end - put "}\n\n" + put '}\n\n' return neworder, table.concat(stats) end diff --git a/src/nvim/generators/nvim_version.lua.in b/src/nvim/generators/nvim_version.lua.in new file mode 100644 index 0000000000..d0dbf77922 --- /dev/null +++ b/src/nvim/generators/nvim_version.lua.in @@ -0,0 +1,9 @@ +return { + {"major", ${NVIM_VERSION_MAJOR}}, + {"minor", ${NVIM_VERSION_MINOR}}, + {"patch", ${NVIM_VERSION_PATCH}}, + {"prerelease", "$NVIM_VERSION_PRERELEASE" ~= ""}, + {"api_level", ${NVIM_API_LEVEL}}, + {"api_compatible", ${NVIM_API_LEVEL_COMPAT}}, + {"api_prerelease", ${NVIM_API_PRERELEASE}}, +} diff --git a/src/nvim/generators/preload.lua b/src/nvim/generators/preload.lua index 4b7fde2c39..e14671074c 100644 --- a/src/nvim/generators/preload.lua +++ b/src/nvim/generators/preload.lua @@ -1,10 +1,13 @@ local srcdir = table.remove(arg, 1) local nlualib = table.remove(arg, 1) -package.path = srcdir .. '/src/nvim/?.lua;' ..srcdir .. '/runtime/lua/?.lua;' .. package.path -_G.vim = require'vim.shared' +local gendir = table.remove(arg, 1) +package.path = srcdir .. '/src/nvim/?.lua;' .. srcdir .. '/runtime/lua/?.lua;' .. package.path +package.path = gendir .. '/?.lua;' .. package.path +_G.vim = require 'vim.shared' _G.vim.inspect = require 'vim.inspect' package.cpath = package.cpath .. ';' .. nlualib require 'nlua0' +vim.NIL = vim.mpack.NIL -- WOW BOB WOW arg[0] = table.remove(arg, 1) return loadfile(arg[0])() diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 73af78d3e2..64c9c5a8c3 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -22,37 +22,42 @@ #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" +#include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/input.h" #include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/lua/executor.h" -#include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/mapping.h" +#include "nvim/mapping_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option_vars.h" #include "nvim/os/fileio.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" @@ -60,8 +65,9 @@ #include "nvim/vim_defs.h" /// Index in scriptin -static int curscript = 0; -FileDescriptor *scriptin[NSCRIPT] = { NULL }; +static int curscript = -1; +/// Streams to read script from +static FileDescriptor scriptin[NSCRIPT] = { 0 }; // These buffers are used for storing: // - stuffed characters: A command that is translated into another command. @@ -92,7 +98,7 @@ static int typeahead_char = 0; ///< typeahead char that's not flushed /// When block_redo is true the redo buffer will not be changed. /// Used by edit() to repeat insertions. -static int block_redo = false; +static bool block_redo = false; static int KeyNoremap = 0; ///< remapping flags @@ -190,15 +196,12 @@ static char *get_buffcont(buffheader_T *buffer, int dozero) /// K_SPECIAL in the returned string is escaped. char *get_recorded(void) { - char *p; - size_t len; - - p = get_buffcont(&recordbuff, true); + char *p = get_buffcont(&recordbuff, true); free_buff(&recordbuff); // Remove the characters that were added the last time, these must be the // (possibly mapped) characters that stopped the recording. - len = strlen(p); + size_t len = strlen(p); if (len >= last_recorded_len) { len -= last_recorded_len; p[len] = NUL; @@ -274,12 +277,10 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle /// Only works when it was just added. static void delete_buff_tail(buffheader_T *buf, int slen) { - int len; - if (buf->bh_curr == NULL) { return; // nothing to delete } - len = (int)strlen(buf->bh_curr->b_str); + int len = (int)strlen(buf->bh_curr->b_str); if (len < slen) { return; } @@ -333,18 +334,16 @@ static void add_char_buff(buffheader_T *buf, int c) /// if that one is empty. /// If advance == true go to the next char. /// No translation is done K_SPECIAL is escaped. -static int read_readbuffers(int advance) +static int read_readbuffers(bool advance) { - int c; - - c = read_readbuf(&readbuf1, advance); + int c = read_readbuf(&readbuf1, advance); if (c == NUL) { c = read_readbuf(&readbuf2, advance); } return c; } -static int read_readbuf(buffheader_T *buf, int advance) +static int read_readbuf(buffheader_T *buf, bool advance) { if (buf->bh_first.b_next == NULL) { // buffer is empty return NUL; @@ -376,16 +375,16 @@ static void start_stuff(void) } } -/// Return true if the stuff buffer is empty. -int stuff_empty(void) +/// @return true if the stuff buffer is empty. +bool stuff_empty(void) FUNC_ATTR_PURE { return (readbuf1.bh_first.b_next == NULL && readbuf2.bh_first.b_next == NULL); } -/// Return true if readbuf1 is empty. There may still be redo characters in -/// redbuf2. -int readbuf1_empty(void) +/// @return true if readbuf1 is empty. There may still be redo characters in +/// redbuf2. +bool readbuf1_empty(void) FUNC_ATTR_PURE { return (readbuf1.bh_first.b_next == NULL); @@ -1002,7 +1001,7 @@ int ins_char_typebuf(int c, int modifiers) unsigned len = special_to_buf(c, modifiers, true, buf); assert(len < sizeof(buf)); buf[len] = NUL; - (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); + ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); return (int)len; } @@ -1148,10 +1147,10 @@ static void gotchars(const uint8_t *chars, size_t len) maptick++; } -/// Record a <Nop> key. -void gotchars_nop(void) +/// Record an <Ignore> key. +void gotchars_ignore(void) { - uint8_t nop_buf[3] = { K_SPECIAL, KS_EXTRA, KE_NOP }; + uint8_t nop_buf[3] = { K_SPECIAL, KS_EXTRA, KE_IGNORE }; gotchars(nop_buf, 3); } @@ -1178,7 +1177,7 @@ void ungetchars(int len) void may_sync_undo(void) { if ((!(State & (MODE_INSERT | MODE_CMDLINE)) || arrow_used) - && scriptin[curscript] == NULL) { + && curscript < 0) { u_sync(false); } } @@ -1218,8 +1217,9 @@ void free_typebuf(void) /// restored when "file" has been read completely. static typebuf_T saved_typebuf[NSCRIPT]; -void save_typebuf(void) +static void save_typebuf(void) { + assert(curscript >= 0); init_typebuf(); saved_typebuf[curscript] = typebuf; alloc_typebuf(); @@ -1294,18 +1294,13 @@ void openscript(char *name, bool directly) return; } - if (scriptin[curscript] != NULL) { // already reading script - curscript++; - } + curscript++; // use NameBuff for expanded name expand_env(name, NameBuff, MAXPATHL); - int error; - if ((scriptin[curscript] = file_open_new(&error, NameBuff, - kFileReadOnly, 0)) == NULL) { + int error = file_open(&scriptin[curscript], NameBuff, kFileReadOnly, 0); + if (error) { semsg(_(e_notopen_2), name, os_strerror(error)); - if (curscript) { - curscript--; - } + curscript--; return; } save_typebuf(); @@ -1316,7 +1311,6 @@ void openscript(char *name, bool directly) // always, "make test" would fail. if (directly) { oparg_T oa; - int oldcurscript; int save_State = State; int save_restart_edit = restart_edit; int save_finish_op = finish_op; @@ -1328,12 +1322,12 @@ void openscript(char *name, bool directly) clear_oparg(&oa); finish_op = false; - oldcurscript = curscript; + int oldcurscript = curscript; do { update_topline_cursor(); // update cursor position and topline normal_cmd(&oa, false); // execute one command - (void)vpeekc(); // check for end of file - } while (scriptin[oldcurscript] != NULL); + vpeekc(); // check for end of file + } while (curscript >= oldcurscript); State = save_State; msg_scroll = save_msg_scroll; @@ -1345,31 +1339,53 @@ void openscript(char *name, bool directly) /// Close the currently active input script. static void closescript(void) { + assert(curscript >= 0); free_typebuf(); typebuf = saved_typebuf[curscript]; - file_free(scriptin[curscript], false); - scriptin[curscript] = NULL; - if (curscript > 0) { - curscript--; - } + file_close(&scriptin[curscript], false); + curscript--; } #if defined(EXITFREE) void close_all_scripts(void) { - while (scriptin[0] != NULL) { + while (curscript >= 0) { closescript(); } } #endif +bool open_scriptin(char *scriptin_name) + FUNC_ATTR_NONNULL_ALL +{ + assert(curscript == -1); + curscript++; + + int error; + if (strequal(scriptin_name, "-")) { + error = file_open_stdin(&scriptin[0]); + } else { + error = file_open(&scriptin[0], scriptin_name, + kFileReadOnly|kFileNonBlocking, 0); + } + if (error) { + fprintf(stderr, _("Cannot open for reading: \"%s\": %s\n"), + scriptin_name, os_strerror(error)); + curscript--; + return false; + } + save_typebuf(); + + return true; +} + /// Return true when reading keys from a script file. int using_script(void) FUNC_ATTR_PURE { - return scriptin[curscript] != NULL; + return curscript >= 0; } /// This function is called just before doing a blocking wait. Thus after @@ -1452,8 +1468,6 @@ int vgetc(void) mouse_row = old_mouse_row; mouse_col = old_mouse_col; } else { - int c2; - int n; // number of characters recorded from the last vgetc() call static size_t last_vgetc_recorded_len = 0; @@ -1483,7 +1497,7 @@ int vgetc(void) int save_allow_keys = allow_keys; no_mapping++; allow_keys = 0; // make sure BS is not found - c2 = vgetorpeek(true); // no mapping for these chars + int c2 = vgetorpeek(true); // no mapping for these chars c = vgetorpeek(true); no_mapping--; allow_keys = save_allow_keys; @@ -1576,6 +1590,7 @@ int vgetc(void) // For a multi-byte character get all the bytes and return the // converted character. // Note: This will loop until enough bytes are received! + int n; if ((n = MB_BYTE2LEN_CHECK(c)) > 1) { no_mapping++; buf[0] = (uint8_t)c; @@ -1584,8 +1599,8 @@ int vgetc(void) if (buf[i] == K_SPECIAL) { // Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, // which represents a K_SPECIAL (0x80). - (void)vgetorpeek(true); // skip KS_SPECIAL - (void)vgetorpeek(true); // skip KE_FILLER + vgetorpeek(true); // skip KS_SPECIAL + vgetorpeek(true); // skip KE_FILLER } } no_mapping--; @@ -1607,7 +1622,7 @@ int vgetc(void) && !is_mouse_key(c)) { mod_mask = 0; int len = ins_char_typebuf(c, 0); - (void)ins_char_typebuf(ESC, 0); + ins_char_typebuf(ESC, 0); ungetchars(len + 3); // K_SPECIAL KS_MODIFIER MOD_MASK_ALT takes 3 more bytes continue; } @@ -1639,9 +1654,7 @@ int vgetc(void) /// directly from the user (ignoring typeahead). int safe_vgetc(void) { - int c; - - c = vgetc(); + int c = vgetc(); if (c == NUL) { c = get_keystroke(NULL); } @@ -1679,9 +1692,7 @@ int vpeekc(void) /// buffer, it must be an ESC that is recognized as the start of a key code. int vpeekc_any(void) { - int c; - - c = vpeekc(); + int c = vpeekc(); if (c == NUL && typebuf.tb_len > 0) { c = ESC; } @@ -1692,10 +1703,8 @@ int vpeekc_any(void) /// @return true if a character is available, false otherwise. bool char_avail(void) { - int retval; - no_mapping++; - retval = vpeekc(); + int retval = vpeekc(); no_mapping--; return retval != NUL; } @@ -1721,7 +1730,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) if (!char_avail()) { // Flush screen updates before blocking. ui_flush(); - (void)os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events); + os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events); if (!multiqueue_empty(main_loop.events)) { state_handle_k_event(); continue; @@ -1794,7 +1803,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) if (win == NULL) { return; } - (void)mouse_comp_pos(win, &row, &col, &lnum); + mouse_comp_pos(win, &row, &col, &lnum); for (wp = firstwin; wp != win; wp = wp->w_next) { winnr++; } @@ -1950,7 +1959,6 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) mapblock_T *mp_match; int mp_match_len = 0; int max_mlen = 0; - int tb_c1; int keylen = *keylenp; int local_State = get_real_state(); bool is_plug_map = false; @@ -1974,7 +1982,7 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) // - waiting for "hit return to continue" and CR or SPACE typed // - waiting for a char with --more-- // - in Ctrl-X mode, and we get a valid char for that mode - tb_c1 = typebuf.tb_buf[typebuf.tb_off]; + int tb_c1 = typebuf.tb_buf[typebuf.tb_off]; if (no_mapping == 0 && (no_zero_mapping == 0 || tb_c1 != '0') && (typebuf.tb_maplen == 0 || is_plug_map @@ -2190,7 +2198,7 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) // mode temporarily. Append K_SELECT to switch back to Select mode. if (VIsual_active && VIsual_select && (mp->m_mode & MODE_VISUAL)) { VIsual_select = false; - (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false); + ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false); } // Copy the values from *mp that are used, because evaluating the @@ -2513,20 +2521,22 @@ static int vgetorpeek(bool advance) // we are expecting to truncate the trailing // white-space, so find the last non-white // character -- webb - if (did_ai - && *skipwhite(get_cursor_line_ptr() + curwin->w_cursor.col) == NUL) { + if (did_ai && *skipwhite(get_cursor_line_ptr() + curwin->w_cursor.col) == NUL) { curwin->w_wcol = 0; ptr = get_cursor_line_ptr(); - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0, ptr, ptr); - while (cts.cts_ptr < ptr + curwin->w_cursor.col) { - if (!ascii_iswhite(*cts.cts_ptr)) { - curwin->w_wcol = cts.cts_vcol; + char *endptr = ptr + curwin->w_cursor.col; + + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, ptr); + StrCharInfo ci = utf_ptr2StrCharInfo(ptr); + int vcol = 0; + while (ci.ptr < endptr) { + if (!ascii_iswhite(ci.chr.value)) { + curwin->w_wcol = vcol; } - cts.cts_vcol += lbr_chartabsize(&cts); - cts.cts_ptr += utfc_ptr2len(cts.cts_ptr); + vcol += win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + ci = utfc_next(ci); } - clear_chartabsize_arg(&cts); curwin->w_wrow = curwin->w_cline_row + curwin->w_wcol / curwin->w_width_inner; @@ -2654,7 +2664,7 @@ static int vgetorpeek(bool advance) showcmd_idx = typebuf.tb_len - SHOWCMD_COLS; } while (showcmd_idx < typebuf.tb_len) { - (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); + add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); } curwin->w_wcol = old_wcol; curwin->w_wrow = old_wrow; @@ -2755,9 +2765,9 @@ static int vgetorpeek(bool advance) } if (timedout && c == ESC) { - // When recording there will be no timeout. Add a <Nop> after the ESC - // to avoid that it forms a key code with following characters. - gotchars_nop(); + // When recording there will be no timeout. Add an <Ignore> after the + // ESC to avoid that it forms a key code with following characters. + gotchars_ignore(); } vgetc_busy--; @@ -2810,10 +2820,10 @@ int inchar(uint8_t *buf, int maxlen, long wait_time) // Get a character from a script file if there is one. // If interrupted: Stop reading script files, close them all. ptrdiff_t read_size = -1; - while (scriptin[curscript] != NULL && read_size <= 0 && !ignore_script) { + while (curscript >= 0 && read_size <= 0 && !ignore_script) { char script_char; if (got_int - || (read_size = file_read(scriptin[curscript], &script_char, 1)) != 1) { + || (read_size = file_read(&scriptin[curscript], &script_char, 1)) != 1) { // Reached EOF or some error occurred. // Careful: closescript() frees typebuf.tb_buf[] and buf[] may // point inside typebuf.tb_buf[]. Don't use buf[] after this! @@ -2920,7 +2930,6 @@ char *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat) { garray_T line_ga; int c1 = -1; - int c2; int cmod = 0; bool aborted = false; @@ -2947,7 +2956,7 @@ char *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat) // Get two extra bytes for special keys if (c1 == K_SPECIAL) { c1 = vgetorpeek(true); - c2 = vgetorpeek(true); + int c2 = vgetorpeek(true); if (c1 == KS_MODIFIER) { cmod = c2; continue; @@ -3037,7 +3046,7 @@ bool map_execute_lua(bool may_repeat) Error err = ERROR_INIT; Array args = ARRAY_DICT_INIT; - nlua_call_ref(ref, NULL, args, false, &err); + nlua_call_ref(ref, NULL, args, kRetNilBool, NULL, &err); if (err.type != kErrorTypeNone) { semsg_multiline("E5108: %s", err.msg); api_clear_error(&err); diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h index 177a021706..4e962c9b03 100644 --- a/src/nvim/getchar.h +++ b/src/nvim/getchar.h @@ -1,11 +1,11 @@ #pragma once -#include <stdbool.h> -#include <stdint.h> +#include <stddef.h> // IWYU pragma: keep +#include <stdint.h> // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep -#include "nvim/getchar_defs.h" // IWYU pragma: export -#include "nvim/os/fileio.h" +#include "nvim/getchar_defs.h" // IWYU pragma: keep +#include "nvim/os/fileio_defs.h" #include "nvim/types_defs.h" // IWYU pragma: keep /// Argument for flush_buffers(). @@ -15,11 +15,7 @@ typedef enum { FLUSH_INPUT, ///< flush typebuf and inchar() input } flush_buffers_T; -/// Maximum number of streams to read script from -enum { NSCRIPT = 15, }; - -/// Streams to read script from -extern FileDescriptor *scriptin[NSCRIPT]; +enum { NSCRIPT = 15, }; ///< Maximum number of streams to read script from #ifdef INCLUDE_GENERATED_DECLARATIONS # include "getchar.h.generated.h" diff --git a/src/nvim/getchar_defs.h b/src/nvim/getchar_defs.h index 5e6db7d9f3..abf812fad3 100644 --- a/src/nvim/getchar_defs.h +++ b/src/nvim/getchar_defs.h @@ -6,22 +6,19 @@ #include "nvim/api/private/defs.h" -typedef struct buffblock buffblock_T; -typedef struct buffheader buffheader_T; - /// structure used to store one block of the stuff/redo/recording buffers -struct buffblock { - buffblock_T *b_next; ///< pointer to next buffblock +typedef struct buffblock { + struct buffblock *b_next; ///< pointer to next buffblock char b_str[1]; ///< contents (actually longer) -}; +} buffblock_T; /// header used for the stuff buffer and the redo buffer -struct buffheader { +typedef struct { buffblock_T bh_first; ///< first (dummy) block of list buffblock_T *bh_curr; ///< buffblock for appending size_t bh_index; ///< index for reading size_t bh_space; ///< space in bh_curr for appending -}; +} buffheader_T; typedef struct { buffheader_T sr_redobuff; diff --git a/src/nvim/gettext.h b/src/nvim/gettext_defs.h index 2c7b5626d2..2c7b5626d2 100644 --- a/src/nvim/gettext.h +++ b/src/nvim/gettext_defs.h diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 270ffe4fa0..22f7daa823 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -11,10 +11,9 @@ #include "nvim/getchar_defs.h" #include "nvim/iconv_defs.h" #include "nvim/macros_defs.h" -#include "nvim/mbyte.h" #include "nvim/menu_defs.h" #include "nvim/os/os_defs.h" -#include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/state_defs.h" #include "nvim/syntax_defs.h" #include "nvim/types_defs.h" @@ -103,34 +102,6 @@ EXTERN struct nvim_stats_s { EXTERN int Rows INIT( = DFLT_ROWS); // nr of rows in the screen EXTERN int Columns INIT( = DFLT_COLS); // nr of columns in the screen -// We use 64-bit file functions here, if available. E.g. ftello() returns -// off_t instead of long, which helps if long is 32 bit and off_t is 64 bit. -// We assume that when fseeko() is available then ftello() is too. -// Note that Windows has different function names. -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__MINGW32__) -typedef __int64 off_T; -# ifdef __MINGW32__ -# define vim_lseek lseek64 -# define vim_fseek fseeko64 -# define vim_ftell ftello64 -# else -# define vim_lseek _lseeki64 -# define vim_fseek _fseeki64 -# define vim_ftell _ftelli64 -# endif -#else -typedef off_t off_T; -# ifdef HAVE_FSEEKO -# define vim_lseek lseek -# define vim_ftell ftello -# define vim_fseek fseeko -# else -# define vim_lseek lseek -# define vim_ftell ftell -# define vim_fseek(a, b, c) fseek(a, (long)b, c) -# endif -#endif - // When vgetc() is called, it sets mod_mask to the set of modifiers that are // held down based on the MOD_MASK_* symbols that are read first. EXTERN int mod_mask INIT( = 0); // current key modifiers @@ -152,7 +123,7 @@ EXTERN bool redraw_cmdline INIT( = false); // cmdline must be redrawn EXTERN bool redraw_mode INIT( = false); // mode must be redrawn EXTERN bool clear_cmdline INIT( = false); // cmdline must be cleared EXTERN bool mode_displayed INIT( = false); // mode is being displayed -EXTERN int cmdline_star INIT( = false); // cmdline is encrypted +EXTERN int cmdline_star INIT( = 0); // cmdline is encrypted EXTERN bool redrawing_cmdline INIT( = false); // cmdline is being redrawn EXTERN bool cmdline_was_last_drawn INIT( = false); // cmdline was last drawn @@ -370,24 +341,7 @@ EXTERN bool did_check_timestamps INIT( = false); // did check timestamps // recently EXTERN int no_check_timestamps INIT( = 0); // Don't check timestamps -EXTERN bool autocmd_busy INIT( = false); // Is apply_autocmds() busy? -EXTERN int autocmd_no_enter INIT( = false); // *Enter autocmds disabled -EXTERN int autocmd_no_leave INIT( = false); // *Leave autocmds disabled EXTERN int modified_was_set; // did ":set modified" -EXTERN bool did_filetype INIT( = false); // FileType event found -// value for did_filetype when starting to execute autocommands -EXTERN bool keep_filetype INIT( = false); - -// When deleting the current buffer, another one must be loaded. -// If we know which one is preferred, au_new_curbuf is set to it. -EXTERN bufref_T au_new_curbuf INIT( = { NULL, 0, 0 }); - -// When deleting a buffer/window and autocmd_busy is true, do not free the -// buffer/window. but link it in the list starting with -// au_pending_free_buf/ap_pending_free_win, using b_next/w_next. -// Free the buffer/window when autocmd_busy is being set to false. -EXTERN buf_T *au_pending_free_buf INIT( = NULL); -EXTERN win_T *au_pending_free_win INIT( = NULL); // Mouse coordinates, set by handle_mouse_event() EXTERN int mouse_grid; @@ -412,7 +366,7 @@ EXTERN win_T *lastwin; // last window EXTERN win_T *prevwin INIT( = NULL); // previous window #define ONE_WINDOW (firstwin == lastwin) #define FOR_ALL_FRAMES(frp, first_frame) \ - for ((frp) = first_frame; (frp) != NULL; (frp) = (frp)->fr_next) // NOLINT + for ((frp) = first_frame; (frp) != NULL; (frp) = (frp)->fr_next) // When using this macro "break" only breaks out of the inner loop. Use "goto" // to break out of the tabpage loop. @@ -426,19 +380,6 @@ EXTERN win_T *prevwin INIT( = NULL); // previous window EXTERN win_T *curwin; // currently active window -typedef struct { - win_T *auc_win; ///< Window used in aucmd_prepbuf(). When not NULL the - ///< window has been allocated. - bool auc_win_used; ///< This auc_win is being used. -} aucmdwin_T; - -/// When executing autocommands for a buffer that is not in any window, a -/// special window is created to handle the side effects. When autocommands -/// nest we may need more than one. -EXTERN kvec_t(aucmdwin_T) aucmd_win_vec INIT( = KV_INITIAL_VALUE); -#define aucmd_win (aucmd_win_vec.items) -#define AUCMD_WIN_COUNT ((int)aucmd_win_vec.size) - // The window layout is kept in a tree of frames. topframe points to the top // of the tree. EXTERN frame_T *topframe; // top of the window frame tree @@ -467,7 +408,7 @@ EXTERN buf_T *curbuf INIT( = NULL); // currently active buffer for (buf_T *buf = lastbuf; buf != NULL; buf = buf->b_prev) #define FOR_ALL_BUF_WININFO(buf, wip) \ - for ((wip) = (buf)->b_wininfo; (wip) != NULL; (wip) = (wip)->wi_next) // NOLINT + for ((wip) = (buf)->b_wininfo; (wip) != NULL; (wip) = (wip)->wi_next) // List of files being edited (global argument list). curwin->w_alist points // to this when the window is using the global argument list. @@ -649,9 +590,9 @@ EXTERN int reg_executing INIT( = 0); // register being executed or zero EXTERN bool pending_end_reg_executing INIT( = false); EXTERN int reg_recorded INIT( = 0); // last recorded register or zero -EXTERN int no_mapping INIT( = false); // currently no mapping allowed +EXTERN int no_mapping INIT( = 0); // currently no mapping allowed EXTERN int no_zero_mapping INIT( = 0); // mapping zero not allowed -EXTERN int allow_keys INIT( = false); // allow key codes when no_mapping is set +EXTERN int allow_keys INIT( = 0); // allow key codes when no_mapping is set EXTERN int no_u_sync INIT( = 0); // Don't call u_sync() EXTERN int u_sync_once INIT( = 0); // Call u_sync() once when evaluating // an expression. @@ -659,10 +600,10 @@ EXTERN int u_sync_once INIT( = 0); // Call u_sync() once when evaluating EXTERN bool force_restart_edit INIT( = false); // force restart_edit after // ex_normal returns EXTERN int restart_edit INIT( = 0); // call edit when next cmd finished -EXTERN int arrow_used; // Normally false, set to true after - // hitting cursor key in insert mode. - // Used by vgetorpeek() to decide when - // to call u_sync() +EXTERN bool arrow_used; // Normally false, set to true after + // hitting cursor key in insert mode. + // Used by vgetorpeek() to decide when + // to call u_sync() EXTERN bool ins_at_eol INIT( = false); // put cursor after eol when // restarting edit after CTRL-O @@ -717,9 +658,9 @@ EXTERN bool typebuf_was_empty INIT( = false); EXTERN int ex_normal_busy INIT( = 0); // recursiveness of ex_normal() EXTERN int expr_map_lock INIT( = 0); // running expr mapping, prevent use of ex_normal() and text changes EXTERN bool ignore_script INIT( = false); // ignore script input -EXTERN int stop_insert_mode; // for ":stopinsert" -EXTERN bool KeyTyped; // true if user typed current char -EXTERN int KeyStuffed; // true if current char from stuffbuf +EXTERN bool stop_insert_mode; // for ":stopinsert" +EXTERN bool KeyTyped; // true if user typed current char +EXTERN int KeyStuffed; // true if current char from stuffbuf EXTERN int maptick INIT( = 0); // tick for each non-mapped char EXTERN int must_redraw INIT( = 0); // type of redraw necessary @@ -730,7 +671,7 @@ EXTERN bool must_redraw_pum INIT( = false); // redraw pum. NB: must_redraw EXTERN bool need_highlight_changed INIT( = true); -EXTERN FILE *scriptout INIT( = NULL); ///< Stream to write script to. +EXTERN FILE *scriptout INIT( = NULL); ///< Write input to this file ("nvim -w"). // Note that even when handling SIGINT, volatile is not necessary because the // callback is not called directly from the signal handlers. @@ -758,11 +699,6 @@ EXTERN char last_mode[MODE_MAX_LENGTH] INIT( = "n"); EXTERN char *last_cmdline INIT( = NULL); // last command line (for ":) EXTERN char *repeat_cmdline INIT( = NULL); // command line for "." EXTERN char *new_last_cmdline INIT( = NULL); // new value for last_cmdline -EXTERN char *autocmd_fname INIT( = NULL); // fname for <afile> on cmdline -EXTERN bool autocmd_fname_full INIT( = false); // autocmd_fname is full path -EXTERN int autocmd_bufnr INIT( = 0); // fnum for <abuf> on cmdline -EXTERN char *autocmd_match INIT( = NULL); // name for <amatch> on cmdline -EXTERN bool did_cursorhold INIT( = false); // set when CursorHold t'gerd EXTERN int postponed_split INIT( = 0); // for CTRL-W CTRL-] command EXTERN int postponed_split_flags INIT( = 0); // args for win_split() @@ -788,7 +724,7 @@ EXTERN char *empty_string_option INIT( = ""); EXTERN bool redir_off INIT( = false); // no redirection for a moment EXTERN FILE *redir_fd INIT( = NULL); // message redirection file EXTERN int redir_reg INIT( = 0); // message redirection register -EXTERN int redir_vname INIT( = 0); // message redirection variable +EXTERN bool redir_vname INIT( = false); // message redirection variable EXTERN garray_T *capture_ga INIT( = NULL); // captured output for execute() EXTERN uint8_t langmap_mapchar[256]; // mapping for language keys @@ -816,6 +752,8 @@ EXTERN bool km_startsel INIT( = false); EXTERN int cmdwin_type INIT( = 0); ///< type of cmdline window or 0 EXTERN int cmdwin_result INIT( = 0); ///< result of cmdline window or 0 EXTERN int cmdwin_level INIT( = 0); ///< cmdline recursion level +EXTERN buf_T *cmdwin_buf INIT( = NULL); ///< buffer of cmdline window or NULL +EXTERN win_T *cmdwin_win INIT( = NULL); ///< window of cmdline window or NULL EXTERN win_T *cmdwin_old_curwin INIT( = NULL); ///< curwin before opening cmdline window or NULL EXTERN char no_lines_msg[] INIT( = N_("--No lines in buffer--")); @@ -868,7 +806,9 @@ EXTERN const char e_argreq[] INIT(= N_("E471: Argument required")); EXTERN const char e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &")); EXTERN const char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; <CR> executes, CTRL-C quits")); EXTERN const char e_curdir[] INIT(= N_("E12: Command not allowed in secure mode in current dir or tag search")); +EXTERN const char e_invalid_buffer_name_str[] INIT(= N_("E158: Invalid buffer name: %s")); EXTERN const char e_command_too_recursive[] INIT(= N_("E169: Command too recursive")); +EXTERN const char e_buffer_is_not_loaded[] INIT(= N_("E681: Buffer is not loaded")); EXTERN const char e_endif[] INIT(= N_("E171: Missing :endif")); EXTERN const char e_endtry[] INIT(= N_("E600: Missing :endtry")); EXTERN const char e_endwhile[] INIT(= N_("E170: Missing :endwhile")); @@ -950,6 +890,7 @@ EXTERN const char e_invalblob[] INIT(= N_("E978: Invalid operation for Blob")); EXTERN const char e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s")); EXTERN const char e_toofewarg[] INIT(= N_("E119: Not enough arguments for function: %s")); EXTERN const char e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: \"%s\"")); +EXTERN const char e_dictkey_len[] INIT(= N_("E716: Key not present in Dictionary: \"%.*s\"")); EXTERN const char e_listreq[] INIT(= N_("E714: List required")); EXTERN const char e_listblobreq[] INIT(= N_("E897: List or Blob required")); EXTERN const char e_listdictarg[] INIT(= N_("E712: Argument of %s must be a List or Dictionary")); @@ -1037,7 +978,7 @@ EXTERN const char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP" EXTERN const char line_msg[] INIT(= N_(" line ")); -EXTERN FILE *time_fd INIT(= NULL); // where to write startup timing +EXTERN FILE *time_fd INIT(= NULL); // Where to write --startuptime report. // Some compilers warn for not using a return value, but in some situations we // can't do anything useful with the value. Assign to this variable to avoid @@ -1051,39 +992,7 @@ EXTERN bool headless_mode INIT(= false); // uncrustify:on -/// Used to track the status of external functions. -/// Currently only used for iconv(). -typedef enum { - kUnknown, - kWorking, - kBroken, -} WorkingStatus; - -/// The scope of a working-directory command like `:cd`. -/// -/// Scopes are enumerated from lowest to highest. When adding a scope make sure -/// to update all functions using scopes as well, such as the implementation of -/// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes -/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead. -typedef enum { - kCdScopeInvalid = -1, - kCdScopeWindow, ///< Affects one window. - kCdScopeTabpage, ///< Affects one tab page. - kCdScopeGlobal, ///< Affects the entire Nvim instance. -} CdScope; - -#define MIN_CD_SCOPE kCdScopeWindow -#define MAX_CD_SCOPE kCdScopeGlobal - -/// What caused the current directory to change. -typedef enum { - kCdCauseOther = -1, - kCdCauseManual, ///< Using `:cd`, `:tcd`, `:lcd` or `chdir()`. - kCdCauseWindow, ///< Switching to another window. - kCdCauseAuto, ///< On 'autochdir'. -} CdCause; - -// Only filled for Win32. +/// Only filled for Win32. EXTERN char windowsVersion[20] INIT( = { 0 }); /// While executing a regexp and set to OPTION_MAGIC_ON or OPTION_MAGIC_OFF this diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 2ef89b778e..e386853022 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -24,11 +24,14 @@ #include "nvim/highlight.h" #include "nvim/log.h" #include "nvim/map_defs.h" +#include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option_vars.h" +#include "nvim/optionstr.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "grid.c.generated.h" @@ -67,7 +70,7 @@ void grid_adjust(ScreenGrid **grid, int *row_off, int *col_off) } } -schar_T schar_from_str(char *str) +schar_T schar_from_str(const char *str) { if (str == NULL) { return 0; @@ -120,6 +123,13 @@ void schar_cache_clear(void) { decor_check_invalid_glyphs(); set_clear(glyph, &glyph_cache); + + // for char options we have stored the original strings. Regenerate + // the parsed schar_T values with the new clean cache. + // This must not return an error as cell widths have not changed. + if (check_chars_options()) { + abort(); + } } bool schar_high(schar_T sc) @@ -137,15 +147,39 @@ bool schar_high(schar_T sc) # define schar_idx(sc) (sc >> 8) #endif -void schar_get(char *buf_out, schar_T sc) +/// sets final NUL +size_t schar_get(char *buf_out, schar_T sc) +{ + size_t len = schar_get_adv(&buf_out, sc); + *buf_out = NUL; + return len; +} + +/// advance buf_out. do NOT set final NUL +size_t schar_get_adv(char **buf_out, schar_T sc) +{ + size_t len; + if (schar_high(sc)) { + uint32_t idx = schar_idx(sc); + assert(idx < glyph_cache.h.n_keys); + len = strlen(&glyph_cache.keys[idx]); + memcpy(*buf_out, &glyph_cache.keys[idx], len); + } else { + len = strnlen((char *)&sc, 4); + memcpy(*buf_out, (char *)&sc, len); + } + *buf_out += len; + return len; +} + +size_t schar_len(schar_T sc) { if (schar_high(sc)) { uint32_t idx = schar_idx(sc); assert(idx < glyph_cache.h.n_keys); - xstrlcpy(buf_out, &glyph_cache.keys[idx], 32); + return strlen(&glyph_cache.keys[idx]); } else { - memcpy(buf_out, (char *)&sc, 4); - buf_out[4] = NUL; + return strnlen((char *)&sc, 4); } } @@ -243,7 +277,7 @@ void line_do_arabic_shape(schar_T *buf, int cols) // Too bigly, discard one code-point. // This should be enough as c0 cannot grow more than from 2 to 4 bytes // (base arabic to extended arabic) - rest -= (size_t)utf_cp_head_off(scbuf + off, scbuf + off + rest - 1) + 1; + rest -= (size_t)utf_cp_bounds(scbuf + off, scbuf + off + rest - 1).begin_off + 1; } memcpy(scbuf_new + len, scbuf + off, rest); buf[i] = schar_from_buf(scbuf_new, len + rest); @@ -263,13 +297,13 @@ void grid_clear_line(ScreenGrid *grid, size_t off, int width, bool valid) grid->chars[off + (size_t)col] = schar_from_ascii(' '); } int fill = valid ? 0 : -1; - (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T)); - (void)memset(grid->vcols + off, -1, (size_t)width * sizeof(colnr_T)); + memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T)); + memset(grid->vcols + off, -1, (size_t)width * sizeof(colnr_T)); } void grid_invalidate(ScreenGrid *grid) { - (void)memset(grid->attrs, -1, sizeof(sattr_T) * (size_t)grid->rows * (size_t)grid->cols); + memset(grid->attrs, -1, sizeof(sattr_T) * (size_t)grid->rows * (size_t)grid->cols); } static bool grid_invalid_row(ScreenGrid *grid, int row) @@ -302,6 +336,9 @@ static int grid_line_coloff = 0; static int grid_line_maxcol = 0; static int grid_line_first = INT_MAX; static int grid_line_last = 0; +static int grid_line_clear_to = 0; +static int grid_line_clear_attr = 0; +static bool grid_line_rl = false; /// Start a group of grid_line_puts calls that builds a single grid line. /// @@ -310,14 +347,18 @@ static int grid_line_last = 0; void grid_line_start(ScreenGrid *grid, int row) { int col = 0; + grid_line_maxcol = grid->cols; grid_adjust(&grid, &row, &col); assert(grid_line_grid == NULL); grid_line_row = row; grid_line_grid = grid; grid_line_coloff = col; grid_line_first = (int)linebuf_size; - grid_line_maxcol = grid->cols - grid_line_coloff; + grid_line_maxcol = MIN(grid_line_maxcol, grid->cols - grid_line_coloff); grid_line_last = 0; + grid_line_clear_to = 0; + grid_line_clear_attr = 0; + grid_line_rl = false; assert((size_t)grid_line_maxcol <= linebuf_size); @@ -433,14 +474,13 @@ int grid_line_puts(int col, const char *text, int textlen, int attr) return col - start_col; } -void grid_line_fill(int start_col, int end_col, int c, int attr) +int grid_line_fill(int start_col, int end_col, schar_T sc, int attr) { end_col = MIN(end_col, grid_line_maxcol); if (start_col >= end_col) { - return; + return end_col; } - schar_T sc = schar_from_char(c); for (int col = start_col; col < end_col; col++) { linebuf_char[col] = sc; linebuf_attr[col] = attr; @@ -449,6 +489,17 @@ void grid_line_fill(int start_col, int end_col, int c, int attr) grid_line_first = MIN(grid_line_first, start_col); grid_line_last = MAX(grid_line_last, end_col); + return end_col; +} + +void grid_line_clear_end(int start_col, int end_col, int attr) +{ + if (grid_line_first > start_col) { + grid_line_first = start_col; + grid_line_last = start_col; + } + grid_line_clear_to = end_col; + grid_line_clear_attr = attr; } /// move the cursor to a position in a currently rendered line. @@ -459,13 +510,15 @@ void grid_line_cursor_goto(int col) void grid_line_mirror(void) { - if (grid_line_first >= grid_line_last) { + grid_line_clear_to = MAX(grid_line_last, grid_line_clear_to); + if (grid_line_first >= grid_line_clear_to) { return; } - linebuf_mirror(&grid_line_first, &grid_line_last, grid_line_maxcol); + linebuf_mirror(&grid_line_first, &grid_line_last, &grid_line_clear_to, grid_line_maxcol); + grid_line_rl = true; } -void linebuf_mirror(int *firstp, int *lastp, int maxcol) +void linebuf_mirror(int *firstp, int *lastp, int *clearp, int maxcol) { int first = *firstp; int last = *lastp; @@ -498,8 +551,9 @@ void linebuf_mirror(int *firstp, int *lastp, int maxcol) linebuf_vcol[mirror - col] = scratch_vcol[col]; } - *lastp = maxcol - first; - *firstp = maxcol - last; + *firstp = maxcol - *clearp; + *clearp = maxcol - first; + *lastp = maxcol - last; } /// End a group of grid_line_puts calls and send the screen buffer to the UI layer. @@ -507,13 +561,14 @@ void grid_line_flush(void) { ScreenGrid *grid = grid_line_grid; grid_line_grid = NULL; - assert(grid_line_last <= grid_line_maxcol); - if (grid_line_first >= grid_line_last) { + grid_line_clear_to = MAX(grid_line_last, grid_line_clear_to); + assert(grid_line_clear_to <= grid_line_maxcol); + if (grid_line_first >= grid_line_clear_to) { return; } grid_put_linebuf(grid, grid_line_row, grid_line_coloff, grid_line_first, grid_line_last, - grid_line_last, false, 0, false); + grid_line_clear_to, grid_line_rl, grid_line_clear_attr, false); } /// flush grid line but only if on a valid row @@ -532,89 +587,17 @@ void grid_line_flush_if_valid_row(void) grid_line_flush(); } -/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col" -/// to "end_col" (exclusive) with character "c1" in first column followed by -/// "c2" in the other columns. Use attributes "attr". -void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1, - int c2, int attr) +void grid_clear(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int attr) { - int row_off = 0, col_off = 0; - grid_adjust(&grid, &row_off, &col_off); - start_row += row_off; - end_row += row_off; - start_col += col_off; - end_col += col_off; - - // safety check - if (end_row > grid->rows) { - end_row = grid->rows; - } - if (end_col > grid->cols) { - end_col = grid->cols; - } - - // nothing to do - if (start_row >= end_row || start_col >= end_col) { - return; - } - for (int row = start_row; row < end_row; row++) { - int dirty_first = INT_MAX; - int dirty_last = 0; - size_t lineoff = grid->line_offset[row]; - - // When drawing over the right half of a double-wide char clear - // out the left half. When drawing over the left half of a - // double wide-char clear out the right half. Only needed in a - // terminal. - if (start_col > 0 && grid->chars[lineoff + (size_t)start_col] == NUL) { - size_t off = lineoff + (size_t)start_col - 1; - grid->chars[off] = schar_from_ascii(' '); - grid->attrs[off] = attr; - dirty_first = start_col - 1; - } - if (end_col < grid->cols && grid->chars[lineoff + (size_t)end_col] == NUL) { - size_t off = lineoff + (size_t)end_col; - grid->chars[off] = schar_from_ascii(' '); - grid->attrs[off] = attr; - dirty_last = end_col + 1; - } - - int col = start_col; - schar_T sc = schar_from_char(c1); - for (col = start_col; col < end_col; col++) { - size_t off = lineoff + (size_t)col; - if (grid->chars[off] != sc || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) { - grid->chars[off] = sc; - grid->attrs[off] = attr; - if (dirty_first == INT_MAX) { - dirty_first = col; - } - dirty_last = col + 1; - } - grid->vcols[off] = -1; - if (col == start_col) { - sc = schar_from_char(c2); - } - } - if (dirty_last > dirty_first) { - if (grid->throttled) { - // Note: assumes msg_grid is the only throttled grid - assert(grid == &msg_grid); - int dirty = 0; - if (attr != HL_ATTR(HLF_MSG) || c2 != ' ') { - dirty = dirty_last; - } else if (c1 != ' ') { - dirty = dirty_first + 1; - } - if (grid->dirty_col && dirty > grid->dirty_col[row]) { - grid->dirty_col[row] = dirty; - } - } else { - int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' '); - ui_line(grid, row, dirty_first, last, dirty_last, attr, false); - } + grid_line_start(grid, row); + end_col = MIN(end_col, grid_line_maxcol); + if (grid_line_row >= grid_line_grid->rows || start_col >= end_col) { + grid_line_grid = NULL; // TODO(bfredl): make callers behave instead + return; } + grid_line_clear_end(start_col, end_col, attr); + grid_line_flush(); } } @@ -650,8 +633,6 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol { bool redraw_next; // redraw_this for next character bool clear_next = false; - int char_cells; // 1: normal char - // 2: occupies two display cells assert(0 <= row && row < grid->rows); // TODO(bfredl): check all callsites and eliminate // Check for illegal col, just in case @@ -673,22 +654,16 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol // two-cell character in the same grid, truncate that into a '>'. if (col > 0 && grid->chars[off_to + (size_t)col] == 0) { linebuf_char[col - 1] = schar_from_ascii('>'); + linebuf_attr[col - 1] = grid->attrs[off_to + (size_t)col - 1]; col--; } + int clear_start = endcol; if (rl) { - // Clear rest first, because it's left of the text. - if (clear_width > 0) { - while (col <= endcol && grid->chars[off_to + (size_t)col] == schar_from_ascii(' ') - && grid->attrs[off_to + (size_t)col] == bg_attr) { - col++; - } - if (col <= endcol) { - grid_fill(grid, row, row + 1, col + coloff, endcol + coloff + 1, ' ', ' ', bg_attr); - } - } - col = endcol + 1; + clear_start = col; + col = endcol; endcol = clear_width; + clear_width = col; } if (p_arshape && !p_tbidi && endcol > col) { @@ -701,17 +676,19 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol } } - redraw_next = grid_char_needs_redraw(grid, col, (size_t)col + off_to, endcol - col); + redraw_next = grid_char_needs_redraw(grid, col, off_to + (size_t)col, endcol - col); - int start_dirty = -1, end_dirty = 0; + int start_dirty = -1; + int end_dirty = 0; while (col < endcol) { - char_cells = 1; + int char_cells = 1; // 1: normal char + // 2: occupies two display cells if (col + 1 < endcol && linebuf_char[col + 1] == 0) { char_cells = 2; } bool redraw_this = redraw_next; // Does character need redraw? - size_t off = (size_t)col + off_to; + size_t off = off_to + (size_t)col; redraw_next = grid_char_needs_redraw(grid, col + char_cells, off + (size_t)char_cells, endcol - col - char_cells); @@ -755,49 +732,59 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol if (clear_next) { // Clear the second half of a double-wide character of which the left // half was overwritten with a single-wide character. - grid->chars[(size_t)col + off_to] = schar_from_ascii(' '); + grid->chars[off_to + (size_t)col] = schar_from_ascii(' '); end_dirty++; } - int clear_end = -1; - if (clear_width > 0 && !rl) { - // blank out the rest of the line - // TODO(bfredl): we could cache winline widths - while (col < clear_width) { - size_t off = (size_t)col + off_to; - if (grid->chars[off] != schar_from_ascii(' ') - || grid->attrs[off] != bg_attr - || rdb_flags & RDB_NODELTA) { - grid->chars[off] = schar_from_ascii(' '); - grid->attrs[off] = bg_attr; - if (start_dirty == -1) { - start_dirty = col; - end_dirty = col; - } else if (clear_end == -1) { - end_dirty = endcol; - } - clear_end = col + 1; + // When clearing the left half of a double-wide char also clear the right half. + if (off_to + (size_t)clear_width < max_off_to + && grid->chars[off_to + (size_t)clear_width] == 0) { + clear_width++; + } + + int clear_dirty_start = -1, clear_end = -1; + // blank out the rest of the line + // TODO(bfredl): we could cache winline widths + col = clear_start; + while (col < clear_width) { + size_t off = off_to + (size_t)col; + if (grid->chars[off] != schar_from_ascii(' ') + || grid->attrs[off] != bg_attr + || rdb_flags & RDB_NODELTA) { + grid->chars[off] = schar_from_ascii(' '); + grid->attrs[off] = bg_attr; + if (clear_dirty_start == -1) { + clear_dirty_start = col; } - grid->vcols[off] = MAXCOL; - col++; + clear_end = col + 1; } + grid->vcols[off] = MAXCOL; + col++; } - if (clear_end < end_dirty) { + if (rl && start_dirty != -1 && clear_dirty_start != -1) { + if (grid->throttled || clear_dirty_start >= start_dirty - 5) { + // cannot draw now or too small to be worth a separate "clear" event + start_dirty = clear_dirty_start; + } else { + ui_line(grid, row, invalid_row, coloff + clear_dirty_start, coloff + clear_dirty_start, + coloff + clear_end, bg_attr, wrap); + } clear_end = end_dirty; + } else { + if (start_dirty == -1) { // clear only + start_dirty = clear_dirty_start; + end_dirty = clear_dirty_start; + } else if (clear_end < end_dirty) { // put only + clear_end = end_dirty; + } else { + end_dirty = endcol; + } } - if (start_dirty == -1) { - start_dirty = end_dirty; - } + if (clear_end > start_dirty) { if (!grid->throttled) { - int start_pos = coloff + start_dirty; - // When drawing over the right half of a double-wide char clear out the - // left half. Only needed in a terminal. - if (invalid_row && start_pos == 0) { - start_pos = -1; - } - ui_line(grid, row, start_pos, coloff + end_dirty, coloff + clear_end, + ui_line(grid, row, invalid_row, coloff + start_dirty, coloff + end_dirty, coloff + clear_end, bg_attr, wrap); } else if (grid->dirty_col) { // TODO(bfredl): really get rid of the extra pseudo terminal in message.c @@ -879,15 +866,20 @@ void grid_free(ScreenGrid *grid) grid->line_offset = NULL; } +#ifdef EXITFREE /// Doesn't allow reinit, so must only be called by free_all_mem! void grid_free_all_mem(void) { grid_free(&default_grid); + grid_free(&msg_grid); + XFREE_CLEAR(msg_grid.dirty_col); xfree(linebuf_char); xfree(linebuf_attr); xfree(linebuf_vcol); xfree(linebuf_scratch); + set_destroy(glyph, &glyph_cache); } +#endif /// (Re)allocates a window grid if size changed while in ext_multigrid mode. /// Updates size, offsets and handle for the grid regardless. @@ -913,14 +905,14 @@ void win_grid_alloc(win_T *wp) wp->w_lines = xcalloc((size_t)rows + 1, sizeof(wline_T)); } - int was_resized = false; + bool was_resized = false; if (want_allocation && (!has_allocation || grid_allocated->rows != total_rows || grid_allocated->cols != total_cols)) { grid_alloc(grid_allocated, total_rows, total_cols, wp->w_grid_alloc.valid, false); grid_allocated->valid = true; - if (wp->w_floating && wp->w_float_config.border) { + if (wp->w_floating && wp->w_config.border) { wp->w_redr_border = true; } was_resized = true; @@ -1082,3 +1074,15 @@ win_T *get_win_by_grid_handle(handle_T handle) } return NULL; } + +/// Put a unicode character in a screen cell. +schar_T schar_from_char(int c) +{ + schar_T sc = 0; + if (c >= 0x200000) { + // TODO(bfredl): this must NEVER happen, even if the file contained overlong sequences + c = 0xFFFD; + } + utf_char2bytes(c, (char *)&sc); + return sc; +} diff --git a/src/nvim/grid.h b/src/nvim/grid.h index 9d8e395dae..7506f0fc9d 100644 --- a/src/nvim/grid.h +++ b/src/nvim/grid.h @@ -2,13 +2,11 @@ #include <stdbool.h> #include <stddef.h> // IWYU pragma: keep -#include <string.h> -#include "nvim/buffer_defs.h" -#include "nvim/grid_defs.h" // IWYU pragma: export +#include "nvim/grid_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" -#include "nvim/mbyte.h" #include "nvim/pos_defs.h" +#include "nvim/types_defs.h" /// By default, all windows are drawn on a single rectangular grid, represented by /// this ScreenGrid instance. In multigrid mode each window will have its own @@ -42,18 +40,6 @@ EXTERN char *linebuf_scratch INIT( = NULL); # define schar_from_ascii(x) ((schar_T)(x)) #endif -/// Put a unicode character in a screen cell. -static inline schar_T schar_from_char(int c) -{ - schar_T sc = 0; - if (c >= 0x200000) { - // TODO(bfredl): this must NEVER happen, even if the file contained overlong sequences - c = 0xFFFD; - } - utf_char2bytes(c, (char *)&sc); - return sc; -} - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "grid.h.generated.h" #endif diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c index 475666be5e..a8a998a361 100644 --- a/src/nvim/hashtab.c +++ b/src/nvim/hashtab.c @@ -24,8 +24,7 @@ #include <string.h> #include "nvim/ascii_defs.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/hashtab.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -122,7 +121,7 @@ hashitem_T *hash_lookup(const hashtab_T *const ht, const char *const key, const { #ifdef HT_DEBUG hash_count_lookup++; -#endif // ifdef HT_DEBUG +#endif // Quickly handle the most common situations: // - return if there is no item at all @@ -155,7 +154,7 @@ hashitem_T *hash_lookup(const hashtab_T *const ht, const char *const key, const #ifdef HT_DEBUG // count a "miss" for hashtab lookup hash_count_perturb++; -#endif // ifdef HT_DEBUG +#endif idx = 5 * idx + perturb + 1; hi = &ht->ht_array[idx & ht->ht_mask]; @@ -190,7 +189,7 @@ void hash_debug_results(void) (int64_t)hash_count_perturb); fprintf(stderr, "Percentage of perturb loops: %" PRId64 "%%\r\n", (int64_t)(hash_count_perturb * 100 / hash_count_lookup)); -#endif // ifdef HT_DEBUG +#endif } /// Add (empty) item for key `key` to hashtable `ht`. @@ -288,7 +287,7 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems) if (ht->ht_filled >= ht->ht_mask + 1) { emsg("hash_may_resize(): table completely filled"); } -#endif // ifdef HT_DEBUG +#endif size_t minsize; const size_t oldsize = ht->ht_mask + 1; diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h index d14eb6944e..06c73ce6c0 100644 --- a/src/nvim/hashtab.h +++ b/src/nvim/hashtab.h @@ -2,7 +2,7 @@ #include <stddef.h> -#include "nvim/hashtab_defs.h" // IWYU pragma: export +#include "nvim/hashtab_defs.h" // IWYU pragma: keep /// Magic number used for hashitem "hi_key" value indicating a deleted item /// diff --git a/src/nvim/hashtab_defs.h b/src/nvim/hashtab_defs.h index 089838fcae..b0c275fcf0 100644 --- a/src/nvim/hashtab_defs.h +++ b/src/nvim/hashtab_defs.h @@ -21,7 +21,7 @@ typedef size_t hash_T; /// value, so that you can get a pointer to the value subtracting an offset from /// the pointer to the key. /// This reduces the size of this item by 1/3. -typedef struct hashitem_S { +typedef struct { /// Cached hash number for hi_key. hash_T hi_hash; @@ -34,11 +34,13 @@ typedef struct hashitem_S { char *hi_key; } hashitem_T; -/// Initial size for a hashtable. -/// Our items are relatively small and growing is expensive, thus start with 16. -/// Must be a power of 2. -/// This allows for storing 10 items (2/3 of 16) before a resize is needed. -enum { HT_INIT_SIZE = 16, }; +enum { + /// Initial size for a hashtable. + /// Our items are relatively small and growing is expensive, thus start with 16. + /// Must be a power of 2. + /// This allows for storing 10 items (2/3 of 16) before a resize is needed. + HT_INIT_SIZE = 16, +}; /// An array-based hashtable. /// @@ -46,7 +48,7 @@ enum { HT_INIT_SIZE = 16, }; /// Values are of any type. /// /// The hashtable grows to accommodate more entries when needed. -typedef struct hashtable_S { +typedef struct { hash_T ht_mask; ///< mask used for hash value ///< (nr of items in array is "ht_mask" + 1) size_t ht_used; ///< number of items used diff --git a/src/nvim/help.c b/src/nvim/help.c index c23dc7fd9d..779772cedf 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -8,36 +8,40 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/extmark_defs.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/help.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/runtime.h" #include "nvim/strings.h" -#include "nvim/syntax.h" #include "nvim/tag.h" #include "nvim/types_defs.h" #include "nvim/vim_defs.h" @@ -173,9 +177,9 @@ void ex_help(exarg_T *eap) // set b_p_ro flag). // Set the alternate file to the previously edited file. alt_fnum = curbuf->b_fnum; - (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL, - ECMD_HIDE + ECMD_SET_HELP, - NULL); // buffer is still open, don't store info + do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL, + ECMD_HIDE + ECMD_SET_HELP, + NULL); // buffer is still open, don't store info if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) { curwin->w_alt_fnum = alt_fnum; @@ -253,7 +257,7 @@ char *check_help_lang(char *arg) /// @param wrong_case no matching case /// /// @return a heuristic indicating how well the given string matches. -int help_heuristic(char *matched_string, int offset, int wrong_case) +int help_heuristic(char *matched_string, int offset, bool wrong_case) FUNC_ATTR_PURE { int num_letters = 0; @@ -609,7 +613,7 @@ void cleanup_help_tags(int num_file, char **file) void prepare_help_buffer(void) { curbuf->b_help = true; - set_string_option_direct("buftype", -1, "help", OPT_FREE|OPT_LOCAL, 0); + set_string_option_direct(kOptBuftype, "help", OPT_LOCAL, 0); // Always set these options after jumping to a help tag, because the // user may have an autocommand that gets in the way. @@ -618,13 +622,13 @@ void prepare_help_buffer(void) // Only set it when needed, buf_init_chartab() is some work. char *p = "!-~,^*,^|,^\",192-255"; if (strcmp(curbuf->b_p_isk, p) != 0) { - set_string_option_direct("isk", -1, p, OPT_FREE|OPT_LOCAL, 0); + set_string_option_direct(kOptIskeyword, p, OPT_LOCAL, 0); check_buf_options(curbuf); - (void)buf_init_chartab(curbuf, false); + buf_init_chartab(curbuf, false); } // Don't use the global foldmethod. - set_string_option_direct("fdm", -1, "manual", OPT_FREE|OPT_LOCAL, 0); + set_string_option_direct(kOptFoldmethod, "manual", OPT_LOCAL, 0); curbuf->b_p_ts = 8; // 'tabstop' is 8. curwin->w_p_list = false; // No list mode. @@ -643,46 +647,9 @@ void prepare_help_buffer(void) set_buflisted(false); } -/// After reading a help file: May cleanup a help buffer when syntax -/// highlighting is not used. -void fix_help_buffer(void) +/// After reading a help file: if help.txt, populate *local-additions* +void get_local_additions(void) { - // Set filetype to "help". - if (strcmp(curbuf->b_p_ft, "help") != 0) { - curbuf->b_ro_locked++; - set_option_value_give_err("ft", STATIC_CSTR_AS_OPTVAL("help"), OPT_LOCAL); - curbuf->b_ro_locked--; - } - - if (!syntax_present(curwin)) { - bool in_example = false; - for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) { - char *line = ml_get_buf(curbuf, lnum); - const size_t len = strlen(line); - if (in_example && len > 0 && !ascii_iswhite(line[0])) { - // End of example: non-white or '<' in first column. - if (line[0] == '<') { - // blank-out a '<' in the first column - line = ml_get_buf_mut(curbuf, lnum); - line[0] = ' '; - } - in_example = false; - } - if (!in_example && len > 0) { - if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) { - // blank-out a '>' in the last column (start of example) - line = ml_get_buf_mut(curbuf, lnum); - line[len - 1] = ' '; - in_example = true; - } else if (line[len - 1] == '~') { - // blank-out a '~' at the end of line (header marker) - line = ml_get_buf_mut(curbuf, lnum); - line[len - 1] = ' '; - } - } - } - } - // In the "help.txt" and "help.abx" file, add the locally added help // files. This uses the very first line in the help file. char *const fname = path_tail(curbuf->b_fname); @@ -734,6 +701,9 @@ void fix_help_buffer(void) const char *const f1 = fnames[i1]; const char *const t1 = path_tail(f1); const char *const e1 = strrchr(t1, '.'); + if (e1 == NULL) { + continue; + } if (path_fnamecmp(e1, ".txt") != 0 && path_fnamecmp(e1, fname + 4) != 0) { // Not .txt and not .abx, remove it. @@ -748,7 +718,7 @@ void fix_help_buffer(void) } const char *const t2 = path_tail(f2); const char *const e2 = strrchr(t2, '.'); - if (e1 == NULL || e2 == NULL) { + if (e2 == NULL) { continue; } if (e1 - f1 != e2 - f2 @@ -833,7 +803,7 @@ void fix_help_buffer(void) linenr_T appended = lnum - lnum_start; if (appended) { mark_adjust(lnum_start + 1, (linenr_T)MAXLNUM, appended, 0, kExtmarkUndo); - buf_redraw_changed_lines_later(curbuf, lnum_start + 1, lnum_start + 1, appended); + changed_lines_redraw_buf(curbuf, lnum_start + 1, lnum_start + 1, appended); } break; } diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 141761c52e..8729c74ce8 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -11,9 +11,10 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/api/ui.h" +#include "nvim/decoration_defs.h" #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" @@ -22,6 +23,7 @@ #include "nvim/macros_defs.h" #include "nvim/map_defs.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/option.h" #include "nvim/popupmenu.h" @@ -40,6 +42,7 @@ static Set(HlEntry) attr_entries = SET_INIT; static Map(int, int) combine_attr_entries = MAP_INIT; static Map(int, int) blend_attr_entries = MAP_INIT; static Map(int, int) blendthrough_attr_entries = MAP_INIT; +static Set(cstr_t) urls = SET_INIT; #define attr_entry(i) attr_entries.keys[i] @@ -113,26 +116,28 @@ retry: {} // new attr id, send event to remote ui:s int id = (int)k; - Array inspect = hl_inspect(id); + Arena arena = ARENA_EMPTY; + Array inspect = hl_inspect(id, &arena); // Note: internally we don't distinguish between cterm and rgb attributes, // remote_ui_hl_attr_define will however. ui_call_hl_attr_define(id, entry.attr, entry.attr, inspect); - api_free_array(inspect); + arena_mem_free(arena_finish(&arena)); return id; } /// When a UI connects, we need to send it the table of highlights used so far. -void ui_send_all_hls(UI *ui) +void ui_send_all_hls(RemoteUI *ui) { for (size_t i = 1; i < set_size(&attr_entries); i++) { - Array inspect = hl_inspect((int)i); + Arena arena = ARENA_EMPTY; + Array inspect = hl_inspect((int)i, &arena); HlAttrs attr = attr_entry(i).attr; remote_ui_hl_attr_define(ui, (Integer)i, attr, attr, inspect); - api_free_array(inspect); + arena_mem_free(arena_finish(&arena)); } for (size_t hlf = 0; hlf < HLF_COUNT; hlf++) { - remote_ui_hl_group_set(ui, cstr_as_string((char *)hlf_names[hlf]), + remote_ui_hl_group_set(ui, cstr_as_string(hlf_names[hlf]), highlight_attr[hlf]); } } @@ -201,13 +206,13 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault) if (!valid_item && p->hl_def != LUA_NOREF && !recursive) { MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ((Integer)ns_id)); - ADD_C(args, CSTR_TO_OBJ(syn_id2name(hl_id))); + ADD_C(args, CSTR_AS_OBJ(syn_id2name(hl_id))); ADD_C(args, BOOLEAN_OBJ(link)); // TODO(bfredl): preload the "global" attr dict? Error err = ERROR_INIT; recursive++; - Object ret = nlua_call_ref(p->hl_def, "hl_def", args, true, &err); + Object ret = nlua_call_ref(p->hl_def, "hl_def", args, kRetObject, NULL, &err); recursive--; // TODO(bfredl): or "inherit", combine with global value? @@ -216,7 +221,7 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault) HlAttrs attrs = HLATTRS_INIT; if (ret.type == kObjectTypeDictionary) { fallback = false; - Dict(highlight) dict = { 0 }; + Dict(highlight) dict = KEYDICT_INIT; if (api_dict_to_keydict(&dict, KeyDict_highlight_get_field, ret.data.dictionary, &err)) { attrs = dict2hlattrs(&dict, true, &it.link_id, &err); @@ -364,7 +369,7 @@ void update_window_hl(win_T *wp, bool invalid) // wp->w_hl_attr_normal group. HL_ATTR(HLF_NFLOAT) is always named. // determine window specific background set in 'winhighlight' - bool float_win = wp->w_floating && !wp->w_float_config.external; + bool float_win = wp->w_floating && !wp->w_config.external; if (float_win && hl_def[HLF_NFLOAT] != 0) { wp->w_hl_attr_normal = hl_def[HLF_NFLOAT]; } else if (hl_def[HLF_COUNT] > 0) { @@ -377,19 +382,19 @@ void update_window_hl(win_T *wp, bool invalid) wp->w_hl_attr_normal = hl_apply_winblend(wp, wp->w_hl_attr_normal); } - wp->w_float_config.shadow = false; - if (wp->w_floating && wp->w_float_config.border) { + wp->w_config.shadow = false; + if (wp->w_floating && wp->w_config.border) { for (int i = 0; i < 8; i++) { int attr = hl_def[HLF_BORDER]; - if (wp->w_float_config.border_hl_ids[i]) { + if (wp->w_config.border_hl_ids[i]) { attr = hl_get_ui_attr(ns_id, HLF_BORDER, - wp->w_float_config.border_hl_ids[i], false); + wp->w_config.border_hl_ids[i], false); } attr = hl_apply_winblend(wp, attr); if (syn_attr2entry(attr).hl_blend > 0) { - wp->w_float_config.shadow = true; + wp->w_config.shadow = true; } - wp->w_float_config.border_attr[i] = attr; + wp->w_config.border_attr[i] = attr; } } @@ -473,6 +478,7 @@ int hl_get_underline(void) .rgb_bg_color = -1, .rgb_sp_color = -1, .hl_blend = -1, + .url = -1, }, .kind = kHlUI, .id1 = 0, @@ -480,6 +486,43 @@ int hl_get_underline(void) }); } +/// Augment an existing attribute with a URL. +/// +/// @param attr Existing attribute to combine with +/// @param url The URL to associate with the highlight attribute +/// @return Combined attribute +int hl_add_url(int attr, const char *url) +{ + HlAttrs attrs = HLATTRS_INIT; + + MHPutStatus status; + uint32_t k = set_put_idx(cstr_t, &urls, url, &status); + if (status != kMHExisting) { + urls.keys[k] = xstrdup(url); + } + + attrs.url = (int32_t)k; + + int new = get_attr_entry((HlEntry){ + .attr = attrs, + .kind = kHlUI, + .id1 = 0, + .id2 = 0, + }); + + return hl_combine_attr(attr, new); +} + +/// Get a URL by its index. +/// +/// @param index URL index +/// @return URL +const char *hl_get_url(uint32_t index) +{ + assert(urls.keys); + return urls.keys[index]; +} + /// Get attribute code for forwarded :terminal highlights. int hl_get_term_attr(HlAttrs *aep) { @@ -490,12 +533,18 @@ int hl_get_term_attr(HlAttrs *aep) /// Clear all highlight tables. void clear_hl_tables(bool reinit) { + const char *url = NULL; + set_foreach(&urls, url, { + xfree((void *)url); + }); + if (reinit) { set_clear(HlEntry, &attr_entries); highlight_init(); map_clear(int, &combine_attr_entries); map_clear(int, &blend_attr_entries); map_clear(int, &blendthrough_attr_entries); + set_clear(cstr_t, &urls); memset(highlight_attr_last, -1, sizeof(highlight_attr_last)); highlight_attr_set_all(); highlight_changed(); @@ -506,6 +555,7 @@ void clear_hl_tables(bool reinit) map_destroy(int, &blend_attr_entries); map_destroy(int, &blendthrough_attr_entries); map_destroy(ColorKey, &ns_hls); + set_destroy(cstr_t, &urls); } } @@ -597,6 +647,10 @@ int hl_combine_attr(int char_attr, int prim_attr) new_en.hl_blend = prim_aep.hl_blend; } + if ((new_en.url == -1) && (prim_aep.url >= 0)) { + new_en.url = prim_aep.url; + } + id = get_attr_entry((HlEntry){ .attr = new_en, .kind = kHlCombine, .id1 = char_attr, .id2 = prim_attr }); if (id > 0) { @@ -678,8 +732,8 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through) } cattrs.cterm_bg_color = fattrs.cterm_bg_color; - cattrs.cterm_fg_color = cterm_blend(ratio, battrs.cterm_fg_color, - fattrs.cterm_bg_color); + cattrs.cterm_fg_color = (int16_t)cterm_blend(ratio, battrs.cterm_fg_color, + fattrs.cterm_bg_color); cattrs.rgb_ae_attr &= ~(HL_FG_INDEXED | HL_BG_INDEXED); } else { cattrs = fattrs; @@ -713,7 +767,8 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through) static int rgb_blend(int ratio, int rgb1, int rgb2) { - int a = ratio, b = 100 - ratio; + int a = ratio; + int b = 100 - ratio; int r1 = (rgb1 & 0xFF0000) >> 16; int g1 = (rgb1 & 0x00FF00) >> 8; int b1 = (rgb1 & 0x0000FF) >> 0; @@ -726,7 +781,7 @@ static int rgb_blend(int ratio, int rgb1, int rgb2) return (mr << 16) + (mg << 8) + mb; } -static int cterm_blend(int ratio, int c1, int c2) +static int cterm_blend(int ratio, int16_t c1, int16_t c2) { // 1. Convert cterm color numbers to RGB. // 2. Blend the RGB colors. @@ -940,7 +995,11 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e { #define HAS_KEY_X(d, key) HAS_KEY(d, highlight, key) HlAttrs hlattrs = HLATTRS_INIT; - int32_t fg = -1, bg = -1, ctermfg = -1, ctermbg = -1, sp = -1; + int32_t fg = -1; + int32_t bg = -1; + int32_t ctermfg = -1; + int32_t ctermbg = -1; + int32_t sp = -1; int blend = -1; int16_t mask = 0; int16_t cterm_mask = 0; @@ -1027,7 +1086,7 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e // Handle cterm attrs if (dict->cterm.type == kObjectTypeDictionary) { - Dict(highlight_cterm) cterm[1] = { 0 }; + Dict(highlight_cterm) cterm[1] = KEYDICT_INIT; if (!api_dict_to_keydict(cterm, KeyDict_highlight_cterm_get_field, dict->cterm.data.dictionary, err)) { return hlattrs; @@ -1078,12 +1137,12 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e hlattrs.rgb_fg_color = fg; hlattrs.rgb_sp_color = sp; hlattrs.hl_blend = blend; - hlattrs.cterm_bg_color = ctermbg == -1 ? 0 : ctermbg + 1; - hlattrs.cterm_fg_color = ctermfg == -1 ? 0 : ctermfg + 1; + hlattrs.cterm_bg_color = ctermbg == -1 ? 0 : (int16_t)(ctermbg + 1); + hlattrs.cterm_fg_color = ctermfg == -1 ? 0 : (int16_t)(ctermfg + 1); hlattrs.cterm_ae_attr = cterm_mask; } else { - hlattrs.cterm_bg_color = bg == -1 ? 0 : bg + 1; - hlattrs.cterm_fg_color = fg == -1 ? 0 : fg + 1; + hlattrs.cterm_bg_color = bg == -1 ? 0 : (int16_t)(bg + 1); + hlattrs.cterm_fg_color = fg == -1 ? 0 : (int16_t)(fg + 1); hlattrs.cterm_ae_attr = mask; } @@ -1119,17 +1178,30 @@ int object_to_color(Object val, char *key, bool rgb, Error *err) } } -Array hl_inspect(int attr) +Array hl_inspect(int attr, Arena *arena) { - // TODO(bfredl): use arena allocation - Array ret = ARRAY_DICT_INIT; - if (hlstate_active) { - hl_inspect_impl(&ret, attr); + if (!hlstate_active) { + return (Array)ARRAY_DICT_INIT; } + Array ret = arena_array(arena, hl_inspect_size(attr)); + hl_inspect_impl(&ret, attr, arena); return ret; } -static void hl_inspect_impl(Array *arr, int attr) +static size_t hl_inspect_size(int attr) +{ + if (attr <= 0 || attr >= (int)set_size(&attr_entries)) { + return 0; + } + + HlEntry e = attr_entry(attr); + if (e.kind == kHlCombine || e.kind == kHlBlend || e.kind == kHlBlendThrough) { + return hl_inspect_size(e.id1) + hl_inspect_size(e.id2); + } + return 1; +} + +static void hl_inspect_impl(Array *arr, int attr, Arena *arena) { Dictionary item = ARRAY_DICT_INIT; if (attr <= 0 || attr >= (int)set_size(&attr_entries)) { @@ -1139,35 +1211,36 @@ static void hl_inspect_impl(Array *arr, int attr) HlEntry e = attr_entry(attr); switch (e.kind) { case kHlSyntax: - PUT(item, "kind", CSTR_TO_OBJ("syntax")); - PUT(item, "hi_name", - CSTR_TO_OBJ(syn_id2name(e.id1))); + item = arena_dict(arena, 3); + PUT_C(item, "kind", CSTR_AS_OBJ("syntax")); + PUT_C(item, "hi_name", CSTR_AS_OBJ(syn_id2name(e.id1))); break; case kHlUI: - PUT(item, "kind", CSTR_TO_OBJ("ui")); + item = arena_dict(arena, 4); + PUT_C(item, "kind", CSTR_AS_OBJ("ui")); const char *ui_name = (e.id1 == -1) ? "Normal" : hlf_names[e.id1]; - PUT(item, "ui_name", CSTR_TO_OBJ(ui_name)); - PUT(item, "hi_name", - CSTR_TO_OBJ(syn_id2name(e.id2))); + PUT_C(item, "ui_name", CSTR_AS_OBJ(ui_name)); + PUT_C(item, "hi_name", CSTR_AS_OBJ(syn_id2name(e.id2))); break; case kHlTerminal: - PUT(item, "kind", CSTR_TO_OBJ("term")); + item = arena_dict(arena, 2); + PUT_C(item, "kind", CSTR_AS_OBJ("term")); break; case kHlCombine: case kHlBlend: case kHlBlendThrough: // attribute combination is associative, so flatten to an array - hl_inspect_impl(arr, e.id1); - hl_inspect_impl(arr, e.id2); + hl_inspect_impl(arr, e.id1, arena); + hl_inspect_impl(arr, e.id2, arena); return; case kHlUnknown: case kHlInvalid: return; } - PUT(item, "id", INTEGER_OBJ(attr)); - ADD(*arr, DICTIONARY_OBJ(item)); + PUT_C(item, "id", INTEGER_OBJ(attr)); + ADD_C(*arr, DICTIONARY_OBJ(item)); } diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h index e5d3f3d1ca..cb3a84bcaf 100644 --- a/src/nvim/highlight.h +++ b/src/nvim/highlight.h @@ -2,12 +2,103 @@ #include <stdbool.h> -#include "nvim/api/keysets_defs.h" +#include "nvim/api/keysets_defs.h" // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep -#include "nvim/buffer_defs.h" // IWYU pragma: keep -#include "nvim/highlight_defs.h" // IWYU pragma: export +#include "nvim/buffer_defs.h" +#include "nvim/highlight_defs.h" // IWYU pragma: keep +#include "nvim/macros_defs.h" #include "nvim/option_vars.h" -#include "nvim/ui.h" +#include "nvim/types_defs.h" +#include "nvim/ui_defs.h" // IWYU pragma: keep + +EXTERN const char *hlf_names[] INIT( = { + [HLF_8] = "SpecialKey", + [HLF_EOB] = "EndOfBuffer", + [HLF_TERM] = "TermCursor", + [HLF_TERMNC] = "TermCursorNC", + [HLF_AT] = "NonText", + [HLF_D] = "Directory", + [HLF_E] = "ErrorMsg", + [HLF_I] = "IncSearch", + [HLF_L] = "Search", + [HLF_LC] = "CurSearch", + [HLF_M] = "MoreMsg", + [HLF_CM] = "ModeMsg", + [HLF_N] = "LineNr", + [HLF_LNA] = "LineNrAbove", + [HLF_LNB] = "LineNrBelow", + [HLF_CLN] = "CursorLineNr", + [HLF_CLS] = "CursorLineSign", + [HLF_CLF] = "CursorLineFold", + [HLF_R] = "Question", + [HLF_S] = "StatusLine", + [HLF_SNC] = "StatusLineNC", + [HLF_C] = "WinSeparator", + [HLF_T] = "Title", + [HLF_V] = "Visual", + [HLF_VNC] = "VisualNC", + [HLF_VSP] = "VertSplit", + [HLF_W] = "WarningMsg", + [HLF_WM] = "WildMenu", + [HLF_FL] = "Folded", + [HLF_FC] = "FoldColumn", + [HLF_ADD] = "DiffAdd", + [HLF_CHD] = "DiffChange", + [HLF_DED] = "DiffDelete", + [HLF_TXD] = "DiffText", + [HLF_SC] = "SignColumn", + [HLF_CONCEAL] = "Conceal", + [HLF_SPB] = "SpellBad", + [HLF_SPC] = "SpellCap", + [HLF_SPR] = "SpellRare", + [HLF_SPL] = "SpellLocal", + [HLF_PNI] = "Pmenu", + [HLF_PSI] = "PmenuSel", + [HLF_PNK] = "PmenuKind", + [HLF_PSK] = "PmenuKindSel", + [HLF_PNX] = "PmenuExtra", + [HLF_PSX] = "PmenuExtraSel", + [HLF_PSB] = "PmenuSbar", + [HLF_PST] = "PmenuThumb", + [HLF_TP] = "TabLine", + [HLF_TPS] = "TabLineSel", + [HLF_TPF] = "TabLineFill", + [HLF_CUC] = "CursorColumn", + [HLF_CUL] = "CursorLine", + [HLF_MC] = "ColorColumn", + [HLF_QFL] = "QuickFixLine", + [HLF_0] = "Whitespace", + [HLF_INACTIVE] = "NormalNC", + [HLF_MSGSEP] = "MsgSeparator", + [HLF_NFLOAT] = "NormalFloat", + [HLF_MSG] = "MsgArea", + [HLF_BORDER] = "FloatBorder", + [HLF_WBR] = "WinBar", + [HLF_WBRNC] = "WinBarNC", + [HLF_CU] = "Cursor", + [HLF_BTITLE] = "FloatTitle", + [HLF_BFOOTER] = "FloatFooter", +}); + +EXTERN int highlight_attr[HLF_COUNT + 1]; // Highl. attr for each context. +EXTERN int highlight_attr_last[HLF_COUNT]; // copy for detecting changed groups +EXTERN int highlight_user[9]; // User[1-9] attributes +EXTERN int highlight_stlnc[9]; // On top of user +EXTERN int cterm_normal_fg_color INIT( = 0); +EXTERN int cterm_normal_bg_color INIT( = 0); +EXTERN RgbValue normal_fg INIT( = -1); +EXTERN RgbValue normal_bg INIT( = -1); +EXTERN RgbValue normal_sp INIT( = -1); + +EXTERN NS ns_hl_global INIT( = 0); // global highlight namespace +EXTERN NS ns_hl_win INIT( = -1); // highlight namespace for the current window +EXTERN NS ns_hl_fast INIT( = -1); // highlight namespace specified in a fast callback +EXTERN NS ns_hl_active INIT( = 0); // currently active/cached namespace + +EXTERN int *hl_attr_active INIT( = highlight_attr); + +// Enums need a typecast to be used as array index. +#define HL_ATTR(n) hl_attr_active[(int)(n)] #ifdef INCLUDE_GENERATED_DECLARATIONS # include "highlight.h.generated.h" @@ -20,8 +111,6 @@ static inline int win_hl_attr(win_T *wp, int hlf) return ((wp->w_ns_hl_attr && ns_hl_fast < 0) ? wp->w_ns_hl_attr : hl_attr_active)[hlf]; } -#define HLATTRS_DICT_SIZE 16 - #define HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp) \ do { \ bool dark_ = (*p_bg == 'd'); \ @@ -29,6 +118,3 @@ static inline int win_hl_attr(win_T *wp, int hlf) rgb_bg = rgb_bg != -1 ? rgb_bg : (dark_ ? 0x000000 : 0xFFFFFF); \ rgb_sp = rgb_sp != -1 ? rgb_sp : 0xFF0000; \ } while (0); - -// Enums need a typecast to be used as array index. -#define HL_ATTR(n) hl_attr_active[(int)(n)] diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 24070199ee..25ab9dc2d9 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -1,9 +1,9 @@ #pragma once -#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> -#include "nvim/macros_defs.h" -#include "nvim/types_defs.h" +#include "nvim/api/private/defs.h" typedef int32_t RgbValue; @@ -35,11 +35,12 @@ typedef enum { /// Stores a complete highlighting entry, including colors and attributes /// for both TUI and GUI. -typedef struct attr_entry { +typedef struct { int16_t rgb_ae_attr, cterm_ae_attr; ///< HlAttrFlags RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color; - int cterm_fg_color, cterm_bg_color; - int hl_blend; + int16_t cterm_fg_color, cterm_bg_color; + int32_t hl_blend; + int32_t url; } HlAttrs; #define HLATTRS_INIT (HlAttrs) { \ @@ -51,167 +52,82 @@ typedef struct attr_entry { .cterm_fg_color = 0, \ .cterm_bg_color = 0, \ .hl_blend = -1, \ + .url = -1, \ } /// Values for index in highlight_attr[]. -/// When making changes, also update hlf_names below! +/// When making changes, also update hlf_names in highlight.h! typedef enum { - HLF_8 = 0, // Meta & special keys listed with ":map", text that is - // displayed different from what it is - HLF_EOB, // after the last line in the buffer - HLF_TERM, // terminal cursor focused - HLF_TERMNC, // terminal cursor unfocused - HLF_AT, // @ characters at end of screen, characters that don't really exist in the text - HLF_D, // directories in CTRL-D listing - HLF_E, // error messages - HLF_I, // incremental search - HLF_L, // last search string - HLF_LC, // current search match - HLF_M, // "--More--" message - HLF_CM, // Mode (e.g., "-- INSERT --") - HLF_N, // line number for ":number" and ":#" commands - HLF_LNA, // LineNrAbove - HLF_LNB, // LineNrBelow - HLF_CLN, // current line number when 'cursorline' is set - HLF_CLS, // current line sign column - HLF_CLF, // current line fold - HLF_R, // return to continue message and yes/no questions - HLF_S, // status lines - HLF_SNC, // status lines of not-current windows - HLF_C, // window split separators - HLF_VSP, // VertSplit - HLF_T, // Titles for output from ":set all", ":autocmd" etc. - HLF_V, // Visual mode - HLF_VNC, // Visual mode, autoselecting and not clipboard owner - HLF_W, // warning messages - HLF_WM, // Wildmenu highlight - HLF_FL, // Folded line - HLF_FC, // Fold column - HLF_ADD, // Added diff line - HLF_CHD, // Changed diff line - HLF_DED, // Deleted diff line - HLF_TXD, // Text Changed in diff line - HLF_SC, // Sign column - HLF_CONCEAL, // Concealed text - HLF_SPB, // SpellBad - HLF_SPC, // SpellCap - HLF_SPR, // SpellRare - HLF_SPL, // SpellLocal - HLF_PNI, // popup menu normal item - HLF_PSI, // popup menu selected item - HLF_PNK, // popup menu normal item "kind" - HLF_PSK, // popup menu selected item "kind" - HLF_PNX, // popup menu normal item "menu" (extra text) - HLF_PSX, // popup menu selected item "menu" (extra text) - HLF_PSB, // popup menu scrollbar - HLF_PST, // popup menu scrollbar thumb - HLF_TP, // tabpage line - HLF_TPS, // tabpage line selected - HLF_TPF, // tabpage line filler - HLF_CUC, // 'cursorcolumn' - HLF_CUL, // 'cursorline' - HLF_MC, // 'colorcolumn' - HLF_QFL, // selected quickfix line - HLF_0, // Whitespace - HLF_INACTIVE, // NormalNC: Normal text in non-current windows - HLF_MSGSEP, // message separator line - HLF_NFLOAT, // Floating window - HLF_MSG, // Message area - HLF_BORDER, // Floating window border - HLF_WBR, // Window bars - HLF_WBRNC, // Window bars of not-current windows - HLF_CU, // Cursor - HLF_BTITLE, // Float Border Title - HLF_BFOOTER, // Float Border Footer - HLF_COUNT, // MUST be the last one + HLF_8 = 0, ///< Meta & special keys listed with ":map", text that is + ///< displayed different from what it is + HLF_EOB, ///< after the last line in the buffer + HLF_TERM, ///< terminal cursor focused + HLF_TERMNC, ///< terminal cursor unfocused + HLF_AT, ///< @ characters at end of screen, characters that don't really exist in the text + HLF_D, ///< directories in CTRL-D listing + HLF_E, ///< error messages + HLF_I, ///< incremental search + HLF_L, ///< last search string + HLF_LC, ///< current search match + HLF_M, ///< "--More--" message + HLF_CM, ///< Mode (e.g., "-- INSERT --") + HLF_N, ///< line number for ":number" and ":#" commands + HLF_LNA, ///< LineNrAbove + HLF_LNB, ///< LineNrBelow + HLF_CLN, ///< current line number when 'cursorline' is set + HLF_CLS, ///< current line sign column + HLF_CLF, ///< current line fold + HLF_R, ///< return to continue message and yes/no questions + HLF_S, ///< status lines + HLF_SNC, ///< status lines of not-current windows + HLF_C, ///< window split separators + HLF_VSP, ///< VertSplit + HLF_T, ///< Titles for output from ":set all", ":autocmd" etc. + HLF_V, ///< Visual mode + HLF_VNC, ///< Visual mode, autoselecting and not clipboard owner + HLF_W, ///< warning messages + HLF_WM, ///< Wildmenu highlight + HLF_FL, ///< Folded line + HLF_FC, ///< Fold column + HLF_ADD, ///< Added diff line + HLF_CHD, ///< Changed diff line + HLF_DED, ///< Deleted diff line + HLF_TXD, ///< Text Changed in diff line + HLF_SC, ///< Sign column + HLF_CONCEAL, ///< Concealed text + HLF_SPB, ///< SpellBad + HLF_SPC, ///< SpellCap + HLF_SPR, ///< SpellRare + HLF_SPL, ///< SpellLocal + HLF_PNI, ///< popup menu normal item + HLF_PSI, ///< popup menu selected item + HLF_PNK, ///< popup menu normal item "kind" + HLF_PSK, ///< popup menu selected item "kind" + HLF_PNX, ///< popup menu normal item "menu" (extra text) + HLF_PSX, ///< popup menu selected item "menu" (extra text) + HLF_PSB, ///< popup menu scrollbar + HLF_PST, ///< popup menu scrollbar thumb + HLF_TP, ///< tabpage line + HLF_TPS, ///< tabpage line selected + HLF_TPF, ///< tabpage line filler + HLF_CUC, ///< 'cursorcolumn' + HLF_CUL, ///< 'cursorline' + HLF_MC, ///< 'colorcolumn' + HLF_QFL, ///< selected quickfix line + HLF_0, ///< Whitespace + HLF_INACTIVE, ///< NormalNC: Normal text in non-current windows + HLF_MSGSEP, ///< message separator line + HLF_NFLOAT, ///< Floating window + HLF_MSG, ///< Message area + HLF_BORDER, ///< Floating window border + HLF_WBR, ///< Window bars + HLF_WBRNC, ///< Window bars of not-current windows + HLF_CU, ///< Cursor + HLF_BTITLE, ///< Float Border Title + HLF_BFOOTER, ///< Float Border Footer + HLF_COUNT, ///< MUST be the last one } hlf_T; -EXTERN const char *hlf_names[] INIT( = { - [HLF_8] = "SpecialKey", - [HLF_EOB] = "EndOfBuffer", - [HLF_TERM] = "TermCursor", - [HLF_TERMNC] = "TermCursorNC", - [HLF_AT] = "NonText", - [HLF_D] = "Directory", - [HLF_E] = "ErrorMsg", - [HLF_I] = "IncSearch", - [HLF_L] = "Search", - [HLF_LC] = "CurSearch", - [HLF_M] = "MoreMsg", - [HLF_CM] = "ModeMsg", - [HLF_N] = "LineNr", - [HLF_LNA] = "LineNrAbove", - [HLF_LNB] = "LineNrBelow", - [HLF_CLN] = "CursorLineNr", - [HLF_CLS] = "CursorLineSign", - [HLF_CLF] = "CursorLineFold", - [HLF_R] = "Question", - [HLF_S] = "StatusLine", - [HLF_SNC] = "StatusLineNC", - [HLF_C] = "WinSeparator", - [HLF_T] = "Title", - [HLF_V] = "Visual", - [HLF_VNC] = "VisualNC", - [HLF_VSP] = "VertSplit", - [HLF_W] = "WarningMsg", - [HLF_WM] = "WildMenu", - [HLF_FL] = "Folded", - [HLF_FC] = "FoldColumn", - [HLF_ADD] = "DiffAdd", - [HLF_CHD] = "DiffChange", - [HLF_DED] = "DiffDelete", - [HLF_TXD] = "DiffText", - [HLF_SC] = "SignColumn", - [HLF_CONCEAL] = "Conceal", - [HLF_SPB] = "SpellBad", - [HLF_SPC] = "SpellCap", - [HLF_SPR] = "SpellRare", - [HLF_SPL] = "SpellLocal", - [HLF_PNI] = "Pmenu", - [HLF_PSI] = "PmenuSel", - [HLF_PNK] = "PmenuKind", - [HLF_PSK] = "PmenuKindSel", - [HLF_PNX] = "PmenuExtra", - [HLF_PSX] = "PmenuExtraSel", - [HLF_PSB] = "PmenuSbar", - [HLF_PST] = "PmenuThumb", - [HLF_TP] = "TabLine", - [HLF_TPS] = "TabLineSel", - [HLF_TPF] = "TabLineFill", - [HLF_CUC] = "CursorColumn", - [HLF_CUL] = "CursorLine", - [HLF_MC] = "ColorColumn", - [HLF_QFL] = "QuickFixLine", - [HLF_0] = "Whitespace", - [HLF_INACTIVE] = "NormalNC", - [HLF_MSGSEP] = "MsgSeparator", - [HLF_NFLOAT] = "NormalFloat", - [HLF_MSG] = "MsgArea", - [HLF_BORDER] = "FloatBorder", - [HLF_WBR] = "WinBar", - [HLF_WBRNC] = "WinBarNC", - [HLF_CU] = "Cursor", - [HLF_BTITLE] = "FloatTitle", - [HLF_BFOOTER] = "FloatFooter", -}); - -EXTERN int highlight_attr[HLF_COUNT + 1]; // Highl. attr for each context. -EXTERN int highlight_attr_last[HLF_COUNT]; // copy for detecting changed groups -EXTERN int highlight_user[9]; // User[1-9] attributes -EXTERN int highlight_stlnc[9]; // On top of user -EXTERN int cterm_normal_fg_color INIT( = 0); -EXTERN int cterm_normal_bg_color INIT( = 0); -EXTERN RgbValue normal_fg INIT( = -1); -EXTERN RgbValue normal_bg INIT( = -1); -EXTERN RgbValue normal_sp INIT( = -1); - -EXTERN NS ns_hl_global INIT( = 0); // global highlight namespace -EXTERN NS ns_hl_win INIT( = -1); // highlight namespace for the current window -EXTERN NS ns_hl_fast INIT( = -1); // highlight namespace specified in a fast callback -EXTERN NS ns_hl_active INIT( = 0); // currently active/cached namespace - -EXTERN int *hl_attr_active INIT( = highlight_attr); - typedef enum { kHlUnknown, kHlUI, @@ -246,3 +162,5 @@ typedef struct { } ColorItem; #define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, .version = -1, \ .is_default = false, .link_global = false } + +enum { HLATTRS_DICT_SIZE = 16, }; diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 3f1758894e..75c23c5bc4 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -15,6 +15,8 @@ #include "nvim/api/private/validate.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/cursor_shape.h" @@ -24,9 +26,9 @@ #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" @@ -34,14 +36,17 @@ #include "nvim/macros_defs.h" #include "nvim/map_defs.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/os/time.h" #include "nvim/runtime.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/vim_defs.h" /// \addtogroup SG_SET @@ -136,263 +141,337 @@ static const char e_missing_argument_str[] // they still work when the runtime files can't be found. static const char *highlight_init_both[] = { - "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey", - "Cursor guibg=fg guifg=bg", - "lCursor guibg=fg guifg=bg", - "DiffText cterm=bold ctermbg=Red gui=bold guibg=Red", - "ErrorMsg ctermbg=DarkRed ctermfg=White guibg=Red guifg=White", - "IncSearch cterm=reverse gui=reverse", - "ModeMsg cterm=bold gui=bold", - "NonText ctermfg=Blue gui=bold guifg=Blue", - "Normal cterm=NONE gui=NONE", - "PmenuSbar ctermbg=Grey guibg=Grey", - "StatusLine cterm=reverse,bold gui=reverse,bold", - "StatusLineNC cterm=reverse gui=reverse", - "TabLineFill cterm=reverse gui=reverse", - "TabLineSel cterm=bold gui=bold", - "TermCursor cterm=reverse gui=reverse", - "WinBar cterm=bold gui=bold", - "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", - "default link VertSplit Normal", - "default link WinSeparator VertSplit", - "default link WinBarNC WinBar", - "default link EndOfBuffer NonText", - "default link LineNrAbove LineNr", - "default link LineNrBelow LineNr", - "default link QuickFixLine Search", - "default link CursorLineSign SignColumn", + "Cursor guifg=bg guibg=fg", + "CursorLineNr gui=bold cterm=bold", + "RedrawDebugNormal gui=reverse cterm=reverse", + "TabLineSel gui=bold cterm=bold", + "TermCursor gui=reverse cterm=reverse", + "Underlined gui=underline cterm=underline", + "lCursor guifg=bg guibg=fg", + + // UI + "default link CursorIM Cursor", "default link CursorLineFold FoldColumn", - "default link CurSearch Search", - "default link PmenuKind Pmenu", - "default link PmenuKindSel PmenuSel", - "default link PmenuExtra Pmenu", - "default link PmenuExtraSel PmenuSel", - "default link Substitute Search", - "default link Whitespace NonText", - "default link MsgSeparator StatusLine", - "default link NormalFloat Pmenu", - "default link FloatBorder WinSeparator", - "default link FloatTitle Title", - "default link FloatFooter Title", - "default FloatShadow blend=80 guibg=Black", - "default FloatShadowThrough blend=100 guibg=Black", - "RedrawDebugNormal cterm=reverse gui=reverse", - "RedrawDebugClear ctermbg=Yellow guibg=Yellow", - "RedrawDebugComposed ctermbg=Green guibg=Green", - "RedrawDebugRecompose ctermbg=Red guibg=Red", - "Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red", - "Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Blue guibg=Yellow", - "default link String Constant", - "default link Character Constant", - "default link Number Constant", - "default link Boolean Constant", - "default link Float Number", - "default link Function Identifier", - "default link Conditional Statement", - "default link Repeat Statement", - "default link Label Statement", - "default link Operator Statement", - "default link Keyword Statement", - "default link Exception Statement", - "default link Include PreProc", - "default link Define PreProc", - "default link Macro PreProc", - "default link PreCondit PreProc", - "default link StorageClass Type", - "default link Structure Type", - "default link Typedef Type", - "default link Tag Special", - "default link SpecialChar Special", - "default link Delimiter Special", + "default link CursorLineSign SignColumn", + "default link EndOfBuffer NonText", + "default link FloatBorder NormalFloat", + "default link FloatFooter FloatTitle", + "default link FloatTitle Title", + "default link FoldColumn SignColumn", + "default link IncSearch CurSearch", + "default link LineNrAbove LineNr", + "default link LineNrBelow LineNr", + "default link MsgSeparator StatusLine", + "default link MsgArea NONE", + "default link NormalNC NONE", + "default link PmenuExtra Pmenu", + "default link PmenuExtraSel PmenuSel", + "default link PmenuKind Pmenu", + "default link PmenuKindSel PmenuSel", + "default link PmenuSbar Pmenu", + "default link Substitute Search", + "default link TabLine StatusLineNC", + "default link TabLineFill TabLine", + "default link TermCursorNC NONE", + "default link VertSplit WinSeparator", + "default link VisualNOS Visual", + "default link Whitespace NonText", + "default link WildMenu PmenuSel", + "default link WinSeparator Normal", + + // Syntax + "default link Character Constant", + "default link Number Constant", + "default link Boolean Constant", + "default link Float Number", + "default link Conditional Statement", + "default link Repeat Statement", + "default link Label Statement", + "default link Keyword Statement", + "default link Exception Statement", + "default link Include PreProc", + "default link Define PreProc", + "default link Macro PreProc", + "default link PreCondit PreProc", + "default link StorageClass Type", + "default link Structure Type", + "default link Typedef Type", + "default link Tag Special", + "default link SpecialChar Special", "default link SpecialComment Special", - "default link Debug Special", - "default DiagnosticError ctermfg=1 guifg=Red", - "default DiagnosticWarn ctermfg=3 guifg=Orange", - "default DiagnosticInfo ctermfg=4 guifg=LightBlue", - "default DiagnosticHint ctermfg=7 guifg=LightGrey", - "default DiagnosticOk ctermfg=10 guifg=LightGreen", - "default DiagnosticUnderlineError cterm=underline gui=underline guisp=Red", - "default DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange", - "default DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue", - "default DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey", - "default DiagnosticUnderlineOk cterm=underline gui=underline guisp=LightGreen", - "default link DiagnosticVirtualTextError DiagnosticError", - "default link DiagnosticVirtualTextWarn DiagnosticWarn", - "default link DiagnosticVirtualTextInfo DiagnosticInfo", - "default link DiagnosticVirtualTextHint DiagnosticHint", - "default link DiagnosticVirtualTextOk DiagnosticOk", - "default link DiagnosticFloatingError DiagnosticError", - "default link DiagnosticFloatingWarn DiagnosticWarn", - "default link DiagnosticFloatingInfo DiagnosticInfo", - "default link DiagnosticFloatingHint DiagnosticHint", - "default link DiagnosticFloatingOk DiagnosticOk", - "default link DiagnosticSignError DiagnosticError", - "default link DiagnosticSignWarn DiagnosticWarn", - "default link DiagnosticSignInfo DiagnosticInfo", - "default link DiagnosticSignHint DiagnosticHint", - "default link DiagnosticSignOk DiagnosticOk", - "default DiagnosticDeprecated cterm=strikethrough gui=strikethrough guisp=Red", - "default link DiagnosticUnnecessary Comment", - "default link LspInlayHint NonText", + "default link Debug Special", + "default link Ignore Normal", + "default link LspInlayHint NonText", "default link SnippetTabstop Visual", - // Text - "default link @text.literal Comment", - "default link @text.reference Identifier", - "default link @text.title Title", - "default link @text.uri Underlined", - "default link @text.underline Underlined", - "default link @text.todo Todo", + // Diagnostic + "default link DiagnosticFloatingError DiagnosticError", + "default link DiagnosticFloatingWarn DiagnosticWarn", + "default link DiagnosticFloatingInfo DiagnosticInfo", + "default link DiagnosticFloatingHint DiagnosticHint", + "default link DiagnosticFloatingOk DiagnosticOk", + "default link DiagnosticVirtualTextError DiagnosticError", + "default link DiagnosticVirtualTextWarn DiagnosticWarn", + "default link DiagnosticVirtualTextInfo DiagnosticInfo", + "default link DiagnosticVirtualTextHint DiagnosticHint", + "default link DiagnosticVirtualTextOk DiagnosticOk", + "default link DiagnosticSignError DiagnosticError", + "default link DiagnosticSignWarn DiagnosticWarn", + "default link DiagnosticSignInfo DiagnosticInfo", + "default link DiagnosticSignHint DiagnosticHint", + "default link DiagnosticSignOk DiagnosticOk", + "default link DiagnosticUnnecessary Comment", + + // Treesitter standard groups + "default link @variable.builtin Special", + + "default link @constant Constant", + "default link @constant.builtin Special", - // Miscs - "default link @comment Comment", - "default link @punctuation Delimiter", + "default link @module Structure", + "default link @module.builtin Special", + "default link @label Label", - // Constants - "default link @constant Constant", - "default link @constant.builtin Special", - "default link @constant.macro Define", - "default link @define Define", - "default link @macro Macro", - "default link @string String", - "default link @string.escape SpecialChar", - "default link @string.special SpecialChar", - "default link @character Character", + "default link @string String", + "default link @string.regexp @string.special", + "default link @string.escape @string.special", + "default link @string.special SpecialChar", + "default link @string.special.url Underlined", + + "default link @character Character", "default link @character.special SpecialChar", - "default link @number Number", - "default link @boolean Boolean", - "default link @float Float", - // Functions - "default link @function Function", + "default link @boolean Boolean", + "default link @number Number", + "default link @number.float Float", + + "default link @type Type", + "default link @type.builtin Special", + + "default link @attribute Macro", + "default link @property Identifier", + + "default link @function Function", "default link @function.builtin Special", - "default link @function.macro Macro", - "default link @parameter Identifier", - "default link @method Function", - "default link @field Identifier", - "default link @property Identifier", + "default link @constructor Special", + "default link @operator Operator", - // Keywords - "default link @conditional Conditional", - "default link @repeat Repeat", - "default link @label Label", - "default link @operator Operator", "default link @keyword Keyword", - "default link @exception Exception", - - "default link @variable Identifier", - "default link @type Type", - "default link @type.definition Typedef", - "default link @storageclass StorageClass", - "default link @namespace Identifier", - "default link @include Include", - "default link @preproc PreProc", - "default link @debug Debug", + + "default link @punctuation Delimiter", // fallback for subgroups; never used itself + "default link @punctuation.special Special", + + "default link @comment Comment", + + "default link @comment.error DiagnosticError", + "default link @comment.warning DiagnosticWarn", + "default link @comment.note DiagnosticInfo", + "default link @comment.todo Todo", + + "@markup.strong gui=bold cterm=bold", + "@markup.italic gui=italic cterm=italic", + "@markup.strikethrough gui=strikethrough cterm=strikethrough", + "@markup.underline gui=underline cterm=underline", + + "default link @markup Special", // fallback for subgroups; never used itself + "default link @markup.heading Title", + "default link @markup.link Underlined", + + "default link @diff.plus Added", + "default link @diff.minus Removed", + "default link @diff.delta Changed", + "default link @tag Tag", // LSP semantic tokens - "default link @lsp.type.class Structure", - "default link @lsp.type.comment Comment", - "default link @lsp.type.decorator Function", - "default link @lsp.type.enum Structure", - "default link @lsp.type.enumMember Constant", - "default link @lsp.type.function Function", - "default link @lsp.type.interface Structure", - "default link @lsp.type.macro Macro", - "default link @lsp.type.method Function", - "default link @lsp.type.namespace Structure", - "default link @lsp.type.parameter Identifier", - "default link @lsp.type.property Identifier", - "default link @lsp.type.struct Structure", - "default link @lsp.type.type Type", - "default link @lsp.type.typeParameter TypeDef", - "default link @lsp.type.variable Identifier", + "default link @lsp.type.class @type", + "default link @lsp.type.comment @comment", + "default link @lsp.type.decorator @attribute", + "default link @lsp.type.enum @type", + "default link @lsp.type.enumMember @constant", + "default link @lsp.type.function @function", + "default link @lsp.type.interface @type", + "default link @lsp.type.macro @constant.macro", + "default link @lsp.type.method @function.method", + "default link @lsp.type.namespace @module", + "default link @lsp.type.parameter @variable.parameter", + "default link @lsp.type.property @property", + "default link @lsp.type.struct @type", + "default link @lsp.type.type @type", + "default link @lsp.type.typeParameter @type.definition", + "default link @lsp.type.variable @variable", NULL }; // Default colors only used with a light background. static const char *highlight_init_light[] = { - "ColorColumn ctermbg=LightRed guibg=LightRed", - "CursorColumn ctermbg=LightGrey guibg=Grey90", - "CursorLine cterm=underline guibg=Grey90", - "CursorLineNr cterm=underline ctermfg=Brown gui=bold guifg=Brown", - "DiffAdd ctermbg=LightBlue guibg=LightBlue", - "DiffChange ctermbg=LightMagenta guibg=LightMagenta", - "DiffDelete ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan", - "Directory ctermfg=DarkBlue guifg=Blue", - "FoldColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", - "Folded ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue", - "LineNr ctermfg=Brown guifg=Brown", - "MatchParen ctermbg=Cyan guibg=Cyan", - "MoreMsg ctermfg=DarkGreen gui=bold guifg=SeaGreen", - "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta", - "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey", - "PmenuThumb ctermbg=Black guibg=Black", - "Question ctermfg=DarkGreen gui=bold guifg=SeaGreen", - "Search ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE", - "SignColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", - "SpecialKey ctermfg=DarkBlue guifg=Blue", - "SpellBad ctermbg=LightRed guisp=Red gui=undercurl", - "SpellCap ctermbg=LightBlue guisp=Blue gui=undercurl", - "SpellLocal ctermbg=Cyan guisp=DarkCyan gui=undercurl", - "SpellRare ctermbg=LightMagenta guisp=Magenta gui=undercurl", - "TabLine cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey", - "Title ctermfg=DarkMagenta gui=bold guifg=Magenta", - "Visual guibg=LightGrey", - "WarningMsg ctermfg=DarkRed guifg=Red", - "Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=Blue guibg=NONE", - "Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE", - "Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a5acd guibg=NONE", - "Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE", - "Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE", - "PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a0dad guibg=NONE", - "Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=bold guifg=SeaGreen guibg=NONE", - "Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=SlateBlue", - "Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=bg guibg=NONE", + "Normal guifg=NvimDarkGrey2 guibg=NvimLightGrey2 ctermfg=NONE ctermbg=NONE", + + // UI + "Added guifg=NvimDarkGreen ctermfg=2", + "Changed guifg=NvimDarkCyan ctermfg=6", + "ColorColumn guibg=NvimLightGrey4 cterm=reverse", + "Conceal guifg=NvimLightGrey4", + "CurSearch guifg=NvimLightGrey1 guibg=NvimDarkYellow ctermfg=15 ctermbg=3", + "CursorColumn guibg=NvimLightGrey3", + "CursorLine guibg=NvimLightGrey3", + "DiffAdd guifg=NvimDarkGrey1 guibg=NvimLightGreen ctermfg=15 ctermbg=2", + "DiffChange guifg=NvimDarkGrey1 guibg=NvimLightGrey4", + "DiffDelete guifg=NvimDarkRed gui=bold ctermfg=1 cterm=bold", + "DiffText guifg=NvimDarkGrey1 guibg=NvimLightCyan ctermfg=15 ctermbg=6", + "Directory guifg=NvimDarkCyan ctermfg=6", + "ErrorMsg guifg=NvimDarkRed ctermfg=1", + "FloatShadow guibg=NvimLightGrey4 ctermbg=0 blend=80", + "FloatShadowThrough guibg=NvimLightGrey4 ctermbg=0 blend=100", + "Folded guifg=NvimDarkGrey4 guibg=NvimLightGrey3", + "LineNr guifg=NvimLightGrey4", + "MatchParen guibg=NvimLightGrey4 gui=bold cterm=bold,underline", + "ModeMsg guifg=NvimDarkGreen ctermfg=2", + "MoreMsg guifg=NvimDarkCyan ctermfg=6", + "NonText guifg=NvimLightGrey4", + "NormalFloat guibg=NvimLightGrey1", + "Pmenu guibg=NvimLightGrey3 cterm=reverse", + "PmenuSel guifg=NvimLightGrey3 guibg=NvimDarkGrey2 cterm=reverse,underline blend=0", + "PmenuThumb guibg=NvimLightGrey4", + "Question guifg=NvimDarkCyan ctermfg=6", + "QuickFixLine guifg=NvimDarkCyan ctermfg=6", + "RedrawDebugClear guibg=NvimLightYellow ctermfg=15 ctermbg=3", + "RedrawDebugComposed guibg=NvimLightGreen ctermfg=15 ctermbg=2", + "RedrawDebugRecompose guibg=NvimLightRed ctermfg=15 ctermbg=1", + "Removed guifg=NvimDarkRed ctermfg=1", + "Search guifg=NvimDarkGrey1 guibg=NvimLightYellow ctermfg=15 ctermbg=3", + "SignColumn guifg=NvimLightGrey4", + "SpecialKey guifg=NvimLightGrey4", + "SpellBad guisp=NvimDarkRed gui=undercurl cterm=undercurl", + "SpellCap guisp=NvimDarkYellow gui=undercurl cterm=undercurl", + "SpellLocal guisp=NvimDarkGreen gui=undercurl cterm=undercurl", + "SpellRare guisp=NvimDarkCyan gui=undercurl cterm=undercurl", + "StatusLine guifg=NvimLightGrey3 guibg=NvimDarkGrey3 cterm=reverse", + "StatusLineNC guifg=NvimDarkGrey3 guibg=NvimLightGrey3 cterm=bold", + "Title guifg=NvimDarkGrey2 gui=bold cterm=bold", + "Visual guibg=NvimLightGrey4 ctermfg=15 ctermbg=0", + "WarningMsg guifg=NvimDarkYellow ctermfg=3", + "WinBar guifg=NvimDarkGrey4 guibg=NvimLightGrey1 gui=bold cterm=bold", + "WinBarNC guifg=NvimDarkGrey4 guibg=NvimLightGrey1 cterm=bold", + + // Syntax + "Constant guifg=NvimDarkGrey2", // Use only `Normal` foreground to be usable on different background + "Operator guifg=NvimDarkGrey2", + "PreProc guifg=NvimDarkGrey2", + "Type guifg=NvimDarkGrey2", + "Delimiter guifg=NvimDarkGrey2", + + "Comment guifg=NvimDarkGrey4", + "String guifg=NvimDarkGreen ctermfg=2", + "Identifier guifg=NvimDarkBlue ctermfg=4", + "Function guifg=NvimDarkCyan ctermfg=6", + "Statement guifg=NvimDarkGrey2 gui=bold cterm=bold", + "Special guifg=NvimDarkCyan ctermfg=6", + "Error guifg=NvimDarkGrey1 guibg=NvimLightRed ctermfg=15 ctermbg=1", + "Todo guifg=NvimDarkGrey2 gui=bold cterm=bold", + + // Diagnostic + "DiagnosticError guifg=NvimDarkRed ctermfg=1", + "DiagnosticWarn guifg=NvimDarkYellow ctermfg=3", + "DiagnosticInfo guifg=NvimDarkCyan ctermfg=6", + "DiagnosticHint guifg=NvimDarkBlue ctermfg=4", + "DiagnosticOk guifg=NvimDarkGreen ctermfg=2", + "DiagnosticUnderlineError guisp=NvimDarkRed gui=underline cterm=underline", + "DiagnosticUnderlineWarn guisp=NvimDarkYellow gui=underline cterm=underline", + "DiagnosticUnderlineInfo guisp=NvimDarkCyan gui=underline cterm=underline", + "DiagnosticUnderlineHint guisp=NvimDarkBlue gui=underline cterm=underline", + "DiagnosticUnderlineOk guisp=NvimDarkGreen gui=underline cterm=underline", + "DiagnosticDeprecated guisp=NvimDarkRed gui=strikethrough cterm=strikethrough", + + // Treesitter standard groups + "@variable guifg=NvimDarkGrey2", NULL }; // Default colors only used with a dark background. static const char *highlight_init_dark[] = { - "ColorColumn ctermbg=DarkRed guibg=DarkRed", - "CursorColumn ctermbg=DarkGrey guibg=Grey40", - "CursorLine cterm=underline guibg=Grey40", - "CursorLineNr cterm=underline ctermfg=Yellow gui=bold guifg=Yellow", - "DiffAdd ctermbg=DarkBlue guibg=DarkBlue", - "DiffChange ctermbg=DarkMagenta guibg=DarkMagenta", - "DiffDelete ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan", - "Directory ctermfg=LightCyan guifg=Cyan", - "FoldColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", - "Folded ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan", - "LineNr ctermfg=Yellow guifg=Yellow", - "MatchParen ctermbg=DarkCyan guibg=DarkCyan", - "MoreMsg ctermfg=LightGreen gui=bold guifg=SeaGreen", - "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta", - "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey", - "PmenuThumb ctermbg=White guibg=White", - "Question ctermfg=LightGreen gui=bold guifg=Green", - "Search ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", - "SignColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", - "SpecialKey ctermfg=LightBlue guifg=Cyan", - "SpellBad ctermbg=Red guisp=Red gui=undercurl", - "SpellCap ctermbg=Blue guisp=Blue gui=undercurl", - "SpellLocal ctermbg=Cyan guisp=Cyan gui=undercurl", - "SpellRare ctermbg=Magenta guisp=Magenta gui=undercurl", - "TabLine cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey", - "Title ctermfg=LightMagenta gui=bold guifg=Magenta", - "Visual guibg=DarkGrey", - "WarningMsg ctermfg=LightRed guifg=Red", - "Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#80a0ff guibg=NONE", - "Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=#ffa0a0 guibg=NONE", - "Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=Orange guibg=NONE", - "Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#40ffff guibg=NONE", - "Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=bold guifg=#ffff60 guibg=NONE", - "PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=#ff80ff guibg=NONE", - "Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=bold guifg=#60ff60 guibg=NONE", - "Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=#80a0ff", - "Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=bg guibg=NONE", + "Normal guifg=NvimLightGrey2 guibg=NvimDarkGrey2 ctermfg=NONE ctermbg=NONE", + + // UI + "Added guifg=NvimLightGreen ctermfg=10", + "Changed guifg=NvimLightCyan ctermfg=14", + "ColorColumn guibg=NvimDarkGrey4 cterm=reverse", + "Conceal guifg=NvimDarkGrey4", + "CurSearch guifg=NvimDarkGrey1 guibg=NvimLightYellow ctermfg=0 ctermbg=11", + "CursorColumn guibg=NvimDarkGrey3", + "CursorLine guibg=NvimDarkGrey3", + "DiffAdd guifg=NvimLightGrey1 guibg=NvimDarkGreen ctermfg=0 ctermbg=10", + "DiffChange guifg=NvimLightGrey1 guibg=NvimDarkGrey4", + "DiffDelete guifg=NvimLightRed gui=bold ctermfg=9 cterm=bold", + "DiffText guifg=NvimLightGrey1 guibg=NvimDarkCyan ctermfg=0 ctermbg=14", + "Directory guifg=NvimLightCyan ctermfg=14", + "ErrorMsg guifg=NvimLightRed ctermfg=9", + "FloatShadow guibg=NvimDarkGrey4 ctermbg=0 blend=80", + "FloatShadowThrough guibg=NvimDarkGrey4 ctermbg=0 blend=100", + "Folded guifg=NvimLightGrey4 guibg=NvimDarkGrey3", + "LineNr guifg=NvimDarkGrey4", + "MatchParen guibg=NvimDarkGrey4 gui=bold cterm=bold,underline", + "ModeMsg guifg=NvimLightGreen ctermfg=10", + "MoreMsg guifg=NvimLightCyan ctermfg=14", + "NonText guifg=NvimDarkGrey4", + "NormalFloat guibg=NvimDarkGrey1", + "Pmenu guibg=NvimDarkGrey3 cterm=reverse", + "PmenuSel guifg=NvimDarkGrey3 guibg=NvimLightGrey2 cterm=reverse,underline blend=0", + "PmenuThumb guibg=NvimDarkGrey4", + "Question guifg=NvimLightCyan ctermfg=14", + "QuickFixLine guifg=NvimLightCyan ctermfg=14", + "RedrawDebugClear guibg=NvimDarkYellow ctermfg=0 ctermbg=11", + "RedrawDebugComposed guibg=NvimDarkGreen ctermfg=0 ctermbg=10", + "RedrawDebugRecompose guibg=NvimDarkRed ctermfg=0 ctermbg=9", + "Removed guifg=NvimLightRed ctermfg=9", + "Search guifg=NvimLightGrey1 guibg=NvimDarkYellow ctermfg=0 ctermbg=11", + "SignColumn guifg=NvimDarkGrey4", + "SpecialKey guifg=NvimDarkGrey4", + "SpellBad guisp=NvimLightRed gui=undercurl cterm=undercurl", + "SpellCap guisp=NvimLightYellow gui=undercurl cterm=undercurl", + "SpellLocal guisp=NvimLightGreen gui=undercurl cterm=undercurl", + "SpellRare guisp=NvimLightCyan gui=undercurl cterm=undercurl", + "StatusLine guifg=NvimDarkGrey3 guibg=NvimLightGrey3 cterm=reverse", + "StatusLineNC guifg=NvimLightGrey3 guibg=NvimDarkGrey3 cterm=bold", + "Title guifg=NvimLightGrey2 gui=bold cterm=bold", + "Visual guibg=NvimDarkGrey4 ctermfg=0 ctermbg=15", + "WarningMsg guifg=NvimLightYellow ctermfg=11", + "WinBar guifg=NvimLightGrey4 guibg=NvimDarkGrey1 gui=bold cterm=bold", + "WinBarNC guifg=NvimLightGrey4 guibg=NvimDarkGrey1 cterm=bold", + + // Syntax + "Constant guifg=NvimLightGrey2", // Use only `Normal` foreground to be usable on different background + "Operator guifg=NvimLightGrey2", + "PreProc guifg=NvimLightGrey2", + "Type guifg=NvimLightGrey2", + "Delimiter guifg=NvimLightGrey2", + + "Comment guifg=NvimLightGrey4", + "String guifg=NvimLightGreen ctermfg=10", + "Identifier guifg=NvimLightBlue ctermfg=12", + "Function guifg=NvimLightCyan ctermfg=14", + "Statement guifg=NvimLightGrey2 gui=bold cterm=bold", + "Special guifg=NvimLightCyan ctermfg=14", + "Error guifg=NvimLightGrey1 guibg=NvimDarkRed ctermfg=0 ctermbg=9", + "Todo guifg=NvimLightGrey2 gui=bold cterm=bold", + + // Diagnostic + "DiagnosticError guifg=NvimLightRed ctermfg=9", + "DiagnosticWarn guifg=NvimLightYellow ctermfg=11", + "DiagnosticInfo guifg=NvimLightCyan ctermfg=14", + "DiagnosticHint guifg=NvimLightBlue ctermfg=12", + "DiagnosticOk guifg=NvimLightGreen ctermfg=10", + "DiagnosticUnderlineError guisp=NvimLightRed gui=underline cterm=underline", + "DiagnosticUnderlineWarn guisp=NvimLightYellow gui=underline cterm=underline", + "DiagnosticUnderlineInfo guisp=NvimLightCyan gui=underline cterm=underline", + "DiagnosticUnderlineHint guisp=NvimLightBlue gui=underline cterm=underline", + "DiagnosticUnderlineOk guisp=NvimLightGreen gui=underline cterm=underline", + "DiagnosticDeprecated guisp=NvimLightRed gui=strikethrough cterm=strikethrough", + + // Treesitter standard groups + "@variable guifg=NvimLightGrey2", NULL }; @@ -626,7 +705,7 @@ void syn_init_cmdline_highlight(bool reset, bool init) /// @param reset clear groups first void init_highlight(bool both, bool reset) { - static int had_both = false; + static bool had_both = false; // Try finding the color scheme file. Used when a color file was loaded // and 'background' or 't_Co' is changed. @@ -663,22 +742,6 @@ void init_highlight(bool both, bool reset) do_highlight(pp[i], reset, true); } - // Reverse looks ugly, but grey may not work for 8 colors. Thus let it - // depend on the number of colors available. - // With 8 colors brown is equal to yellow, need to use black for Search fg - // to avoid Statement highlighted text disappears. - // Clear the attributes, needed when changing the t_Co value. - if (t_colors > 8) { - do_highlight((*p_bg == 'l' - ? "Visual cterm=NONE ctermbg=LightGrey" - : "Visual cterm=NONE ctermbg=DarkGrey"), false, true); - } else { - do_highlight("Visual cterm=reverse ctermbg=NONE", false, true); - if (*p_bg == 'l') { - do_highlight("Search ctermfg=black", false, true); - } - } - syn_init_cmdline_highlight(false, false); } @@ -950,16 +1013,12 @@ void do_highlight(const char *line, const bool forceit, const bool init) // Handle ":highlight link {from} {to}" command. if (dolink) { const char *from_start = linep; - const char *from_end; - const char *to_start; - const char *to_end; - int from_id; int to_id; HlGroup *hlgroup = NULL; - from_end = skiptowhite(from_start); - to_start = skipwhite(from_end); - to_end = skiptowhite(to_start); + const char *from_end = skiptowhite(from_start); + const char *to_start = skipwhite(from_end); + const char *to_end = skiptowhite(to_start); if (ends_excmd((uint8_t)(*from_start)) || ends_excmd((uint8_t)(*to_start))) { @@ -973,7 +1032,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) return; } - from_id = syn_check_group(from_start, (size_t)(from_end - from_start)); + int from_id = syn_check_group(from_start, (size_t)(from_end - from_start)); if (strncmp(to_start, "NONE", 4) == 0) { to_id = 0; } else { @@ -1280,9 +1339,10 @@ void do_highlight(const char *line, const bool forceit, const bool init) // wrong. if (dark != -1 && dark != (*p_bg == 'd') - && !option_was_set("bg")) { - set_option_value_give_err("bg", CSTR_AS_OPTVAL(dark ? "dark" : "light"), 0); - reset_option_was_set("bg"); + && !option_was_set(kOptBackground)) { + set_option_value_give_err(kOptBackground, + CSTR_AS_OPTVAL(dark ? "dark" : "light"), 0); + reset_option_was_set(kOptBackground); } } } @@ -1528,7 +1588,7 @@ static void highlight_list_one(const int id) sgp->sg_blend + 1, NULL, "blend"); if (sgp->sg_link && !got_int) { - (void)syn_list_header(didh, 0, id, true); + syn_list_header(didh, 0, id, true); didh = true; msg_puts_attr("links to", HL_ATTR(HLF_D)); msg_putchar(' '); @@ -1612,10 +1672,7 @@ Dictionary ns_get_hl_defs(NS ns_id, Dict(get_highlight) *opts, Arena *arena, Err return rv; cleanup: - api_free_integer(id); - api_free_boolean(link); - Dictionary empty = ARRAY_DICT_INIT; - return empty; + return (Dictionary)ARRAY_DICT_INIT; } /// Outputs a highlight when doing ":hi MyHighlight" @@ -1658,7 +1715,7 @@ static bool highlight_list_arg(const int id, bool didh, const int type, int iarg } } - (void)syn_list_header(didh, vim_strsize(ts) + (int)strlen(name) + 1, id, false); + syn_list_header(didh, vim_strsize(ts) + (int)strlen(name) + 1, id, false); didh = true; if (!got_int) { if (*name != NUL) { @@ -1842,8 +1899,8 @@ static void set_hl_attr(int idx) HlGroup *sgp = hl_table + idx; at_en.cterm_ae_attr = (int16_t)sgp->sg_cterm; - at_en.cterm_fg_color = sgp->sg_cterm_fg; - at_en.cterm_bg_color = sgp->sg_cterm_bg; + at_en.cterm_fg_color = (int16_t)sgp->sg_cterm_fg; + at_en.cterm_bg_color = (int16_t)sgp->sg_cterm_bg; at_en.rgb_ae_attr = (int16_t)sgp->sg_gui; // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name @@ -2046,7 +2103,6 @@ int syn_get_final_id(int hl_id) bool syn_ns_get_final_id(int *ns_id, int *hl_idp) { - int count; int hl_id = *hl_idp; bool used = false; @@ -2057,7 +2113,7 @@ bool syn_ns_get_final_id(int *ns_id, int *hl_idp) // Follow links until there is no more. // Look out for loops! Break after 100 links. - for (count = 100; --count >= 0;) { + for (int count = 100; --count >= 0;) { HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one // TODO(bfredl): when using "tmp" attribute (no link) the function might be @@ -2182,7 +2238,7 @@ void highlight_changed(void) HlAttrs attrs = syn_attr2entry(highlight_attr[hlf]); msg_grid.blending = attrs.hl_blend > -1; } - ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]), + ui_call_hl_group_set(cstr_as_string(hlf_names[hlf]), highlight_attr[hlf]); highlight_attr_last[hlf] = highlight_attr[hlf]; } @@ -2829,6 +2885,29 @@ color_name_table_T color_name_table[] = { { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) }, { "Navy", RGB_(0x00, 0x00, 0x80) }, { "NavyBlue", RGB_(0x0, 0x0, 0x80) }, + // Default Neovim palettes. + // Dark/light palette is used for background in dark/light color scheme and + // for foreground in light/dark color scheme. + { "NvimDarkBlue", RGB_(0x00, 0x4c, 0x73) }, + { "NvimDarkCyan", RGB_(0x00, 0x73, 0x73) }, + { "NvimDarkGreen", RGB_(0x00, 0x55, 0x23) }, + { "NvimDarkGrey1", RGB_(0x07, 0x08, 0x0d) }, + { "NvimDarkGrey2", RGB_(0x14, 0x16, 0x1b) }, + { "NvimDarkGrey3", RGB_(0x2c, 0x2e, 0x33) }, + { "NvimDarkGrey4", RGB_(0x4f, 0x52, 0x58) }, + { "NvimDarkMagenta", RGB_(0x47, 0x00, 0x45) }, + { "NvimDarkRed", RGB_(0x59, 0x00, 0x08) }, + { "NvimDarkYellow", RGB_(0x6b, 0x53, 0x00) }, + { "NvimLightBlue", RGB_(0xa6, 0xdb, 0xff) }, + { "NvimLightCyan", RGB_(0x8c, 0xf8, 0xf7) }, + { "NvimLightGreen", RGB_(0xb3, 0xf6, 0xc0) }, + { "NvimLightGrey1", RGB_(0xee, 0xf1, 0xf8) }, + { "NvimLightGrey2", RGB_(0xe0, 0xe2, 0xea) }, + { "NvimLightGrey3", RGB_(0xc4, 0xc6, 0xcd) }, + { "NvimLightGrey4", RGB_(0x9b, 0x9e, 0xa4) }, + { "NvimLightMagenta", RGB_(0xff, 0xca, 0xff) }, + { "NvimLightRed", RGB_(0xff, 0xc0, 0xb9) }, + { "NvimLightYellow", RGB_(0xfc, 0xe0, 0x94) }, { "OldLace", RGB_(0xfd, 0xf5, 0xe6) }, { "Olive", RGB_(0x80, 0x80, 0x00) }, { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) }, diff --git a/src/nvim/highlight_group.h b/src/nvim/highlight_group.h index ca7bd36271..47d58d20f2 100644 --- a/src/nvim/highlight_group.h +++ b/src/nvim/highlight_group.h @@ -1,19 +1,18 @@ #pragma once -#include "nvim/api/keysets_defs.h" +#include "nvim/api/keysets_defs.h" // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep -#include "nvim/api/private/helpers.h" #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/highlight_defs.h" #include "nvim/types_defs.h" // IWYU pragma: keep -#define MAX_HL_ID 20000 // maximum value for a highlight ID. +enum { MAX_HL_ID = 20000, }; ///< maximum value for a highlight ID. typedef struct { char *name; RgbValue color; } color_name_table_T; -extern color_name_table_T color_name_table[]; +extern color_name_table_T color_name_table[700]; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "highlight_group.h.generated.h" diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 348f3a6528..14247b6d86 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -7,6 +7,7 @@ #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -17,24 +18,27 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/extmark.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/extmark_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/indent.h" #include "nvim/indent_c.h" -#include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/input.h" #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/search.h" #include "nvim/state_defs.h" #include "nvim/strings.h" @@ -54,15 +58,13 @@ bool tabstop_set(char *var, colnr_T **array) { int valcount = 1; - int t; - char *cp; if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) { *array = NULL; return true; } - for (cp = var; *cp != NUL; cp++) { + for (char *cp = var; *cp != NUL; cp++) { if (cp == var || cp[-1] == ',') { char *end; @@ -90,8 +92,8 @@ bool tabstop_set(char *var, colnr_T **array) *array = (colnr_T *)xmalloc((unsigned)(valcount + 1) * sizeof(int)); (*array)[0] = (colnr_T)valcount; - t = 1; - for (cp = var; *cp != NUL;) { + int t = 1; + for (char *cp = var; *cp != NUL;) { int n = atoi(cp); // Catch negative values, overflow and ridiculous big values. @@ -116,6 +118,7 @@ bool tabstop_set(char *var, colnr_T **array) /// If "vts" is set then the tab widths are taken from that array, /// otherwise the value of ts is used. int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts) + FUNC_ATTR_PURE { OptInt ts = ts_arg == 0 ? 8 : ts_arg; colnr_T tabcol = 0; @@ -172,14 +175,13 @@ int tabstop_at(colnr_T col, OptInt ts, const colnr_T *vts) colnr_T tabstop_start(colnr_T col, int ts, colnr_T *vts) { colnr_T tabcol = 0; - int t; if (vts == NULL || vts[0] == 0) { return ((col / ts) * ts); } const int tabcount = vts[0]; - for (t = 1; t <= tabcount; t++) { + for (int t = 1; t <= tabcount; t++) { tabcol += vts[t]; if (tabcol > col) { return (tabcol - vts[t]); @@ -259,8 +261,6 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, int ts_arg, const colnr_ /// See if two tabstop arrays contain the same values. bool tabstop_eq(const colnr_T *ts1, const colnr_T *ts2) { - int t; - if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) { return false; } @@ -271,7 +271,7 @@ bool tabstop_eq(const colnr_T *ts1, const colnr_T *ts2) return false; } - for (t = 1; t <= ts1[0]; t++) { + for (int t = 1; t <= ts1[0]; t++) { if (ts1[t] != ts2[t]) { return false; } @@ -283,15 +283,12 @@ bool tabstop_eq(const colnr_T *ts1, const colnr_T *ts2) /// Copy a tabstop array, allocating space for the new array. int *tabstop_copy(const int *oldts) { - int *newts; - int t; - if (oldts == 0) { return 0; } - newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(int)); - for (t = 0; t <= oldts[0]; t++) { + int *newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(int)); + for (int t = 0; t <= oldts[0]; t++) { newts[t] = oldts[t]; } @@ -353,117 +350,127 @@ int get_sts_value(void) return result; } -// Count the size (in window cells) of the indent in the current line. +/// Count the size (in window cells) of the indent in the current line. int get_indent(void) { - return get_indent_str_vtab(get_cursor_line_ptr(), - curbuf->b_p_ts, - curbuf->b_p_vts_array, - false); + return indent_size_ts(get_cursor_line_ptr(), curbuf->b_p_ts, curbuf->b_p_vts_array); } -// Count the size (in window cells) of the indent in line "lnum". +/// Count the size (in window cells) of the indent in line "lnum". int get_indent_lnum(linenr_T lnum) { - return get_indent_str_vtab(ml_get(lnum), - curbuf->b_p_ts, - curbuf->b_p_vts_array, - false); + return indent_size_ts(ml_get(lnum), curbuf->b_p_ts, curbuf->b_p_vts_array); } -// Count the size (in window cells) of the indent in line "lnum" of buffer -// "buf". +/// Count the size (in window cells) of the indent in line "lnum" of buffer "buf". int get_indent_buf(buf_T *buf, linenr_T lnum) { - return get_indent_str_vtab(ml_get_buf(buf, lnum), buf->b_p_ts, buf->b_p_vts_array, false); + return indent_size_ts(ml_get_buf(buf, lnum), buf->b_p_ts, buf->b_p_vts_array); } -/// Count the size (in window cells) of the indent in line "ptr", with -/// 'tabstop' at "ts". -/// If @param list is true, count only screen size for tabs. -int get_indent_str(const char *ptr, int ts, bool list) - FUNC_ATTR_NONNULL_ALL +/// Compute the size of the indent (in window cells) in line "ptr", +/// without tabstops (count tab as ^I or <09>). +int indent_size_no_ts(char const *ptr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE { - int count = 0; - - for (; *ptr; ptr++) { - // Count a tab for what it is worth. - if (*ptr == TAB) { - if (!list || curwin->w_p_lcs_chars.tab1) { - // count a tab for what it is worth - count += ts - (count % ts); - } else { - // In list mode, when tab is not set, count screen char width - // for Tab, displays: ^I - count += ptr2cells(ptr); - } - } else if (*ptr == ' ') { - // Count a space for one. - count++; + int tab_size = byte2cells(TAB); + + int vcol = 0; + while (true) { + char const c = *ptr++; + if (c == ' ') { + vcol++; + } else if (c == TAB) { + vcol += tab_size; } else { - break; + return vcol; } } - return count; } -/// Count the size (in window cells) of the indent in line "ptr", using -/// variable tabstops. -/// if "list" is true, count only screen size for tabs. -int get_indent_str_vtab(const char *ptr, OptInt ts, colnr_T *vts, bool list) +/// Compute the size of the indent (in window cells) in line "ptr", +/// using tabstops +int indent_size_ts(char const *ptr, OptInt ts, colnr_T *vts) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_PURE { - int count = 0; + assert(char2cells(' ') == 1); + + int vcol = 0; + int tabstop_width, next_tab_vcol; + + if (vts == NULL || vts[0] < 1) { // tab has fixed width + // can ts be 0 ? This is from tabstop_padding(). + tabstop_width = (int)(ts == 0 ? 8 : ts); + next_tab_vcol = tabstop_width; + } else { // tab has variable width + colnr_T *cur_tabstop = vts + 1; + colnr_T *const last_tabstop = vts + vts[0]; + + while (cur_tabstop != last_tabstop) { + int cur_vcol = vcol; + vcol += *cur_tabstop++; + assert(cur_vcol < vcol); + + do { + char const c = *ptr++; + if (c == ' ') { + cur_vcol++; + } else if (c == TAB) { + break; + } else { + return cur_vcol; + } + } while (cur_vcol != vcol); + } - for (; *ptr; ptr++) { - if (*ptr == TAB) { // count a tab for what it is worth - if (!list || curwin->w_p_lcs_chars.tab1) { - count += tabstop_padding(count, ts, vts); - } else { - // In list mode, when tab is not set, count screen char width - // for Tab, displays: ^I - count += ptr2cells(ptr); - } - } else if (*ptr == ' ') { - count++; // count a space for one + tabstop_width = *last_tabstop; + next_tab_vcol = vcol + tabstop_width; + } + + assert(tabstop_width != 0); + while (true) { + char const c = *ptr++; + if (c == ' ') { + vcol++; + next_tab_vcol += (vcol == next_tab_vcol) ? tabstop_width : 0; + } else if (c == TAB) { + vcol = next_tab_vcol; + next_tab_vcol += tabstop_width; } else { - break; + return vcol; } } - return count; } -// Set the indent of the current line. -// Leaves the cursor on the first non-blank in the line. -// Caller must take care of undo. -// "flags": -// SIN_CHANGED: call changed_bytes() if the line was changed. -// SIN_INSERT: insert the indent in front of the line. -// SIN_UNDO: save line for undo before changing it. -// SIN_NOMARK: don't move extmarks (because just after ml_append or something) -// @param size measured in spaces -// Returns true if the line was changed. -int set_indent(int size, int flags) +/// Set the indent of the current line. +/// Leaves the cursor on the first non-blank in the line. +/// Caller must take care of undo. +/// "flags": +/// SIN_CHANGED: call changed_bytes() if the line was changed. +/// SIN_INSERT: insert the indent in front of the line. +/// SIN_UNDO: save line for undo before changing it. +/// SIN_NOMARK: don't move extmarks (because just after ml_append or something) +/// @param size measured in spaces +/// +/// @return true if the line was changed. +bool set_indent(int size, int flags) { - char *p; char *newline; char *oldline; char *s; - int todo; - int ind_len; // Measured in characters. - int line_len; int doit = false; int ind_done = 0; // Measured in spaces. int tab_pad; - int retval = false; + bool retval = false; // Number of initial whitespace chars when 'et' and 'pi' are both set. int orig_char_len = -1; // First check if there is anything to do and compute the number of // characters needed for the indent. - todo = size; - ind_len = 0; - p = oldline = get_cursor_line_ptr(); + int todo = size; + int ind_len = 0; // Measured in characters. + char *p = oldline = get_cursor_line_ptr(); // Calculate the buffer size for the new indent, and check to see if it // isn't already set. @@ -561,7 +568,7 @@ int set_indent(int size, int flags) } else { p = skipwhite(p); } - line_len = (int)strlen(p) + 1; + int line_len = (int)strlen(p) + 1; // If 'preserveindent' and 'expandtab' are both set keep the original // characters and allocate accordingly. We will fill the rest with spaces @@ -799,49 +806,60 @@ int get_breakindent_win(win_T *wp, char *line) { static int prev_indent = 0; // cached indent value static OptInt prev_ts = 0; // cached tabstop value + static colnr_T *prev_vts = NULL; // cached vartabs values static int prev_fnum = 0; // cached buffer number static char *prev_line = NULL; // cached copy of "line" static varnumber_T prev_tick = 0; // changedtick of cached value - static colnr_T *prev_vts = NULL; // cached vartabs values - static int prev_list = 0; // cached list value + static int prev_list = 0; // cached list indent static int prev_listopt = 0; // cached w_p_briopt_list value + static bool prev_no_ts = false; // cached no_ts value + static unsigned prev_dy_uhex = 0; // cached 'display' "uhex" value static char *prev_flp = NULL; // cached formatlistpat value int bri = 0; // window width minus window margin space, i.e. what rests for text - const int eff_wwidth = wp->w_width_inner - - ((wp->w_p_nu || wp->w_p_rnu) - && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0); - - // used cached indent, unless - // - buffer changed - // - 'tabstop' changed - // - buffer was changed - // - 'briopt_list changed' changed or - // - 'formatlistpattern' changed - // - line changed - // - 'vartabs' changed + const int eff_wwidth = wp->w_width_inner - win_col_off(wp) + win_col_off2(wp); + + // In list mode, if 'listchars' "tab" isn't set, a TAB is displayed as ^I. + const bool no_ts = wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL; + + // Used cached indent, unless + // - buffer changed, or + // - 'tabstop' changed, or + // - 'vartabstop' changed, or + // - buffer was changed, or + // - 'breakindentopt' "list" changed, or + // - 'list' or 'listchars' "tab" changed, or + // - 'display' "uhex" flag changed, or + // - 'formatlistpat' changed, or + // - line changed. if (prev_fnum != wp->w_buffer->b_fnum || prev_ts != wp->w_buffer->b_p_ts + || prev_vts != wp->w_buffer->b_p_vts_array || prev_tick != buf_get_changedtick(wp->w_buffer) || prev_listopt != wp->w_briopt_list + || prev_no_ts != no_ts + || prev_dy_uhex != (dy_flags & DY_UHEX) || prev_flp == NULL || strcmp(prev_flp, get_flp_value(wp->w_buffer)) != 0 - || prev_line == NULL || strcmp(prev_line, line) != 0 - || prev_vts != wp->w_buffer->b_p_vts_array) { + || prev_line == NULL || strcmp(prev_line, line) != 0) { prev_fnum = wp->w_buffer->b_fnum; xfree(prev_line); prev_line = xstrdup(line); prev_ts = wp->w_buffer->b_p_ts; - prev_tick = buf_get_changedtick(wp->w_buffer); prev_vts = wp->w_buffer->b_p_vts_array; if (wp->w_briopt_vcol == 0) { - prev_indent = get_indent_str_vtab(line, - wp->w_buffer->b_p_ts, - wp->w_buffer->b_p_vts_array, - wp->w_p_list); + if (no_ts) { + prev_indent = indent_size_no_ts(line); + } else { + prev_indent = indent_size_ts(line, wp->w_buffer->b_p_ts, + wp->w_buffer->b_p_vts_array); + } } + prev_tick = buf_get_changedtick(wp->w_buffer); prev_listopt = wp->w_briopt_list; prev_list = 0; + prev_no_ts = no_ts; + prev_dy_uhex = (dy_flags & DY_UHEX); xfree(prev_flp); prev_flp = xstrdup(get_flp_value(wp->w_buffer)); // add additional indent for numbered lists @@ -900,7 +918,7 @@ int get_breakindent_win(win_T *wp, char *line) // non-blank in the line. // When extra == 1: Return true if the cursor is before the first non-blank in // the line. -int inindent(int extra) +bool inindent(int extra) { char *ptr; colnr_T col; @@ -934,23 +952,18 @@ static void emsg_text_too_long(void) /// ":retab". void ex_retab(exarg_T *eap) { - linenr_T lnum; bool got_tab = false; int num_spaces = 0; - int num_tabs; - int len; int start_col = 0; // For start of white-space string int64_t start_vcol = 0; // For start of white-space string - int old_len; char *new_line = (char *)1; // init to non-NULL colnr_T *new_vts_array = NULL; char *new_ts_str; // string value of tab argument - int save_list; linenr_T first_line = 0; // first changed line linenr_T last_line = 0; // last changed line - save_list = curwin->w_p_list; + int save_list = curwin->w_p_list; curwin->w_p_list = 0; // don't want list mode here new_ts_str = eap->arg; @@ -970,7 +983,7 @@ void ex_retab(exarg_T *eap) } else { new_ts_str = xmemdupz(new_ts_str, (size_t)(eap->arg - new_ts_str)); } - for (lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) { + for (linenr_T lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) { char *ptr = ml_get(lnum); int col = 0; int64_t vcol = 0; @@ -992,8 +1005,8 @@ void ex_retab(exarg_T *eap) // Retabulate this string of white-space // len is virtual length of white string - len = num_spaces = (int)(vcol - start_vcol); - num_tabs = 0; + int len = num_spaces = (int)(vcol - start_vcol); + int num_tabs = 0; if (!curbuf->b_p_et) { int t, s; @@ -1015,7 +1028,7 @@ void ex_retab(exarg_T *eap) // len is actual number of white characters used len = num_spaces + num_tabs; - old_len = (int)strlen(ptr); + int old_len = (int)strlen(ptr); const int new_len = old_len - col + start_col + len + 1; if (new_len <= 0 || new_len >= MAXCOL) { emsg_text_too_long(); @@ -1092,7 +1105,7 @@ void ex_retab(exarg_T *eap) colnr_T *old_vts_ary = curbuf->b_p_vts_array; if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) { - set_string_option_direct("vts", -1, new_ts_str, OPT_FREE | OPT_LOCAL, 0); + set_string_option_direct(kOptVartabstop, new_ts_str, OPT_LOCAL, 0); curbuf->b_p_vts_array = new_vts_array; xfree(old_vts_ary); } else { @@ -1111,19 +1124,14 @@ void ex_retab(exarg_T *eap) /// Get indent level from 'indentexpr'. int get_expr_indent(void) { - int indent = -1; - pos_T save_pos; - colnr_T save_curswant; - int save_set_curswant; - int save_State; - int use_sandbox = was_set_insecurely(curwin, "indentexpr", OPT_LOCAL); + bool use_sandbox = was_set_insecurely(curwin, kOptIndentexpr, OPT_LOCAL); const sctx_T save_sctx = current_sctx; // Save and restore cursor position and curswant, in case it was changed // * via :normal commands. - save_pos = curwin->w_cursor; - save_curswant = curwin->w_curswant; - save_set_curswant = curwin->w_set_curswant; + pos_T save_pos = curwin->w_cursor; + colnr_T save_curswant = curwin->w_curswant; + bool save_set_curswant = curwin->w_set_curswant; set_vim_var_nr(VV_LNUM, (varnumber_T)curwin->w_cursor.lnum); if (use_sandbox) { @@ -1135,7 +1143,7 @@ int get_expr_indent(void) // Need to make a copy, the 'indentexpr' option could be changed while // evaluating it. char *inde_copy = xstrdup(curbuf->b_p_inde); - indent = (int)eval_to_number(inde_copy); + int indent = (int)eval_to_number(inde_copy); xfree(inde_copy); if (use_sandbox) { @@ -1147,7 +1155,7 @@ int get_expr_indent(void) // Restore the cursor position so that 'indentexpr' doesn't need to. // Pretend to be in Insert mode, allow cursor past end of line for "o" // command. - save_State = State; + int save_State = State; State = MODE_INSERT; curwin->w_cursor = save_pos; curwin->w_curswant = save_curswant; @@ -1188,10 +1196,6 @@ int get_lisp_indent(void) pos_T *pos; pos_T paren; int amount; - char *that; - - // Set vi_lisp to use the vi-compatible method. - int vi_lisp = (vim_strchr(p_cpo, CPO_LISP) != NULL); pos_T realpos = curwin->w_cursor; curwin->w_cursor.col = 0; @@ -1218,7 +1222,7 @@ int get_lisp_indent(void) continue; } - for (that = get_cursor_line_ptr(); *that != NUL; that++) { + for (char *that = get_cursor_line_ptr(); *that != NUL; that++) { if (*that == ';') { while (*(that + 1) != NUL) { that++; @@ -1268,94 +1272,83 @@ int get_lisp_indent(void) curwin->w_cursor.col = pos->col; colnr_T col = pos->col; - that = get_cursor_line_ptr(); + char *line = get_cursor_line_ptr(); + + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, pos->lnum, line); - if (vi_lisp && (get_indent() == 0)) { - amount = 2; + StrCharInfo sci = utf_ptr2StrCharInfo(line); + amount = 0; + while (*sci.ptr != NUL && col > 0) { + amount += win_charsize(cstype, amount, sci.ptr, sci.chr.value, &csarg).width; + sci = utfc_next(sci); + col--; + } + char *that = sci.ptr; + + // Some keywords require "body" indenting rules (the + // non-standard-lisp ones are Scheme special forms): + // (let ((a 1)) instead (let ((a 1)) + // (...)) of (...)) + if (((*that == '(') || (*that == '[')) && lisp_match(that + 1)) { + amount += 2; } else { - char *line = that; - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line); - while (*cts.cts_ptr != NUL && col > 0) { - cts.cts_vcol += lbr_chartabsize_adv(&cts); - col--; + if (*that != NUL) { + that++; + amount++; + } + colnr_T firsttry = amount; + + while (ascii_iswhite(*that)) { + amount += win_charsize(cstype, amount, that, (uint8_t)(*that), &csarg).width; + that++; } - amount = cts.cts_vcol; - that = cts.cts_ptr; - clear_chartabsize_arg(&cts); - - // Some keywords require "body" indenting rules (the - // non-standard-lisp ones are Scheme special forms): - // (let ((a 1)) instead (let ((a 1)) - // (...)) of (...)) - if (!vi_lisp && ((*that == '(') || (*that == '[')) - && lisp_match(that + 1)) { - amount += 2; - } else { - if (*that != NUL) { - that++; - amount++; - } - colnr_T firsttry = amount; - init_chartabsize_arg(&cts, curwin, (colnr_T)(that - line), - amount, line, that); - while (ascii_iswhite(*cts.cts_ptr)) { - cts.cts_vcol += lbr_chartabsize(&cts); - cts.cts_ptr++; + if (*that && (*that != ';')) { + // Not a comment line. + // Test *that != '(' to accommodate first let/do + // argument if it is more than one line. + if ((*that != '(') && (*that != '[')) { + firsttry++; } - that = cts.cts_ptr; - amount = cts.cts_vcol; - clear_chartabsize_arg(&cts); - - if (*that && (*that != ';')) { - // Not a comment line. - // Test *that != '(' to accommodate first let/do - // argument if it is more than one line. - if (!vi_lisp && (*that != '(') && (*that != '[')) { - firsttry++; - } - parencount = 0; - - init_chartabsize_arg(&cts, curwin, - (colnr_T)(that - line), amount, line, that); - if (vi_lisp || ((*that != '"') && (*that != '\'') - && (*that != '#') - && (((uint8_t)(*that) < '0') || ((uint8_t)(*that) > '9')))) { - int quotecount = 0; - while (*cts.cts_ptr - && (!ascii_iswhite(*cts.cts_ptr) || quotecount || parencount) - && (!((*cts.cts_ptr == '(' || *cts.cts_ptr == '[') - && !quotecount && !parencount && vi_lisp))) { - if (*cts.cts_ptr == '"') { - quotecount = !quotecount; - } - if (((*cts.cts_ptr == '(') || (*cts.cts_ptr == '[')) && !quotecount) { - parencount++; - } - if (((*cts.cts_ptr == ')') || (*cts.cts_ptr == ']')) && !quotecount) { - parencount--; - } - if ((*cts.cts_ptr == '\\') && (*(cts.cts_ptr + 1) != NUL)) { - cts.cts_vcol += lbr_chartabsize_adv(&cts); - } - - cts.cts_vcol += lbr_chartabsize_adv(&cts); + parencount = 0; + + CharInfo ci = utf_ptr2CharInfo(that); + if (((ci.value != '"') && (ci.value != '\'') && (ci.value != '#') + && ((ci.value < '0') || (ci.value > '9')))) { + int quotecount = 0; + while (*that && (!ascii_iswhite(ci.value) || quotecount || parencount)) { + if (ci.value == '"') { + quotecount = !quotecount; + } + if (((ci.value == '(') || (ci.value == '[')) && !quotecount) { + parencount++; + } + if (((ci.value == ')') || (ci.value == ']')) && !quotecount) { + parencount--; + } + if ((ci.value == '\\') && (*(that + 1) != NUL)) { + amount += win_charsize(cstype, amount, that, ci.value, &csarg).width; + StrCharInfo next_sci = utfc_next((StrCharInfo){ that, ci }); + that = next_sci.ptr; + ci = next_sci.chr; } - } - while (ascii_iswhite(*cts.cts_ptr)) { - cts.cts_vcol += lbr_chartabsize(&cts); - cts.cts_ptr++; + amount += win_charsize(cstype, amount, that, ci.value, &csarg).width; + StrCharInfo next_sci = utfc_next((StrCharInfo){ that, ci }); + that = next_sci.ptr; + ci = next_sci.chr; } - that = cts.cts_ptr; - amount = cts.cts_vcol; - clear_chartabsize_arg(&cts); + } - if (!*that || (*that == ';')) { - amount = firsttry; - } + while (ascii_iswhite(*that)) { + amount += win_charsize(cstype, amount, that, (uint8_t)(*that), &csarg).width; + that++; + } + + if (!*that || (*that == ';')) { + amount = firsttry; } } } @@ -1374,7 +1367,7 @@ static int lisp_match(char *p) char *word = *curbuf->b_p_lw != NUL ? curbuf->b_p_lw : p_lispwords; while (*word != NUL) { - (void)copy_option_part(&word, buf, sizeof(buf), ","); + copy_option_part(&word, buf, sizeof(buf), ","); int len = (int)strlen(buf); if ((strncmp(buf, p, (size_t)len) == 0) && ascii_iswhite_or_nul(p[len])) { diff --git a/src/nvim/indent.h b/src/nvim/indent.h index b64015958c..54d27e6243 100644 --- a/src/nvim/indent.h +++ b/src/nvim/indent.h @@ -1,6 +1,5 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index c140d468d8..a660c9dead 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -9,12 +9,11 @@ #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/edit.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/macros_defs.h" -#include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/option.h" @@ -24,6 +23,7 @@ #include "nvim/search.h" #include "nvim/state_defs.h" #include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" // Find result cache for cpp_baseclass @@ -3105,8 +3105,8 @@ int get_c_indent(void) } else { // Found first unterminated line on a row, may // line up with this line, remember its indent - // 100 + // NOLINT(whitespace/tab) - // -> here; // NOLINT(whitespace/tab) + // 100 + + // -> here; l = get_cursor_line_ptr(); amount = cur_amount; @@ -3655,7 +3655,7 @@ static int find_match(int lookfor, linenr_T ourscope) if (cin_iselse(look)) { mightbeif = cin_skipcomment(look + 4); if (!cin_isif(mightbeif)) { - elselevel++; // NOLINT(readability/braces) + elselevel++; } continue; } @@ -3670,7 +3670,7 @@ static int find_match(int lookfor, linenr_T ourscope) // If it's an "if" decrement elselevel look = cin_skipcomment(get_cursor_line_ptr()); if (cin_isif(look)) { - elselevel--; // NOLINT(readability/braces) + elselevel--; // When looking for an "if" ignore "while"s that // get in the way. if (elselevel == 0 && lookfor == LOOKFOR_IF) { diff --git a/src/nvim/indent_c.h b/src/nvim/indent_c.h index 64ba67a42b..65a402f02c 100644 --- a/src/nvim/indent_c.h +++ b/src/nvim/indent_c.h @@ -1,7 +1,7 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "indent_c.h.generated.h" diff --git a/src/nvim/input.c b/src/nvim/input.c index fb25968071..7667c49452 100644 --- a/src/nvim/input.c +++ b/src/nvim/input.c @@ -7,11 +7,11 @@ #include <string.h> #include "nvim/ascii_defs.h" -#include "nvim/event/multiqueue.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/input.h" #include "nvim/keycodes.h" #include "nvim/mbyte.h" @@ -35,7 +35,7 @@ /// @param[in] str Prompt: question to ask user. Is always followed by /// " (y/n)?". /// @param[in] direct Determines what function to use to get user input. If -/// true then ui_inchar() will be used, otherwise vgetc(). +/// true then os_inchar() will be used, otherwise vgetc(). /// I.e. when direct is true then characters are obtained /// directly from the user without buffers involved. /// @@ -159,7 +159,7 @@ int get_keystroke(MultiQueue *events) /// When "mouse_used" is not NULL allow using the mouse. /// /// @param colon allow colon to abort -int get_number(int colon, int *mouse_used) +int get_number(int colon, bool *mouse_used) { int n = 0; int typed = 0; @@ -220,12 +220,8 @@ int get_number(int colon, int *mouse_used) /// /// When "mouse_used" is not NULL allow using the mouse and in that case return /// the line number. -int prompt_for_number(int *mouse_used) +int prompt_for_number(bool *mouse_used) { - int i; - int save_cmdline_row; - int save_State; - // When using ":silent" assume that <CR> was entered. if (mouse_used != NULL) { msg_puts(_("Type number and <Enter> or click with the mouse " @@ -236,14 +232,14 @@ int prompt_for_number(int *mouse_used) // Set the state such that text can be selected/copied/pasted and we still // get mouse events. - save_cmdline_row = cmdline_row; + int save_cmdline_row = cmdline_row; cmdline_row = 0; - save_State = State; + int save_State = State; State = MODE_ASKMORE; // prevents a screen update when using a timer // May show different mouse shape. setmouse(); - i = get_number(true, mouse_used); + int i = get_number(true, mouse_used); if (KeyTyped) { // don't call wait_return() now if (msg_row > 0) { diff --git a/src/nvim/input.h b/src/nvim/input.h index 3d948fa4ca..8741dafba4 100644 --- a/src/nvim/input.h +++ b/src/nvim/input.h @@ -1,6 +1,6 @@ #pragma once -#include "nvim/event/multiqueue.h" // IWYU pragma: keep +#include "nvim/event/defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "input.h.generated.h" diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 12543a2d42..41b964323e 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -9,34 +9,43 @@ #include <stdlib.h> #include <string.h> +#include "klib/kvec.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" +#include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" -#include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -51,9 +60,11 @@ #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/tag.h" #include "nvim/textformat.h" @@ -62,6 +73,7 @@ #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/winfloat.h" // Definitions used for CTRL-X submode. // Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] @@ -252,6 +264,8 @@ static colnr_T compl_col = 0; ///< column where the text starts ///< that is being completed static char *compl_orig_text = NULL; ///< text as it was before ///< completion started +/// Undo information to restore extmarks for original text. +static extmark_undo_vec_t compl_orig_extmarks; static int compl_cont_mode = 0; static expand_T compl_xp; @@ -593,7 +607,6 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp int min_len, char **tofree) { bool has_lower = false; - bool was_letter = false; // Allocate wide character array for the completion and fill it. int *const wca = xmalloc((size_t)char_len * sizeof(*wca)); @@ -625,6 +638,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp // Rule 2: No lower case, 2nd consecutive letter converted to // upper case. if (!has_lower) { + bool was_letter = false; const char *p = compl_orig_text; for (int i = 0; i < min_len; i++) { const int c = mb_ptr2char_adv(&p); @@ -744,6 +758,16 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir return res; } +/// free cptext +static inline void free_cptext(char *const *const cptext) +{ + if (cptext != NULL) { + for (size_t i = 0; i < CPT_COUNT; i++) { + xfree(cptext[i]); + } + } +} + /// Add a match to the list of matches /// /// @param[in] str text of the match to add @@ -781,16 +805,10 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons } else { os_breakcheck(); } -#define FREE_CPTEXT(cptext, cptext_allocated) \ - do { \ - if ((cptext) != NULL && (cptext_allocated)) { \ - for (size_t i = 0; i < CPT_COUNT; i++) { \ - xfree((cptext)[i]); \ - } \ - } \ - } while (0) if (got_int) { - FREE_CPTEXT(cptext, cptext_allocated); + if (cptext_allocated) { + free_cptext(cptext); + } return FAIL; } if (len < 0) { @@ -804,7 +822,9 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons if (!match_at_original_text(match) && strncmp(match->cp_str, str, (size_t)len) == 0 && ((int)strlen(match->cp_str) <= len || match->cp_str[len] == NUL)) { - FREE_CPTEXT(cptext, cptext_allocated); + if (cptext_allocated) { + free_cptext(cptext); + } return NOTDONE; } match = match->cp_next; @@ -909,13 +929,11 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len) /// Reduce the longest common string for match "match". static void ins_compl_longest_match(compl_T *match) { - int had_match; - if (compl_leader == NULL) { // First match, use it as a whole. compl_leader = xstrdup(match->cp_str); - had_match = (curwin->w_cursor.col > compl_col); + bool had_match = (curwin->w_cursor.col > compl_col); ins_compl_delete(); ins_bytes(compl_leader + get_compl_len()); ins_redraw(false); @@ -949,7 +967,7 @@ static void ins_compl_longest_match(compl_T *match) if (*p != NUL) { // Leader was shortened, need to change the inserted text. *p = NUL; - had_match = (curwin->w_cursor.col > compl_col); + bool had_match = (curwin->w_cursor.col > compl_col); ins_compl_delete(); ins_bytes(compl_leader + get_compl_len()); ins_redraw(false); @@ -1261,6 +1279,9 @@ void ins_compl_show_pum(void) } if (compl_match_array == NULL) { + if (compl_started && has_event(EVENT_COMPLETECHANGED)) { + trigger_complete_changed_event(cur); + } return; } @@ -1276,11 +1297,38 @@ void ins_compl_show_pum(void) pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0); curwin->w_cursor.col = col; + // After adding leader, set the current match to shown match. + if (compl_started && compl_curr_match != compl_shown_match) { + compl_curr_match = compl_shown_match; + } + if (has_event(EVENT_COMPLETECHANGED)) { trigger_complete_changed_event(cur); } } +/// used for set or update info +void compl_set_info(int pum_idx) +{ + compl_T *comp = compl_first_match; + char *pum_text = compl_match_array[pum_idx].pum_text; + + while (comp != NULL) { + if (pum_text == comp->cp_str + || pum_text == comp->cp_text[CPT_ABBR]) { + comp->cp_text[CPT_INFO] = compl_match_array[pum_idx].pum_info; + + // if comp is current match update completed_item value + if (comp == compl_curr_match) { + dict_T *dict = ins_compl_dict_alloc(compl_curr_match); + set_vim_var_dict(VV_COMPLETED_ITEM, dict); + } + break; + } + comp = comp->cp_next; + } +} + #define DICT_FIRST (1) ///< use just first element in "dict" #define DICT_EXACT (2) ///< "dict" is the exact name of a file @@ -1289,7 +1337,7 @@ void ins_compl_show_pum(void) /// /// @param flags DICT_FIRST and/or DICT_EXACT /// @param thesaurus Thesaurus completion -static void ins_compl_dictionaries(char *dict_start, char *pat, int flags, int thesaurus) +static void ins_compl_dictionaries(char *dict_start, char *pat, int flags, bool thesaurus) { char *dict = dict_start; char *ptr; @@ -1432,8 +1480,8 @@ static int thesaurus_add_words_in_line(char *fname, char **buf_arg, int dir, con /// Process "count" dictionary/thesaurus "files" and add the text matching /// "regmatch". -static void ins_compl_files(int count, char **files, int thesaurus, int flags, regmatch_T *regmatch, - char *buf, Direction *dir) +static void ins_compl_files(int count, char **files, bool thesaurus, int flags, + regmatch_T *regmatch, char *buf, Direction *dir) FUNC_ATTR_NONNULL_ARG(2, 7) { for (int i = 0; i < count && !got_int && !compl_interrupted; i++) { @@ -1442,7 +1490,7 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r msg_hist_off = true; // reset in msg_trunc() vim_snprintf(IObuff, IOSIZE, _("Scanning dictionary: %s"), files[i]); - (void)msg_trunc(IObuff, true, HL_ATTR(HLF_R)); + msg_trunc(IObuff, true, HL_ATTR(HLF_R)); } if (fp == NULL) { @@ -1549,9 +1597,7 @@ static void ins_compl_free(void) if (match->cp_flags & CP_FREE_FNAME) { xfree(match->cp_fname); } - for (int i = 0; i < CPT_COUNT; i++) { - xfree(match->cp_text[i]); - } + free_cptext(match->cp_text); tv_clear(&match->cp_user_data); xfree(match); } while (compl_curr_match != NULL && !is_first_match(compl_curr_match)); @@ -1569,6 +1615,7 @@ void ins_compl_clear(void) XFREE_CLEAR(compl_pattern); XFREE_CLEAR(compl_leader); edit_submode_extra = NULL; + kv_destroy(compl_orig_extmarks); XFREE_CLEAR(compl_orig_text); compl_enter_selects = false; // clear v:completed_item @@ -2019,6 +2066,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) ins_bytes_len(p + compl_len, (size_t)(len - compl_len)); } } + restore_orig_extmarks(); retval = true; } @@ -2454,9 +2502,7 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) CLEAR_FIELD(cptext); } if (word == NULL || (!empty && *word == NUL)) { - for (size_t i = 0; i < CPT_COUNT; i++) { - xfree(cptext[i]); - } + free_cptext(cptext); tv_clear(&user_data); return FAIL; } @@ -2505,6 +2551,22 @@ static void ins_compl_add_dict(dict_T *dict) } } +/// Save extmarks in "compl_orig_text" so that they may be restored when the +/// completion is cancelled, or the original text is completed. +static void save_orig_extmarks(void) +{ + extmark_splice_delete(curbuf, curwin->w_cursor.lnum - 1, compl_col, curwin->w_cursor.lnum - 1, + compl_col + compl_length, &compl_orig_extmarks, true, kExtmarkUndo); +} + +static void restore_orig_extmarks(void) +{ + for (long i = (int)kv_size(compl_orig_extmarks) - 1; i > -1; i--) { + ExtmarkUndoObject undo_info = kv_A(compl_orig_extmarks, i); + extmark_apply_undo(undo_info, true); + } +} + /// Start completion for the complete() function. /// /// @param startcol where the matched text starts (1 is first column). @@ -2526,10 +2588,10 @@ static void set_completion(colnr_T startcol, list_T *list) startcol = curwin->w_cursor.col; } compl_col = startcol; - compl_length = (int)curwin->w_cursor.col - (int)startcol; + compl_length = curwin->w_cursor.col - startcol; // compl_pattern doesn't need to be set - compl_orig_text = xstrnsave(get_cursor_line_ptr() + compl_col, - (size_t)compl_length); + compl_orig_text = xstrnsave(get_cursor_line_ptr() + compl_col, (size_t)compl_length); + save_orig_extmarks(); if (p_ic) { flags |= CP_ICASE; } @@ -2706,9 +2768,11 @@ static int info_add_completion_info(list_T *li) // Skip the element with the CP_ORIGINAL_TEXT flag at the beginning, in case of // forward completion, or at the end, in case of backward completion. - match = forward ? match->cp_next - : (compl_no_select && match_at_original_text(match) - ? match->cp_prev : match->cp_prev->cp_prev); + match = (forward || match->cp_prev == NULL + ? match->cp_next + : (compl_no_select && match_at_original_text(match) + ? match->cp_prev + : match->cp_prev->cp_prev)); while (match != NULL && !match_at_original_text(match)) { dict_T *di = tv_dict_alloc(); @@ -2790,6 +2854,11 @@ static void get_complete_info(list_T *what_list, dict_T *retdict) ret = tv_dict_add_nr(retdict, S_LEN("selected"), (compl_curr_match != NULL) ? compl_curr_match->cp_number - 1 : -1); + win_T *wp = win_float_find_preview(); + if (wp != NULL) { + tv_dict_add_nr(retdict, S_LEN("preview_winid"), wp->handle); + tv_dict_add_nr(retdict, S_LEN("preview_bufnr"), wp->w_buffer->handle); + } } (void)ret; @@ -2903,7 +2972,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar : st->ins_buf->b_sfname == NULL ? st->ins_buf->b_fname : st->ins_buf->b_sfname); - (void)msg_trunc(IObuff, true, HL_ATTR(HLF_R)); + msg_trunc(IObuff, true, HL_ATTR(HLF_R)); } } else if (*st->e_cpt == NUL) { status = INS_COMPL_CPT_END; @@ -2931,12 +3000,12 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar if (!shortmess(SHM_COMPLETIONSCAN)) { msg_hist_off = true; // reset in msg_trunc() vim_snprintf(IObuff, IOSIZE, "%s", _("Scanning tags.")); - (void)msg_trunc(IObuff, true, HL_ATTR(HLF_R)); + msg_trunc(IObuff, true, HL_ATTR(HLF_R)); } } // in any case e_cpt is advanced to the next entry - (void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ","); + copy_option_part(&st->e_cpt, IObuff, IOSIZE, ","); st->found_all = true; if (compl_type == -1) { @@ -3488,7 +3557,12 @@ void ins_compl_delete(void) /// "in_compl_func" is true when called from complete_check(). void ins_compl_insert(bool in_compl_func) { - ins_bytes(compl_shown_match->cp_str + get_compl_len()); + int compl_len = get_compl_len(); + // Make sure we don't go over the end of the string, this can happen with + // illegal bytes. + if (compl_len < (int)strlen(compl_shown_match->cp_str)) { + ins_bytes(compl_shown_match->cp_str + compl_len); + } compl_used_match = !match_at_original_text(compl_shown_match); dict_T *dict = ins_compl_dict_alloc(compl_shown_match); @@ -3689,12 +3763,16 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match if (compl_no_insert && !started) { ins_bytes(compl_orig_text + get_compl_len()); compl_used_match = false; + restore_orig_extmarks(); } else if (insert_match) { if (!compl_get_longest || compl_used_match) { ins_compl_insert(in_compl_func); } else { ins_bytes(compl_leader + get_compl_len()); } + if (!strcmp(compl_curr_match->cp_str, compl_orig_text)) { + restore_orig_extmarks(); + } } else { compl_used_match = false; } @@ -3759,8 +3837,8 @@ void ins_compl_check_keys(int frequency, bool in_compl_func) if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R) { c = safe_vgetc(); // Eat the character compl_shows_dir = ins_compl_key2dir(c); - (void)ins_compl_next(false, ins_compl_key2count(c), - c != K_UP && c != K_DOWN, in_compl_func); + ins_compl_next(false, ins_compl_key2count(c), + c != K_UP && c != K_DOWN, in_compl_func); } else { // Need to get the character to have KeyTyped set. We'll put it // back with vungetc() below. But skip K_IGNORE. @@ -3780,7 +3858,7 @@ void ins_compl_check_keys(int frequency, bool in_compl_func) int todo = compl_pending > 0 ? compl_pending : -compl_pending; compl_pending = 0; - (void)ins_compl_next(false, todo, true, in_compl_func); + ins_compl_next(false, todo, true, in_compl_func); } } @@ -3882,8 +3960,8 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) prefix = ""; } STRCPY(compl_pattern, prefix); - (void)quote_meta(compl_pattern + strlen(prefix), - line + compl_col, compl_length); + quote_meta(compl_pattern + strlen(prefix), + line + compl_col, compl_length); } else if (--startcol < 0 || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) { // Match any word of at least two chars @@ -3910,12 +3988,12 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) // xmalloc(7) is enough -- Acevedo compl_pattern = xmalloc(7); STRCPY(compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, 1); + quote_meta(compl_pattern + 2, line + compl_col, 1); STRCAT(compl_pattern, "\\k"); } else { compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2); STRCPY(compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, compl_length); + quote_meta(compl_pattern + 2, line + compl_col, compl_length); } } @@ -4267,7 +4345,9 @@ static int ins_compl_start(void) // Always add completion for the original text. xfree(compl_orig_text); + kv_destroy(compl_orig_extmarks); compl_orig_text = xstrnsave(line + compl_col, (size_t)compl_length); + save_orig_extmarks(); int flags = CP_ORIGINAL_TEXT; if (p_ic) { flags |= CP_ICASE; @@ -4276,6 +4356,7 @@ static int ins_compl_start(void) flags, false) != OK) { XFREE_CLEAR(compl_pattern); XFREE_CLEAR(compl_orig_text); + kv_destroy(compl_orig_extmarks); return FAIL; } @@ -4391,7 +4472,7 @@ int ins_complete(int c, bool enable_pum) // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert // mode. if (got_int && !global_busy) { - (void)vgetc(); + vgetc(); got_int = false; } @@ -4508,6 +4589,7 @@ static unsigned quote_meta(char *dest, char *src, int len) void free_insexpand_stuff(void) { XFREE_CLEAR(compl_orig_text); + kv_destroy(compl_orig_extmarks); callback_free(&cfu_cb); callback_free(&ofu_cb); callback_free(&tsrfu_cb); diff --git a/src/nvim/insexpand.h b/src/nvim/insexpand.h index 121d5568ff..b880e64ea4 100644 --- a/src/nvim/insexpand.h +++ b/src/nvim/insexpand.h @@ -1,10 +1,9 @@ #pragma once -#include "nvim/macros_defs.h" #include "nvim/option_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep -#include "nvim/vim_defs.h" +#include "nvim/vim_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "insexpand.h.generated.h" diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c index 745500fe39..44ddfbba00 100644 --- a/src/nvim/keycodes.c +++ b/src/nvim/keycodes.c @@ -4,21 +4,22 @@ #include <stdbool.h> #include <stdio.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/ascii_defs.h" #include "nvim/charset.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" +#include "nvim/option_vars.h" #include "nvim/strings.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -399,7 +400,7 @@ int name_to_mod_mask(int c) int simplify_key(const int key, int *modifiers) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - if (!(*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT))) { + if (!(*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) { return key; } @@ -757,17 +758,20 @@ static int extract_modifiers(int key, int *modp, const bool simplify, bool *cons { int modifiers = *modp; - // Command-key and ctrl are special - if (!(modifiers & MOD_MASK_CMD) && !(modifiers & MOD_MASK_CTRL)) { - if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { - key = TOUPPER_ASC(key); + if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { + key = TOUPPER_ASC(key); + // With <C-S-a> we keep the shift modifier. + // With <S-a>, <A-S-a> and <S-A> we don't keep the shift modifier. + if (!(modifiers & MOD_MASK_CTRL)) { modifiers &= ~MOD_MASK_SHIFT; } } + // <C-H> and <C-h> mean the same thing, always use "H" if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key)) { key = TOUPPER_ASC(key); } + if (simplify && (modifiers & MOD_MASK_CTRL) && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { key = CTRL_CHR(key); @@ -853,8 +857,8 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) /// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER. /// /// When "flags" has REPTERM_FROM_PART, trailing <C-v> is included, otherwise it is removed (to make -/// ":map xx ^V" map xx to nothing). When cpo_flags contains FLAG_CPO_BSLASH, a backslash can be -/// used in place of <C-v>. All other <C-v> characters are removed. +/// ":map xx ^V" map xx to nothing). When cpo_val contains CPO_BSLASH, a backslash can be used in +/// place of <C-v>. All other <C-v> characters are removed. /// /// @param[in] from What characters to replace. /// @param[in] from_len Length of the "from" argument. @@ -868,20 +872,19 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) /// REPTERM_NO_SPECIAL do not accept <key> notation /// REPTERM_NO_SIMPLIFY do not simplify <C-H> into 0x08, etc. /// @param[out] did_simplify set when some <C-H> code was simplified, unless it is NULL. -/// @param[in] cpo_flags Relevant flags derived from p_cpo, see CPO_TO_CPO_FLAGS. +/// @param[in] cpo_val The value of 'cpoptions' to use. Only CPO_BSLASH matters. /// /// @return The same as what `*bufp` is set to. char *replace_termcodes(const char *const from, const size_t from_len, char **const bufp, const scid_T sid_arg, const int flags, bool *const did_simplify, - const int cpo_flags) - FUNC_ATTR_NONNULL_ARG(1, 3) + const char *const cpo_val) + FUNC_ATTR_NONNULL_ARG(1, 3, 7) { - char key; size_t dlen = 0; - const char *src; const char *const end = from + from_len - 1; - const bool do_backslash = !(cpo_flags & FLAG_CPO_BSLASH); // backslash is a special character + // backslash is a special character + const bool do_backslash = (vim_strchr(cpo_val, CPO_BSLASH) == NULL); const bool do_special = !(flags & REPTERM_NO_SPECIAL); bool allocated = (*bufp == NULL); @@ -891,7 +894,7 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co const size_t buf_len = allocated ? from_len * 6 + 1 : 128; char *result = allocated ? xmalloc(buf_len) : *bufp; // buffer for resulting string - src = from; + const char *src = from; // Copy each byte from *from to result[dlen] while (src <= end) { @@ -966,7 +969,7 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co // For "from" side the CTRL-V at the end is included, for the "to" // part it is removed. // If 'cpoptions' does not contain 'B', also accept a backslash. - key = *src; + char key = *src; if (key == Ctrl_V || (do_backslash && key == '\\')) { src++; // skip CTRL-V or backslash if (src > end) { @@ -1059,7 +1062,8 @@ char *vim_strsave_escape_ks(char *p) /// vim_strsave_escape_ks(). Works in-place. void vim_unescape_ks(char *p) { - uint8_t *s = (uint8_t *)p, *d = (uint8_t *)p; + uint8_t *s = (uint8_t *)p; + uint8_t *d = (uint8_t *)p; while (*s != NUL) { if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) { diff --git a/src/nvim/keycodes.h b/src/nvim/keycodes.h index db9ef38cc3..18af3f87d6 100644 --- a/src/nvim/keycodes.h +++ b/src/nvim/keycodes.h @@ -1,11 +1,7 @@ #pragma once -#include <stddef.h> - #include "nvim/ascii_defs.h" -#include "nvim/option_defs.h" -#include "nvim/option_vars.h" -#include "nvim/strings.h" +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep // Keycode definitions for special keys. // @@ -476,11 +472,6 @@ enum key_extra { /// This is a total of 6 tokens, and is currently the longest one possible. #define MAX_KEY_CODE_LEN 6 -#define FLAG_CPO_BSLASH 0x01 -#define CPO_TO_CPO_FLAGS ((vim_strchr((char *)p_cpo, CPO_BSLASH) == NULL) \ - ? 0 \ - : FLAG_CPO_BSLASH) - /// Flags for replace_termcodes() enum { REPTERM_FROM_PART = 1, diff --git a/src/nvim/lib/queue.h b/src/nvim/lib/queue_defs.h index 40769e44b5..1f113a057a 100644 --- a/src/nvim/lib/queue.h +++ b/src/nvim/lib/queue_defs.h @@ -23,9 +23,9 @@ #include "nvim/func_attr.h" -typedef struct _queue { - struct _queue *next; - struct _queue *prev; +typedef struct queue { + struct queue *next; + struct queue *prev; } QUEUE; // Public macros. @@ -44,22 +44,29 @@ typedef struct _queue { } // ffi.cdef is unable to swallow `bool` in place of `int` here. +static inline int QUEUE_EMPTY(const QUEUE *q) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + static inline int QUEUE_EMPTY(const QUEUE *const q) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return q == q->next; } #define QUEUE_HEAD(q) (q)->next -static inline void QUEUE_INIT(QUEUE *const q) FUNC_ATTR_ALWAYS_INLINE +static inline void QUEUE_INIT(QUEUE *q) + REAL_FATTR_ALWAYS_INLINE; + +static inline void QUEUE_INIT(QUEUE *const q) { q->next = q; q->prev = q; } +static inline void QUEUE_ADD(QUEUE *h, QUEUE *n) + REAL_FATTR_ALWAYS_INLINE; + static inline void QUEUE_ADD(QUEUE *const h, QUEUE *const n) - FUNC_ATTR_ALWAYS_INLINE { h->prev->next = n->next; n->next->prev = h->prev; @@ -67,8 +74,10 @@ static inline void QUEUE_ADD(QUEUE *const h, QUEUE *const n) h->prev->next = h; } +static inline void QUEUE_INSERT_HEAD(QUEUE *h, QUEUE *q) + REAL_FATTR_ALWAYS_INLINE; + static inline void QUEUE_INSERT_HEAD(QUEUE *const h, QUEUE *const q) - FUNC_ATTR_ALWAYS_INLINE { q->next = h->next; q->prev = h; @@ -76,8 +85,10 @@ static inline void QUEUE_INSERT_HEAD(QUEUE *const h, QUEUE *const q) h->next = q; } +static inline void QUEUE_INSERT_TAIL(QUEUE *h, QUEUE *q) + REAL_FATTR_ALWAYS_INLINE; + static inline void QUEUE_INSERT_TAIL(QUEUE *const h, QUEUE *const q) - FUNC_ATTR_ALWAYS_INLINE { q->next = h; q->prev = h->prev; @@ -85,7 +96,10 @@ static inline void QUEUE_INSERT_TAIL(QUEUE *const h, QUEUE *const q) h->prev = q; } -static inline void QUEUE_REMOVE(QUEUE *const q) FUNC_ATTR_ALWAYS_INLINE +static inline void QUEUE_REMOVE(QUEUE *q) + REAL_FATTR_ALWAYS_INLINE; + +static inline void QUEUE_REMOVE(QUEUE *const q) { q->prev->next = q->next; q->next->prev = q->prev; diff --git a/src/nvim/lib/ringbuf.h b/src/nvim/lib/ringbuf.h deleted file mode 100644 index c8abccfeb4..0000000000 --- a/src/nvim/lib/ringbuf.h +++ /dev/null @@ -1,290 +0,0 @@ -/// Macros-based ring buffer implementation. -/// -/// Supported functions: -/// -/// - new: allocates new ring buffer. -/// - dealloc: free ring buffer itself. -/// - free: free ring buffer and all its elements. -/// - push: adds element to the end of the buffer. -/// - length: get buffer length. -/// - size: size of the ring buffer. -/// - idx: get element at given index. -/// - idx_p: get pointer to the element at given index. -/// - insert: insert element at given position. -/// - remove: remove element from given position. - -#pragma once - -#include <assert.h> -#include <stddef.h> -#include <stdint.h> -#include <string.h> - -#include "nvim/func_attr.h" -#include "nvim/memory.h" - -#define _RINGBUF_LENGTH(rb) \ - ((rb)->first == NULL ? 0 \ - : ((rb)->next == (rb)->first) ? (size_t)((rb)->buf_end - (rb)->buf) + 1 \ - : ((rb)->next > (rb)->first) ? (size_t)((rb)->next - (rb)->first) \ - : (size_t)((rb)->next - (rb)->buf + (rb)->buf_end - (rb)->first + 1)) - -#define _RINGBUF_NEXT(rb, var) \ - ((var) == (rb)->buf_end ? (rb)->buf : (var) + 1) -#define _RINGBUF_PREV(rb, var) \ - ((var) == (rb)->buf ? (rb)->buf_end : (var) - 1) - -/// Iterate over all ringbuf values -/// -/// @param rb Ring buffer to iterate over. -/// @param RBType Type of the ring buffer element. -/// @param varname Variable name. -#define RINGBUF_FORALL(rb, RBType, varname) \ - size_t varname##_length_fa_ = _RINGBUF_LENGTH(rb); \ - for (RBType *varname = ((rb)->first == NULL ? (rb)->next : (rb)->first); \ - varname##_length_fa_; \ - (varname = _RINGBUF_NEXT(rb, varname)), \ - varname##_length_fa_--) - -/// Iterate over all ringbuf values, from end to the beginning -/// -/// Unlike previous RINGBUF_FORALL uses already defined variable, in place of -/// defining variable in the cycle body. -/// -/// @param rb Ring buffer to iterate over. -/// @param RBType Type of the ring buffer element. -/// @param varname Variable name. -#define RINGBUF_ITER_BACK(rb, RBType, varname) \ - size_t varname##_length_ib_ = _RINGBUF_LENGTH(rb); \ - for (varname = ((rb)->next == (rb)->buf ? (rb)->buf_end : (rb)->next - 1); \ - varname##_length_ib_; \ - (varname = _RINGBUF_PREV(rb, varname)), \ - varname##_length_ib_--) - -/// Define a ring buffer structure -/// -/// @param TypeName Ring buffer type name. Actual type name will be -/// `{TypeName}RingBuffer`. -/// @param RBType Type of the single ring buffer element. -#define RINGBUF_TYPEDEF(TypeName, RBType) \ - typedef struct { \ - RBType *buf; \ - RBType *next; \ - RBType *first; \ - RBType *buf_end; \ - } TypeName##RingBuffer; - -/// Dummy item free macros, for use in RINGBUF_INIT -/// -/// This macros actually does nothing. -/// -/// @param[in] item Item to be freed. -#define RINGBUF_DUMMY_FREE(item) - -/// Static ring buffer -/// -/// @warning Ring buffers created with this macros must neither be freed nor -/// deallocated. -/// -/// @param scope Ring buffer scope. -/// @param TypeName Ring buffer type name. -/// @param RBType Type of the single ring buffer element. -/// @param varname Variable name. -/// @param rbsize Ring buffer size. -#define RINGBUF_STATIC(scope, TypeName, RBType, varname, rbsize) \ - static RBType _##varname##_buf[rbsize]; \ - scope TypeName##RingBuffer varname = { \ - .buf = _##varname##_buf, \ - .next = _##varname##_buf, \ - .first = NULL, \ - .buf_end = _##varname##_buf + rbsize - 1, \ - }; - -/// Initialize a new ring buffer -/// -/// @param TypeName Ring buffer type name. Actual type name will be -/// `{TypeName}RingBuffer`. -/// @param funcprefix Prefix for all ring buffer functions. Function name will -/// look like `{funcprefix}_rb_{function_name}`. -/// @param RBType Type of the single ring buffer element. -/// @param rbfree Function used to free ring buffer element. May be -/// a macros like `#define RBFREE(item)` (to skip freeing). -/// -/// Intended function signature: `void *rbfree(RBType *)`; -#define RINGBUF_INIT(TypeName, funcprefix, RBType, rbfree) \ - static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \ - REAL_FATTR_WARN_UNUSED_RESULT; \ - static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \ - { \ - assert(size != 0); \ - RBType *buf = xmalloc(size * sizeof(RBType)); \ - return (TypeName##RingBuffer) { \ - .buf = buf, \ - .next = buf, \ - .first = NULL, \ - .buf_end = buf + size - 1, \ - }; \ - } \ - static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \ - REAL_FATTR_UNUSED; \ - static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \ - { \ - if (rb == NULL) { \ - return; \ - } \ - RINGBUF_FORALL(rb, RBType, rbitem) { \ - rbfree(rbitem); \ - } \ - XFREE_CLEAR(rb->buf); \ - } \ - static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \ - REAL_FATTR_UNUSED; \ - static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \ - { \ - XFREE_CLEAR(rb->buf); \ - } \ - static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \ - RBType item) \ - REAL_FATTR_NONNULL_ARG(1); \ - static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \ - RBType item) \ - { \ - if (rb->next == rb->first) { \ - rbfree(rb->first); \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - } else if (rb->first == NULL) { \ - rb->first = rb->next; \ - } \ - *rb->next = item; \ - rb->next = _RINGBUF_NEXT(rb, rb->next); \ - } \ - static inline ptrdiff_t funcprefix##_rb_find_idx(const TypeName##RingBuffer *const rb, \ - const RBType *const item_p) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \ - static inline ptrdiff_t funcprefix##_rb_find_idx(const TypeName##RingBuffer *const rb, \ - const RBType *const item_p) \ - { \ - assert(rb->buf <= item_p); \ - assert(rb->buf_end >= item_p); \ - if (rb->first == NULL) { \ - return -1; \ - } else if (item_p >= rb->first) { \ - return item_p - rb->first; \ - } else { \ - return item_p - rb->buf + rb->buf_end - rb->first + 1; \ - } \ - } \ - static inline size_t funcprefix##_rb_size(const TypeName##RingBuffer *const rb) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \ - static inline size_t funcprefix##_rb_size(const TypeName##RingBuffer *const rb) \ - { \ - return (size_t)(rb->buf_end - rb->buf) + 1; \ - } \ - static inline size_t funcprefix##_rb_length(const TypeName##RingBuffer *const rb) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \ - static inline size_t funcprefix##_rb_length(const TypeName##RingBuffer *const rb) \ - { \ - return _RINGBUF_LENGTH(rb); \ - } \ - static inline RBType *funcprefix##_rb_idx_p(const TypeName##RingBuffer *const rb, \ - const size_t idx) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \ - static inline RBType *funcprefix##_rb_idx_p(const TypeName##RingBuffer *const rb, \ - const size_t idx) \ - { \ - assert(idx <= funcprefix##_rb_size(rb)); \ - assert(idx <= funcprefix##_rb_length(rb)); \ - if (rb->first + idx > rb->buf_end) { \ - return rb->buf + ((rb->first + idx) - (rb->buf_end + 1)); \ - } else { \ - return rb->first + idx; \ - } \ - } \ - static inline RBType funcprefix##_rb_idx(const TypeName##RingBuffer *const rb, \ - const size_t idx) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \ - static inline RBType funcprefix##_rb_idx(const TypeName##RingBuffer *const rb, \ - const size_t idx) \ - { \ - return *funcprefix##_rb_idx_p(rb, idx); \ - } \ - static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \ - const size_t idx, \ - RBType item) \ - REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \ - static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \ - const size_t idx, \ - RBType item) \ - { \ - assert(idx <= funcprefix##_rb_size(rb)); \ - assert(idx <= funcprefix##_rb_length(rb)); \ - const size_t length = funcprefix##_rb_length(rb); \ - if (idx == length) { \ - funcprefix##_rb_push(rb, item); \ - return; \ - } \ - RBType *const insertpos = funcprefix##_rb_idx_p(rb, idx); \ - if (insertpos == rb->next) { \ - funcprefix##_rb_push(rb, item); \ - return; \ - } \ - if (length == funcprefix##_rb_size(rb)) { \ - rbfree(rb->first); \ - } \ - if (insertpos < rb->next) { \ - memmove(insertpos + 1, insertpos, \ - (size_t)((uintptr_t)rb->next - (uintptr_t)insertpos)); \ - } else { \ - assert(insertpos > rb->first); \ - assert(rb->next <= rb->first); \ - memmove(rb->buf + 1, rb->buf, \ - (size_t)((uintptr_t)rb->next - (uintptr_t)rb->buf)); \ - *rb->buf = *rb->buf_end; \ - memmove(insertpos + 1, insertpos, \ - (size_t)((uintptr_t)(rb->buf_end + 1) - (uintptr_t)insertpos)); \ - } \ - *insertpos = item; \ - if (length == funcprefix##_rb_size(rb)) { \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - } \ - rb->next = _RINGBUF_NEXT(rb, rb->next); \ - } \ - static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \ - const size_t idx) \ - REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \ - static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \ - const size_t idx) \ - { \ - assert(idx < funcprefix##_rb_size(rb)); \ - assert(idx < funcprefix##_rb_length(rb)); \ - RBType *const rmpos = funcprefix##_rb_idx_p(rb, idx); \ - rbfree(rmpos); \ - if (rmpos == rb->next - 1) { \ - rb->next--; \ - if (rb->first == rb->next) { \ - rb->first = NULL; \ - rb->next = rb->buf; \ - } \ - } else if (rmpos == rb->first) { \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - if (rb->first == rb->next) { \ - rb->first = NULL; \ - rb->next = rb->buf; \ - } \ - } else if (rb->first < rb->next || rb->next == rb->buf) { \ - assert(rmpos > rb->first); \ - assert(rmpos <= _RINGBUF_PREV(rb, rb->next)); \ - memmove(rb->first + 1, rb->first, \ - (size_t)((uintptr_t)rmpos - (uintptr_t)rb->first)); \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - } else if (rmpos < rb->next) { \ - memmove(rmpos, rmpos + 1, \ - (size_t)((uintptr_t)rb->next - (uintptr_t)rmpos)); \ - rb->next = _RINGBUF_PREV(rb, rb->next); \ - } else { \ - assert(rb->first < rb->buf_end); \ - memmove(rb->first + 1, rb->first, \ - (size_t)((uintptr_t)rmpos - (uintptr_t)rb->first)); \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - } \ - } diff --git a/src/nvim/log.c b/src/nvim/log.c index aeee088cd3..fbb3e0385a 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -19,13 +19,13 @@ #include "auto/config.h" #include "nvim/ascii_defs.h" #include "nvim/eval.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/os/fs.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/stdpaths_defs.h" #include "nvim/os/time.h" #include "nvim/path.h" @@ -296,7 +296,7 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context, FUNC_ATTR_PRINTF(7, 0) { // Name of the Nvim instance that produced the log. - static char name[16] = { 0 }; + static char name[32] = { 0 }; static const char *log_levels[] = { [LOGLVL_DBG] = "DBG", diff --git a/src/nvim/log.h b/src/nvim/log.h index cac074d146..1fb15e3503 100644 --- a/src/nvim/log.h +++ b/src/nvim/log.h @@ -9,13 +9,19 @@ // USDT probes. Example invocation: // NVIM_PROBE(nvim_foo_bar, 1, string.data); #if defined(HAVE_SYS_SDT_H) -# include <sys/sdt.h> // NOLINT +# include <sys/sdt.h> // IWYU pragma: keep # define NVIM_PROBE(name, n, ...) STAP_PROBE##n(neovim, name, __VA_ARGS__) #else # define NVIM_PROBE(name, n, ...) #endif +// uncrustify:off +#if NVIM_HAS_INCLUDE(<sanitizer/asan_interface.h>) +# include <sanitizer/asan_interface.h> // IWYU pragma: keep +#endif +// uncrustify:on + #define LOGLVL_DBG 1 #define LOGLVL_INF 2 #define LOGLVL_WRN 3 @@ -45,10 +51,6 @@ # define LOG_CALLSTACK_TO_FILE(fp) log_callstack_to_file(fp, __func__, __LINE__) #endif -#if NVIM_HAS_INCLUDE("sanitizer/asan_interface.h") -# include "sanitizer/asan_interface.h" -#endif - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "log.h.generated.h" #endif diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 4598d48c4a..bba771f8a5 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -16,8 +16,8 @@ #include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_encode.h" #include "nvim/eval/userfunc.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" +#include "nvim/highlight_group.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" @@ -149,7 +149,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) } } else { if (tsize == 0 - || (tsize == ret.maxidx + || (tsize <= ret.maxidx && other_keys_num == 0 && ret.string_keys_num == 0)) { ret.type = kObjectTypeArray; @@ -171,11 +171,12 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) /// Helper structure for nlua_pop_typval typedef struct { - typval_T *tv; ///< Location where conversion result is saved. - bool container; ///< True if tv is a container. - bool special; ///< If true then tv is a _VAL part of special dictionary - ///< that represents mapping. - int idx; ///< Container index (used to detect self-referencing structures). + typval_T *tv; ///< Location where conversion result is saved. + size_t list_len; ///< Maximum length when tv is a list. + bool container; ///< True if tv is a container. + bool special; ///< If true then tv is a _VAL part of special dictionary + ///< that represents mapping. + int idx; ///< Container index (used to detect self-referencing structures). } TVPopStackItem; /// Convert lua object to Vimscript typval_T @@ -193,7 +194,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) const int initial_size = lua_gettop(lstate); kvec_withinit_t(TVPopStackItem, 2) stack = KV_INITIAL_VALUE; kvi_init(stack); - kvi_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); + kvi_push(stack, ((TVPopStackItem){ .tv = ret_tv })); while (ret && kv_size(stack)) { if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); @@ -232,19 +233,14 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) }); kvi_push(stack, cur); tv_list_append_list(cur.tv->vval.v_list, kv_pair); - cur = (TVPopStackItem) { - .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)), - .container = false, - .special = false, - .idx = 0, - }; + cur = (TVPopStackItem){ .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)) }; } else { dictitem_T *const di = tv_dict_item_alloc_len(s, len); if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { abort(); } kvi_push(stack, cur); - cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; + cur = (TVPopStackItem){ .tv = &di->di_tv }; } } else { lua_pop(lstate, 1); @@ -252,23 +248,18 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } } else { assert(cur.tv->v_type == VAR_LIST); - lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1); - if (lua_isnil(lstate, -1)) { - lua_pop(lstate, 2); + if ((size_t)tv_list_len(cur.tv->vval.v_list) == cur.list_len) { + lua_pop(lstate, 1); continue; } + lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1); // Not populated yet, need to create list item to push. tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) { .v_type = VAR_UNKNOWN, }); kvi_push(stack, cur); // TODO(ZyX-I): Use indexes, here list item *will* be reallocated. - cur = (TVPopStackItem) { - .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)), - .container = false, - .special = false, - .idx = 0, - }; + cur = (TVPopStackItem){ .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)) }; } } assert(!cur.container); @@ -332,6 +323,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx); cur.tv->vval.v_list->lua_table_ref = table_ref; tv_list_ref(cur.tv->vval.v_list); + cur.list_len = table_props.maxidx; if (table_props.maxidx != 0) { cur.container = true; cur.idx = lua_gettop(lstate); @@ -355,6 +347,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) cur.tv = &val_di->di_tv; cur.tv->vval.v_list->lua_table_ref = table_ref; assert(cur.tv->v_type == VAR_LIST); + cur.list_len = table_props.string_keys_num; } else { cur.tv->v_type = VAR_DICT; cur.tv->vval.v_dict = tv_dict_alloc(); @@ -372,9 +365,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) cur.tv->vval.v_float = (float_T)table_props.val; break; case kObjectTypeNil: - emsg(_("E5100: Cannot convert given lua table: table " - "should either have a sequence of positive integer keys " - "or contain only string keys")); + emsg(_("E5100: Cannot convert given lua table: table should " + "contain either only integer keys or only string keys")); ret = false; break; default: @@ -727,7 +719,7 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special } for (size_t i = 0; i < dict.size; i++) { nlua_push_String(lstate, dict.items[i].key, special); - nlua_push_Object(lstate, dict.items[i].value, special); + nlua_push_Object(lstate, &dict.items[i].value, special); lua_rawset(lstate, -3); } } @@ -740,7 +732,7 @@ void nlua_push_Array(lua_State *lstate, const Array array, bool special) { lua_createtable(lstate, (int)array.size, 0); for (size_t i = 0; i < array.size; i++) { - nlua_push_Object(lstate, array.items[i], special); + nlua_push_Object(lstate, &array.items[i], special); lua_rawseti(lstate, -2, (int)i + 1); } } @@ -761,10 +753,10 @@ GENERATE_INDEX_FUNCTION(Tabpage) /// Convert given Object to lua value /// /// Leaves converted value on top of the stack. -void nlua_push_Object(lua_State *lstate, const Object obj, bool special) +void nlua_push_Object(lua_State *lstate, Object *obj, bool special) FUNC_ATTR_NONNULL_ALL { - switch (obj.type) { + switch (obj->type) { case kObjectTypeNil: if (special) { lua_pushnil(lstate); @@ -773,12 +765,14 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) } break; case kObjectTypeLuaRef: { - nlua_pushref(lstate, obj.data.luaref); + nlua_pushref(lstate, obj->data.luaref); + api_free_luaref(obj->data.luaref); + obj->data.luaref = LUA_NOREF; break; } #define ADD_TYPE(type, data_key) \ case kObjectType##type: { \ - nlua_push_##type(lstate, obj.data.data_key, special); \ + nlua_push_##type(lstate, obj->data.data_key, special); \ break; \ } ADD_TYPE(Boolean, boolean) @@ -790,7 +784,7 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) #undef ADD_TYPE #define ADD_REMOTE_TYPE(type) \ case kObjectType##type: { \ - nlua_push_##type(lstate, (type)obj.data.integer, special); \ + nlua_push_##type(lstate, (type)obj->data.integer, special); \ break; \ } ADD_REMOTE_TYPE(Buffer) @@ -803,8 +797,8 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) /// Convert lua value to string /// /// Always pops one value from the stack. -String nlua_pop_String(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +String nlua_pop_String(lua_State *lstate, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT { if (lua_type(lstate, -1) != LUA_TSTRING) { lua_pop(lstate, 1); @@ -815,7 +809,10 @@ String nlua_pop_String(lua_State *lstate, Error *err) ret.data = (char *)lua_tolstring(lstate, -1, &(ret.size)); assert(ret.data != NULL); - ret.data = xmemdupz(ret.data, ret.size); + // TODO(bfredl): it would be "nice" to just use the memory of the lua string + // directly, although ensuring the lifetime of such strings is a bit tricky + // (an API call could invoke nested lua, which triggers GC, and kaboom?) + ret.data = arena_memdupz(arena, ret.data, ret.size); lua_pop(lstate, 1); return ret; @@ -824,8 +821,8 @@ String nlua_pop_String(lua_State *lstate, Error *err) /// Convert lua value to integer /// /// Always pops one value from the stack. -Integer nlua_pop_Integer(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +Integer nlua_pop_Integer(lua_State *lstate, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT { if (lua_type(lstate, -1) != LUA_TNUMBER) { lua_pop(lstate, 1); @@ -848,8 +845,8 @@ Integer nlua_pop_Integer(lua_State *lstate, Error *err) /// thus `err` is never set as any lua value can be co-erced into a lua bool /// /// Always pops one value from the stack. -Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +Boolean nlua_pop_Boolean(lua_State *lstate, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT { const Boolean ret = lua_toboolean(lstate, -1); lua_pop(lstate, 1); @@ -923,7 +920,7 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *cons /// Convert lua table to float /// /// Always pops one value from the stack. -Float nlua_pop_Float(lua_State *lstate, Error *err) +Float nlua_pop_Float(lua_State *lstate, Arena *arena, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (lua_type(lstate, -1) == LUA_TNUMBER) { @@ -947,29 +944,29 @@ Float nlua_pop_Float(lua_State *lstate, Error *err) /// @param[in] table_props nlua_traverse_table() output. /// @param[out] err Location where error will be saved. static Array nlua_pop_Array_unchecked(lua_State *const lstate, const LuaTableProps table_props, - Error *const err) + Arena *arena, Error *const err) { - Array ret = { .size = table_props.maxidx, .items = NULL }; + Array ret = arena_array(arena, table_props.maxidx); - if (ret.size == 0) { + if (table_props.maxidx == 0) { lua_pop(lstate, 1); return ret; } - ret.items = xcalloc(ret.size, sizeof(*ret.items)); - for (size_t i = 1; i <= ret.size; i++) { + for (size_t i = 1; i <= table_props.maxidx; i++) { Object val; lua_rawgeti(lstate, -1, (int)i); - val = nlua_pop_Object(lstate, false, err); + val = nlua_pop_Object(lstate, false, arena, err); if (ERROR_SET(err)) { - ret.size = i - 1; lua_pop(lstate, 1); - api_free_array(ret); + if (!arena) { + api_free_array(ret); + } return (Array) { .size = 0, .items = NULL }; } - ret.items[i - 1] = val; + ADD_C(ret, val); } lua_pop(lstate, 1); @@ -979,15 +976,14 @@ static Array nlua_pop_Array_unchecked(lua_State *const lstate, const LuaTablePro /// Convert lua table to array /// /// Always pops one value from the stack. -Array nlua_pop_Array(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +Array nlua_pop_Array(lua_State *lstate, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT { - const LuaTableProps table_props = nlua_check_type(lstate, err, - kObjectTypeArray); + const LuaTableProps table_props = nlua_check_type(lstate, err, kObjectTypeArray); if (table_props.type != kObjectTypeArray) { return (Array) { .size = 0, .items = NULL }; } - return nlua_pop_Array_unchecked(lstate, table_props, err); + return nlua_pop_Array_unchecked(lstate, table_props, arena, err); } /// Convert lua table to dictionary @@ -999,30 +995,30 @@ Array nlua_pop_Array(lua_State *lstate, Error *err) /// @param[in] table_props nlua_traverse_table() output. /// @param[out] err Location where error will be saved. static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, const LuaTableProps table_props, - bool ref, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + bool ref, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 5) FUNC_ATTR_WARN_UNUSED_RESULT { - Dictionary ret = { .size = table_props.string_keys_num, .items = NULL }; + Dictionary ret = arena_dict(arena, table_props.string_keys_num); - if (ret.size == 0) { + if (table_props.string_keys_num == 0) { lua_pop(lstate, 1); return ret; } - ret.items = xcalloc(ret.size, sizeof(*ret.items)); lua_pushnil(lstate); - for (size_t i = 0; lua_next(lstate, -2) && i < ret.size;) { + for (size_t i = 0; lua_next(lstate, -2) && i < table_props.string_keys_num;) { // stack: dict, key, value if (lua_type(lstate, -2) == LUA_TSTRING) { lua_pushvalue(lstate, -2); // stack: dict, key, value, key - ret.items[i].key = nlua_pop_String(lstate, err); + String key = nlua_pop_String(lstate, arena, err); // stack: dict, key, value if (!ERROR_SET(err)) { - ret.items[i].value = nlua_pop_Object(lstate, ref, err); + Object value = nlua_pop_Object(lstate, ref, arena, err); + kv_push_c(ret, ((KeyValuePair) { .key = key, .value = value })); // stack: dict, key } else { lua_pop(lstate, 1); @@ -1030,8 +1026,9 @@ static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, const LuaTabl } if (ERROR_SET(err)) { - ret.size = i; - api_free_dictionary(ret); + if (!arena) { + api_free_dictionary(ret); + } lua_pop(lstate, 2); // stack: return (Dictionary) { .size = 0, .items = NULL }; @@ -1050,8 +1047,8 @@ static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, const LuaTabl /// Convert lua table to dictionary /// /// Always pops one value from the stack. -Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 4) FUNC_ATTR_WARN_UNUSED_RESULT { const LuaTableProps table_props = nlua_check_type(lstate, err, kObjectTypeDictionary); @@ -1060,7 +1057,7 @@ Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Error *err) return (Dictionary) { .size = 0, .items = NULL }; } - return nlua_pop_Dictionary_unchecked(lstate, table_props, ref, err); + return nlua_pop_Dictionary_unchecked(lstate, table_props, ref, arena, err); } /// Helper structure for nlua_pop_Object @@ -1072,13 +1069,14 @@ typedef struct { /// Convert lua table to object /// /// Always pops one value from the stack. -Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) +Object nlua_pop_Object(lua_State *const lstate, bool ref, Arena *arena, Error *const err) + FUNC_ATTR_NONNULL_ARG(1, 4) FUNC_ATTR_WARN_UNUSED_RESULT { Object ret = NIL; const int initial_size = lua_gettop(lstate); kvec_withinit_t(ObjPopStackItem, 2) stack = KV_INITIAL_VALUE; kvi_init(stack); - kvi_push(stack, ((ObjPopStackItem) { &ret, false })); + kvi_push(stack, ((ObjPopStackItem){ .obj = &ret })); while (!ERROR_SET(err) && kv_size(stack)) { ObjPopStackItem cur = kv_pop(stack); if (cur.container) { @@ -1088,8 +1086,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) } if (cur.obj->type == kObjectTypeDictionary) { // stack: …, dict, key - if (cur.obj->data.dictionary.size - == cur.obj->data.dictionary.capacity) { + if (cur.obj->data.dictionary.size == cur.obj->data.dictionary.capacity) { lua_pop(lstate, 2); continue; } @@ -1108,15 +1105,9 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) size_t len; const char *s = lua_tolstring(lstate, -2, &len); const size_t idx = cur.obj->data.dictionary.size++; - cur.obj->data.dictionary.items[idx].key = (String) { - .data = xmemdupz(s, len), - .size = len, - }; + cur.obj->data.dictionary.items[idx].key = CBUF_TO_ARENA_STR(arena, s, len); kvi_push(stack, cur); - cur = (ObjPopStackItem) { - .obj = &cur.obj->data.dictionary.items[idx].value, - .container = false, - }; + cur = (ObjPopStackItem){ .obj = &cur.obj->data.dictionary.items[idx].value }; } else { // stack: …, dict lua_pop(lstate, 1); @@ -1130,15 +1121,8 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) } const size_t idx = cur.obj->data.array.size++; lua_rawgeti(lstate, -1, (int)idx + 1); - if (lua_isnil(lstate, -1)) { - lua_pop(lstate, 2); - continue; - } kvi_push(stack, cur); - cur = (ObjPopStackItem) { - .obj = &cur.obj->data.array.items[idx], - .container = false, - }; + cur = (ObjPopStackItem){ .obj = &cur.obj->data.array.items[idx] }; } } assert(!cur.container); @@ -1152,7 +1136,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) case LUA_TSTRING: { size_t len; const char *s = lua_tolstring(lstate, -1, &len); - *cur.obj = STRING_OBJ(((String) { .data = xmemdupz(s, len), .size = len })); + *cur.obj = STRING_OBJ(CBUF_TO_ARENA_STR(arena, s, len)); break; } case LUA_TNUMBER: { @@ -1170,23 +1154,17 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) switch (table_props.type) { case kObjectTypeArray: - *cur.obj = ARRAY_OBJ(((Array) { .items = NULL, .size = 0, .capacity = 0 })); + *cur.obj = ARRAY_OBJ(((Array)ARRAY_DICT_INIT)); if (table_props.maxidx != 0) { - cur.obj->data.array.items = - xcalloc(table_props.maxidx, - sizeof(cur.obj->data.array.items[0])); - cur.obj->data.array.capacity = table_props.maxidx; + cur.obj->data.array = arena_array(arena, table_props.maxidx); cur.container = true; kvi_push(stack, cur); } break; case kObjectTypeDictionary: - *cur.obj = DICTIONARY_OBJ(((Dictionary) { .items = NULL, .size = 0, .capacity = 0 })); + *cur.obj = DICTIONARY_OBJ(((Dictionary)ARRAY_DICT_INIT)); if (table_props.string_keys_num != 0) { - cur.obj->data.dictionary.items = - xcalloc(table_props.string_keys_num, - sizeof(cur.obj->data.dictionary.items[0])); - cur.obj->data.dictionary.capacity = table_props.string_keys_num; + cur.obj->data.dictionary = arena_dict(arena, table_props.string_keys_num); cur.container = true; kvi_push(stack, cur); lua_pushnil(lstate); @@ -1238,7 +1216,9 @@ type_error: } kvi_destroy(stack); if (ERROR_SET(err)) { - api_free_object(ret); + if (!arena) { + api_free_object(ret); + } ret = NIL; lua_pop(lstate, lua_gettop(lstate) - initial_size + 1); } @@ -1246,20 +1226,20 @@ type_error: return ret; } -LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err) +LuaRef nlua_pop_LuaRef(lua_State *const lstate, Arena *arena, Error *err) { LuaRef rv = nlua_ref_global(lstate, -1); lua_pop(lstate, 1); return rv; } -handle_T nlua_pop_handle(lua_State *lstate, Error *err) +handle_T nlua_pop_handle(lua_State *lstate, Arena *arena, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { handle_T ret; if (lua_type(lstate, -1) != LUA_TNUMBER) { api_set_error(err, kErrorTypeValidation, "Expected Lua number"); - ret = (handle_T) - 1; + ret = (handle_T)(-1); } else { ret = (handle_T)lua_tonumber(lstate, -1); } @@ -1315,7 +1295,8 @@ void nlua_init_types(lua_State *const lstate) } // lua specific variant of api_dict_to_keydict -void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_opt, Error *err) +void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_opt, Arena *arena, + Error *err) { if (!lua_istable(L, -1)) { api_set_error(err, kErrorTypeValidation, "Expected Lua table"); @@ -1342,24 +1323,31 @@ void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_ char *mem = ((char *)retval + field->ptr_off); if (field->type == kObjectTypeNil) { - *(Object *)mem = nlua_pop_Object(L, true, err); + *(Object *)mem = nlua_pop_Object(L, true, arena, err); } else if (field->type == kObjectTypeInteger) { - *(Integer *)mem = nlua_pop_Integer(L, err); + if (field->is_hlgroup && lua_type(L, -1) == LUA_TSTRING) { + size_t name_len; + const char *name = lua_tolstring(L, -1, &name_len); + lua_pop(L, 1); + *(Integer *)mem = name_len > 0 ? syn_check_group(name, name_len) : 0; + } else { + *(Integer *)mem = nlua_pop_Integer(L, arena, err); + } } else if (field->type == kObjectTypeBoolean) { *(Boolean *)mem = nlua_pop_Boolean_strict(L, err); } else if (field->type == kObjectTypeString) { - *(String *)mem = nlua_pop_String(L, err); + *(String *)mem = nlua_pop_String(L, arena, err); } else if (field->type == kObjectTypeFloat) { - *(Float *)mem = nlua_pop_Float(L, err); + *(Float *)mem = nlua_pop_Float(L, arena, err); } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { - *(handle_T *)mem = nlua_pop_handle(L, err); + *(handle_T *)mem = nlua_pop_handle(L, arena, err); } else if (field->type == kObjectTypeArray) { - *(Array *)mem = nlua_pop_Array(L, err); + *(Array *)mem = nlua_pop_Array(L, arena, err); } else if (field->type == kObjectTypeDictionary) { - *(Dictionary *)mem = nlua_pop_Dictionary(L, false, err); + *(Dictionary *)mem = nlua_pop_Dictionary(L, false, arena, err); } else if (field->type == kObjectTypeLuaRef) { - *(LuaRef *)mem = nlua_pop_LuaRef(L, err); + *(LuaRef *)mem = nlua_pop_LuaRef(L, arena, err); } else { abort(); } @@ -1372,3 +1360,48 @@ void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_ lua_pop(L, 1); // [] } + +void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) +{ + lua_createtable(L, 0, 0); + for (size_t i = 0; table[i].str; i++) { + KeySetLink *field = &table[i]; + bool is_set = true; + if (field->opt_index >= 0) { + OptKeySet *ks = (OptKeySet *)value; + is_set = ks->is_set_ & (1ULL << field->opt_index); + } + + if (!is_set) { + continue; + } + + char *mem = ((char *)value + field->ptr_off); + + lua_pushstring(L, field->str); + if (field->type == kObjectTypeNil) { + nlua_push_Object(L, (Object *)mem, false); + } else if (field->type == kObjectTypeInteger) { + lua_pushinteger(L, *(Integer *)mem); + } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow + || field->type == kObjectTypeTabpage) { + lua_pushinteger(L, *(handle_T *)mem); + } else if (field->type == kObjectTypeFloat) { + lua_pushnumber(L, *(Float *)mem); + } else if (field->type == kObjectTypeBoolean) { + lua_pushboolean(L, *(Boolean *)mem); + } else if (field->type == kObjectTypeString) { + nlua_push_String(L, *(String *)mem, false); + } else if (field->type == kObjectTypeArray) { + nlua_push_Array(L, *(Array *)mem, false); + } else if (field->type == kObjectTypeDictionary) { + nlua_push_Dictionary(L, *(Dictionary *)mem, false); + } else if (field->type == kObjectTypeLuaRef) { + nlua_pushref(L, *(LuaRef *)mem); + } else { + abort(); + } + + lua_rawset(L, -3); + } +} diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 06d16efb05..1a9bd026b5 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -15,7 +15,9 @@ #include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/ui.h" #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" @@ -33,8 +35,9 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_getln.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/keycodes.h" #include "nvim/lua/converter.h" @@ -45,22 +48,30 @@ #include "nvim/main.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/option_vars.h" #include "nvim/os/fileio.h" +#include "nvim/os/fileio_defs.h" #include "nvim/os/os.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/strings.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/usercmd.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#ifndef MSWIN +# include <pthread.h> +#endif + static int in_fast_callback = 0; static bool in_script = false; @@ -115,7 +126,7 @@ lua_State *get_global_lstate(void) /// Convert lua error into a Vim error message /// /// @param lstate Lua interpreter state. -/// @param[in] msg Message base, must contain one `%*s`. +/// @param[in] msg Message base, must contain one `%.*s`. void nlua_error(lua_State *const lstate, const char *const msg) FUNC_ATTR_NONNULL_ALL { @@ -208,7 +219,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags const char *error = lua_tostring(lstate, -1); multiqueue_put(main_loop.events, nlua_luv_error_event, - 2, xstrdup(error), (intptr_t)kCallback); + xstrdup(error), (void *)(intptr_t)kCallback); lua_pop(lstate, 1); // error message retval = -status; } else { // LUA_OK @@ -254,8 +265,7 @@ static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nres if (status == LUA_ERRMEM && !(flags & LUVF_CALLBACK_NOEXIT)) { // Terminate this thread, as the main thread may be able to continue // execution. - os_errmsg(e_outofmem); - os_errmsg("\n"); + fprintf(stderr, "%s\n", e_outofmem); lua_close(lstate); #ifdef MSWIN ExitThread(0); @@ -266,11 +276,11 @@ static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nres const char *error = lua_tostring(lstate, -1); loop_schedule_deferred(&main_loop, - event_create(nlua_luv_error_event, 2, + event_create(nlua_luv_error_event, xstrdup(error), - is_callback - ? (intptr_t)kThreadCallback - : (intptr_t)kThread)); + (void *)(intptr_t)(is_callback + ? kThreadCallback + : kThread))); lua_pop(lstate, 1); // error message retval = -status; } else { // LUA_OK @@ -302,7 +312,10 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) lua_pop(lstate, 1); Error err = ERROR_INIT; - const Array pat = nlua_pop_Array(lstate, &err); + // TODO(bfredl): we could use an arena here for both "pat" and "ret", but then + // we need a path to not use the freelist but a private block local to the thread. + // We do not want mutex contentionery for the main arena freelist. + const Array pat = nlua_pop_Array(lstate, NULL, &err); if (ERROR_SET(&err)) { luaL_where(lstate, 1); lua_pushstring(lstate, err.msg); @@ -379,8 +392,7 @@ static int nlua_schedule(lua_State *const lstate) LuaRef cb = nlua_ref_global(lstate, 1); - multiqueue_put(main_loop.events, nlua_schedule_event, - 1, (void *)(ptrdiff_t)cb); + multiqueue_put(main_loop.events, nlua_schedule_event, (void *)(ptrdiff_t)cb); return 0; } @@ -643,8 +655,7 @@ static bool nlua_init_packages(lua_State *lstate, bool is_standalone) lua_getglobal(lstate, "require"); lua_pushstring(lstate, "vim._init_packages"); if (nlua_pcall(lstate, 1, 0)) { - os_errmsg(lua_tostring(lstate, -1)); - os_errmsg("\n"); + fprintf(stderr, "%s\n", lua_tostring(lstate, -1)); return false; } @@ -818,12 +829,12 @@ void nlua_init(char **argv, int argc, int lua_arg0) lua_State *lstate = luaL_newstate(); if (lstate == NULL) { - os_errmsg(_("E970: Failed to initialize lua interpreter\n")); + fprintf(stderr, _("E970: Failed to initialize lua interpreter\n")); os_exit(1); } luaL_openlibs(lstate); if (!nlua_state_init(lstate)) { - os_errmsg(_("E970: Failed to initialize builtin lua modules\n")); + fprintf(stderr, _("E970: Failed to initialize builtin lua modules\n")); #ifdef EXITFREE nlua_common_free_all_mem(lstate); #endif @@ -1022,15 +1033,14 @@ static int nlua_print(lua_State *const lstate) if (is_thread) { loop_schedule_deferred(&main_loop, - event_create(nlua_print_event, 2, + event_create(nlua_print_event, msg_ga.ga_data, - (intptr_t)msg_ga.ga_len)); + (void *)(intptr_t)msg_ga.ga_len)); } else if (in_fast_callback) { multiqueue_put(main_loop.events, nlua_print_event, - 2, msg_ga.ga_data, (intptr_t)msg_ga.ga_len); + msg_ga.ga_data, (void *)(intptr_t)msg_ga.ga_len); } else { - nlua_print_event((void *[]){ msg_ga.ga_data, - (void *)(intptr_t)msg_ga.ga_len }); + nlua_print_event((void *[]){ msg_ga.ga_data, (void *)(intptr_t)msg_ga.ga_len }); } return 0; @@ -1236,13 +1246,13 @@ static int nlua_rpc(lua_State *lstate, bool request) const char *name = luaL_checklstring(lstate, 2, &name_len); int nargs = lua_gettop(lstate) - 2; Error err = ERROR_INIT; - Array args = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + Array args = arena_array(&arena, (size_t)nargs); for (int i = 0; i < nargs; i++) { lua_pushvalue(lstate, i + 3); - ADD(args, nlua_pop_Object(lstate, false, &err)); + ADD(args, nlua_pop_Object(lstate, false, &arena, &err)); if (ERROR_SET(&err)) { - api_free_array(args); goto check_err; } } @@ -1251,7 +1261,7 @@ static int nlua_rpc(lua_State *lstate, bool request) ArenaMem res_mem = NULL; Object result = rpc_send_call(chan_id, name, args, &res_mem, &err); if (!ERROR_SET(&err)) { - nlua_push_Object(lstate, result, false); + nlua_push_Object(lstate, &result, false); arena_mem_free(res_mem); } } else { @@ -1259,10 +1269,11 @@ static int nlua_rpc(lua_State *lstate, bool request) api_set_error(&err, kErrorTypeValidation, "Invalid channel: %" PRIu64, chan_id); } - api_free_array(args); // TODO(bfredl): no } check_err: + arena_mem_free(arena_finish(&arena)); + if (ERROR_SET(&err)) { lua_pushstring(lstate, err.msg); api_clear_error(&err); @@ -1535,10 +1546,12 @@ int typval_exec_lua_callable(LuaRef lua_cb, int argcount, typval_T *argvars, typ /// /// @param[in] str String to execute. /// @param[in] args array of ... args +/// @param[in] mode Whether and how the the return value should be converted to Object +/// @param[in] arena can be NULL, then nested allocations are used /// @param[out] err Location where error will be saved. /// /// @return Return value of the execution. -Object nlua_exec(const String str, const Array args, Error *err) +Object nlua_exec(const String str, const Array args, LuaRetMode mode, Arena *arena, Error *err) { lua_State *const lstate = global_lstate; @@ -1551,7 +1564,7 @@ Object nlua_exec(const String str, const Array args, Error *err) } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, args.items[i], false); + nlua_push_Object(lstate, &args.items[i], false); } if (nlua_pcall(lstate, (int)args.size, 1)) { @@ -1562,7 +1575,7 @@ Object nlua_exec(const String str, const Array args, Error *err) return NIL; } - return nlua_pop_Object(lstate, false, err); + return nlua_call_pop_retval(lstate, mode, arena, err); } bool nlua_ref_is_function(LuaRef ref) @@ -1583,12 +1596,12 @@ bool nlua_ref_is_function(LuaRef ref) /// @param ref the reference to call (not consumed) /// @param name if non-NULL, sent to callback as first arg /// if NULL, only args are used -/// @param retval if true, convert return value to Object -/// if false, only check if return value is truthy +/// @param mode Whether and how the the return value should be converted to Object +/// @param arena can be NULL, then nested allocations are used /// @param err Error details, if any (if NULL, errors are echoed) -/// @return Return value of function, if retval was set. Otherwise -/// BOOLEAN_OBJ(true) or NIL. -Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Error *err) +/// @return Return value of function, as per mode +Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode, Arena *arena, + Error *err) { lua_State *const lstate = global_lstate; nlua_pushref(lstate, ref); @@ -1598,7 +1611,7 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Erro nargs++; } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, args.items[i], false); + nlua_push_Object(lstate, &args.items[i], false); } if (nlua_pcall(lstate, nargs, 1)) { @@ -1614,18 +1627,34 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Erro return NIL; } - if (retval) { - Error dummy = ERROR_INIT; - if (err == NULL) { - err = &dummy; - } - return nlua_pop_Object(lstate, false, err); - } else { - bool value = lua_toboolean(lstate, -1); + return nlua_call_pop_retval(lstate, mode, arena, err); +} + +static Object nlua_call_pop_retval(lua_State *lstate, LuaRetMode mode, Arena *arena, Error *err) +{ + if (lua_isnil(lstate, -1)) { lua_pop(lstate, 1); + return NIL; + } + Error dummy = ERROR_INIT; - return value ? BOOLEAN_OBJ(true) : NIL; + switch (mode) { + case kRetNilBool: { + bool bool_value = lua_toboolean(lstate, -1); + lua_pop(lstate, 1); + + return BOOLEAN_OBJ(bool_value); + } + case kRetLuaref: { + LuaRef ref = nlua_ref_global(lstate, -1); + lua_pop(lstate, 1); + + return LUAREF_OBJ(ref); } + case kRetObject: + return nlua_pop_Object(lstate, false, arena, err ? err : &dummy); + } + UNREACHABLE; } /// check if the current execution context is safe for calling deferred API @@ -1635,27 +1664,38 @@ bool nlua_is_deferred_safe(void) return in_fast_callback == 0; } -/// Run lua string +/// Executes Lua code. /// -/// Used for :lua. +/// Implements `:lua` and `:lua ={expr}`. /// -/// @param eap Vimscript command being run. +/// @param eap Vimscript `:lua {code}`, `:{range}lua`, or `:lua ={expr}` command. void ex_lua(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { + // ":{range}lua", only if no {code} + if (*eap->arg == NUL) { + if (eap->addr_count > 0) { + cmd_source_buffer(eap, true); + } else { + emsg(_(e_argreq)); + } + return; + } + size_t len; char *code = script_get(eap, &len); if (eap->skip || code == NULL) { xfree(code); return; } - // When =expr is used transform it to vim.print(expr) + + // ":lua {code}", ":={expr}" or ":lua ={expr}" + // + // When "=expr" is used transform it to "vim.print(expr)". if (eap->cmdidx == CMD_equal || code[0] == '=') { size_t off = (eap->cmdidx == CMD_equal) ? 0 : 1; len += sizeof("vim.print()") - 1 - off; - // code_buf needs to be 1 char larger then len for null byte in the end. - // lua nlua_typval_exec doesn't expect null terminated string so len - // needs to end before null byte. + // `nlua_typval_exec` doesn't expect NUL-terminated string so `len` must end before NUL byte. char *code_buf = xmallocz(len); vim_snprintf(code_buf, len + 1, "vim.print(%s)", code + off); xfree(code); @@ -1667,11 +1707,11 @@ void ex_lua(exarg_T *const eap) xfree(code); } -/// Run lua string for each line in range +/// Executes Lua code for-each line in a buffer range. /// -/// Used for :luado. +/// Implements `:luado`. /// -/// @param eap Vimscript command being run. +/// @param eap Vimscript `:luado {code}` command. void ex_luado(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { @@ -1715,10 +1755,15 @@ void ex_luado(exarg_T *const eap) nlua_error(lstate, _("E5110: Error executing lua: %.*s")); return; } + + buf_T *const was_curbuf = curbuf; + for (linenr_T l = eap->line1; l <= eap->line2; l++) { + // Check the line number, the command may have deleted lines. if (l > curbuf->b_ml.ml_line_count) { break; } + lua_pushvalue(lstate, -1); const char *const old_line = ml_get_buf(curbuf, l); // Get length of old_line here as calling Lua code may free it. @@ -1729,6 +1774,13 @@ void ex_luado(exarg_T *const eap) nlua_error(lstate, _("E5111: Error calling lua: %.*s")); break; } + + // Catch the command switching to another buffer. + // Check the line number, the command may have deleted lines. + if (curbuf != was_curbuf || l > curbuf->b_ml.ml_line_count) { + break; + } + if (lua_isstring(lstate, -1)) { size_t new_line_len; const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); @@ -1743,16 +1795,17 @@ void ex_luado(exarg_T *const eap) } lua_pop(lstate, 1); } + lua_pop(lstate, 1); check_cursor(); redraw_curbuf_later(UPD_NOT_VALID); } -/// Run lua file +/// Executes Lua code from a file location. /// -/// Used for :luafile. +/// Implements `:luafile`. /// -/// @param eap Vimscript command being run. +/// @param eap Vimscript `:luafile {file}` command. void ex_luafile(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { @@ -1776,7 +1829,11 @@ bool nlua_exec_file(const char *path) lua_getglobal(lstate, "loadfile"); lua_pushstring(lstate, path); } else { - FileDescriptor *stdin_dup = file_open_stdin(); + FileDescriptor stdin_dup; + int error = file_open_stdin(&stdin_dup); + if (error) { + return false; + } StringBuilder sb = KV_INITIAL_VALUE; kv_resize(sb, 64); @@ -1785,7 +1842,7 @@ bool nlua_exec_file(const char *path) if (got_int) { // User canceled. return false; } - ptrdiff_t read_size = file_read(stdin_dup, IObuff, 64); + ptrdiff_t read_size = file_read(&stdin_dup, IObuff, 64); if (read_size < 0) { // Error. return false; } @@ -1797,7 +1854,7 @@ bool nlua_exec_file(const char *path) } } kv_push(sb, NUL); - file_free(stdin_dup, false); + file_close(&stdin_dup, false); lua_getglobal(lstate, "loadstring"); lua_pushstring(lstate, sb.items); @@ -1900,13 +1957,14 @@ int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results) *num_results = 0; *results = NULL; - int prefix_len = (int)nlua_pop_Integer(lstate, &err); + Arena arena = ARENA_EMPTY; + int prefix_len = (int)nlua_pop_Integer(lstate, &arena, &err); if (ERROR_SET(&err)) { ret = FAIL; goto cleanup; } - Array completions = nlua_pop_Array(lstate, &err); + Array completions = nlua_pop_Array(lstate, &arena, &err); if (ERROR_SET(&err)) { ret = FAIL; goto cleanup_array; @@ -1930,7 +1988,7 @@ int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results) *num_results = result_array.ga_len; cleanup_array: - api_free_array(completions); + arena_mem_free(arena_finish(&arena)); cleanup: @@ -2178,7 +2236,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) // every possible modifier (with room to spare). If the list of possible // modifiers grows this may need to be updated. char buf[200] = { 0 }; - (void)uc_mods(buf, &cmdmod, false); + uc_mods(buf, &cmdmod, false); lua_pushstring(lstate, buf); lua_setfield(lstate, -2, "mods"); @@ -2274,11 +2332,9 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) /// String representation of a Lua function reference /// /// @return Allocated string -char *nlua_funcref_str(LuaRef ref) +char *nlua_funcref_str(LuaRef ref, Arena *arena) { lua_State *const lstate = global_lstate; - StringBuilder str = KV_INITIAL_VALUE; - kv_resize(str, 16); if (!lua_checkstack(lstate, 1)) { goto plain; @@ -2292,14 +2348,13 @@ char *nlua_funcref_str(LuaRef ref) lua_Debug ar; if (lua_getinfo(lstate, ">S", &ar) && *ar.source == '@' && ar.linedefined >= 0) { char *src = home_replace_save(NULL, ar.source + 1); - kv_printf(str, "<Lua %d: %s:%d>", ref, src, ar.linedefined); + String str = arena_printf(arena, "<Lua %d: %s:%d>", ref, src, ar.linedefined); xfree(src); - return str.items; + return str.data; } -plain: - kv_printf(str, "<Lua %d>", ref); - return str.items; +plain: {} + return arena_printf(arena, "<Lua %d>", ref).data; } /// Execute the vim._defaults module to set up default mappings and autocommands @@ -2311,7 +2366,23 @@ void nlua_init_defaults(void) lua_getglobal(L, "require"); lua_pushstring(L, "vim._defaults"); if (nlua_pcall(L, 1, 0)) { - os_errmsg(lua_tostring(L, -1)); - os_errmsg("\n"); + fprintf(stderr, "%s\n", lua_tostring(L, -1)); } } + +/// check lua function exist +bool nlua_func_exists(const char *lua_funcname) +{ + MAXSIZE_TEMP_ARRAY(args, 1); + size_t length = strlen(lua_funcname) + 8; + char *str = xmalloc(length); + vim_snprintf(str, length, "return %s", lua_funcname); + ADD_C(args, CSTR_AS_OBJ(str)); + Error err = ERROR_INIT; + Object result = NLUA_EXEC_STATIC("return type(loadstring(...)()) == 'function'", args, + kRetNilBool, NULL, &err); + xfree(str); + + api_clear_error(&err); + return LUARET_TRUTHY(result); +} diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index b38faddbb3..ebcd62122f 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -4,18 +4,13 @@ #include <lua.h> #include <stdbool.h> -#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/assert_defs.h" -#include "nvim/cmdexpand_defs.h" -#include "nvim/eval/typval_defs.h" -#include "nvim/ex_cmds_defs.h" +#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep +#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" -#include "nvim/lua/converter.h" #include "nvim/macros_defs.h" -#include "nvim/map_defs.h" #include "nvim/types_defs.h" -#include "nvim/usercmd.h" +#include "nvim/usercmd.h" // IWYU pragma: keep // Generated by msgpack-gen.lua void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; @@ -29,7 +24,8 @@ typedef struct { #endif } nlua_ref_state_t; -#define NLUA_EXEC_STATIC(cstr, arg, err) nlua_exec(STATIC_CSTR_AS_STRING(cstr), arg, err) +#define NLUA_EXEC_STATIC(cstr, arg, mode, arena, err) \ + nlua_exec(STATIC_CSTR_AS_STRING(cstr), arg, mode, arena, err) #define NLUA_CLEAR_REF(x) \ do { \ @@ -40,6 +36,16 @@ typedef struct { } \ } while (0) +typedef enum { + kRetObject, ///< any object, but doesn't preserve nested luarefs + kRetNilBool, ///< NIL preserved as such, other values return their booleanness + ///< Should also be used when return value is ignored, as it is allocation-free + kRetLuaref, ///< return value becomes a single Luaref, regardless of type (except NIL) +} LuaRetMode; + +/// To use with kRetNilBool for quick thuthyness check +#define LUARET_TRUTHY(res) ((res).type == kObjectTypeBoolean && (res).data.boolean == true) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/executor.h.generated.h" #endif diff --git a/src/nvim/lua/secure.c b/src/nvim/lua/secure.c index 65c13f8872..f62e0ace04 100644 --- a/src/nvim/lua/secure.c +++ b/src/nvim/lua/secure.c @@ -4,7 +4,7 @@ #include "nvim/charset.h" #include "nvim/ex_cmds_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/lua/secure.h" diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c index c261c5105e..ba83239dc5 100644 --- a/src/nvim/lua/spell.c +++ b/src/nvim/lua/spell.c @@ -7,7 +7,7 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight_defs.h" #include "nvim/lua/spell.h" @@ -15,10 +15,11 @@ #include "nvim/spell.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "lua/spell.c.generated.h" // IWYU pragma: export +# include "lua/spell.c.generated.h" #endif int nlua_spell_check(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL { if (lua_gettop(lstate) < 1) { return luaL_error(lstate, "Expected 1 argument"); @@ -99,6 +100,7 @@ static const luaL_Reg spell_functions[] = { }; int luaopen_spell(lua_State *L) + FUNC_ATTR_NONNULL_ALL { lua_newtable(L); luaL_register(L, NULL, spell_functions); diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 33770b2e62..8f58fd1a1a 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -6,7 +6,7 @@ #include <stddef.h> #include <stdint.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #ifdef NVIM_VENDOR_BIT # include "bit.h" @@ -19,10 +19,10 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/ex_eval.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/lua/base64.h" #include "nvim/lua/converter.h" @@ -31,10 +31,12 @@ #include "nvim/lua/xdiff.h" #include "nvim/map_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/strings.h" #include "nvim/types_defs.h" @@ -83,7 +85,8 @@ static int regex_match_line(lua_State *lstate) handle_T bufnr = (handle_T)luaL_checkinteger(lstate, 2); linenr_T rownr = (linenr_T)luaL_checkinteger(lstate, 3); - int start = 0, end = -1; + int start = 0; + int end = -1; if (narg >= 4) { start = (int)luaL_checkinteger(lstate, 4); } @@ -178,7 +181,8 @@ int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL } } - size_t codepoints = 0, codeunits = 0; + size_t codepoints = 0; + size_t codeunits = 0; mb_utflen(s1, (size_t)idx, &codepoints, &codeunits); lua_pushinteger(lstate, (lua_Integer)codepoints); @@ -222,11 +226,12 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL size_t s1_len; const char *s1 = luaL_checklstring(lstate, 1, &s1_len); ptrdiff_t offset = luaL_checkinteger(lstate, 2); - if (offset < 0 || offset > (intptr_t)s1_len) { + if (offset <= 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int head_offset = -utf_cp_head_off(s1, s1 + offset - 1); - lua_pushinteger(lstate, head_offset); + size_t const off = (size_t)(offset - 1); + int head_off = -utf_cp_bounds_len(s1, s1 + off, (int)(s1_len - off)).begin_off; + lua_pushinteger(lstate, head_off); return 1; } @@ -242,11 +247,12 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL size_t s1_len; const char *s1 = luaL_checklstring(lstate, 1, &s1_len); ptrdiff_t offset = luaL_checkinteger(lstate, 2); - if (offset < 0 || offset > (intptr_t)s1_len) { + if (offset <= 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int tail_offset = utf_cp_tail_off(s1, s1 + offset - 1); - lua_pushinteger(lstate, tail_offset); + size_t const off = (size_t)(offset - 1); + int tail_off = utf_cp_bounds_len(s1, s1 + off, (int)(s1_len - off)).end_off - 1; + lua_pushinteger(lstate, tail_off); return 1; } @@ -537,11 +543,14 @@ static int nlua_iconv(lua_State *lstate) return 1; } -// Like 'zx' but don't call newFoldLevel() +// Update foldlevels (e.g., by evaluating 'foldexpr') for all lines in the current window without +// invoking other side effects. Unlike `zx`, it does not close manually opened folds and does not +// open folds under the cursor. static int nlua_foldupdate(lua_State *lstate) { curwin->w_foldinvalid = true; // recompute folds - foldOpenCursor(); + foldUpdate(curwin, 1, (linenr_T)MAXLNUM); + curwin->w_foldinvalid = false; return 0; } diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 008b3f2e95..25a753b179 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -1,5 +1,5 @@ -// lua bindings for tree-sitter. -// NB: this file mostly contains a generic lua interface for tree-sitter +// lua bindings for treesitter. +// NB: this file mostly contains a generic lua interface for treesitter // trees and nodes, and could be broken out as a reusable lua package #include <assert.h> @@ -56,6 +56,7 @@ typedef struct { # include "lua/treesitter.c.generated.h" #endif +// TSParser static struct luaL_Reg parser_meta[] = { { "__gc", parser_gc }, { "__tostring", parser_tostring }, @@ -70,6 +71,7 @@ static struct luaL_Reg parser_meta[] = { { NULL, NULL } }; +// TSTree static struct luaL_Reg tree_meta[] = { { "__gc", tree_gc }, { "__tostring", tree_tostring }, @@ -80,6 +82,7 @@ static struct luaL_Reg tree_meta[] = { { NULL, NULL } }; +// TSNode static struct luaL_Reg node_meta[] = { { "__tostring", node_tostring }, { "__eq", node_eq }, @@ -119,6 +122,7 @@ static struct luaL_Reg node_meta[] = { { NULL, NULL } }; +// TSQuery static struct luaL_Reg query_meta[] = { { "__gc", query_gc }, { "__tostring", query_tostring }, @@ -1360,9 +1364,16 @@ static int node_equal(lua_State *L) /// assumes the match table being on top of the stack static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx) { - for (int i = 0; i < match->capture_count; i++) { - push_node(L, match->captures[i].node, nodeidx); - lua_rawseti(L, -2, (int)match->captures[i].index + 1); + // [match] + for (size_t i = 0; i < match->capture_count; i++) { + lua_rawgeti(L, -1, (int)match->captures[i].index + 1); // [match, captures] + if (lua_isnil(L, -1)) { // [match, nil] + lua_pop(L, 1); // [match] + lua_createtable(L, 1, 0); // [match, captures] + } + push_node(L, match->captures[i].node, nodeidx); // [match, captures, node] + lua_rawseti(L, -2, (int)lua_objlen(L, -2) + 1); // [match, captures] + lua_rawseti(L, -2, (int)match->captures[i].index + 1); // [match] } } @@ -1375,7 +1386,7 @@ static int query_next_match(lua_State *L) TSQueryMatch match; if (ts_query_cursor_next_match(cursor, &match)) { lua_pushinteger(L, match.pattern_index + 1); // [index] - lua_createtable(L, (int)ts_query_capture_count(query), 2); // [index, match] + lua_createtable(L, (int)ts_query_capture_count(query), 0); // [index, match] set_match(L, &match, lua_upvalueindex(2)); return 2; } @@ -1417,7 +1428,8 @@ static int query_next_capture(lua_State *L) if (n_pred > 0 && (ud->max_match_id < (int)match.id)) { ud->max_match_id = (int)match.id; - lua_pushvalue(L, lua_upvalueindex(4)); // [index, node, match] + // Create a new cleared match table + lua_createtable(L, (int)ts_query_capture_count(query), 2); // [index, node, match] set_match(L, &match, lua_upvalueindex(2)); lua_pushinteger(L, match.pattern_index + 1); lua_setfield(L, -2, "pattern"); @@ -1427,6 +1439,10 @@ static int query_next_capture(lua_State *L) lua_pushboolean(L, false); lua_setfield(L, -2, "active"); } + + // Set current_match to the new match + lua_replace(L, lua_upvalueindex(4)); // [index, node] + lua_pushvalue(L, lua_upvalueindex(4)); // [index, node, match] return 3; } return 2; @@ -1449,10 +1465,7 @@ static int node_rawquery(lua_State *L) cursor = ts_query_cursor_new(); } -#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH - // reset the start depth ts_query_cursor_set_max_start_depth(cursor, UINT32_MAX); -#endif ts_query_cursor_set_match_limit(cursor, 256); ts_query_cursor_exec(cursor, query, node); @@ -1475,11 +1488,8 @@ static int node_rawquery(lua_State *L) if (lua_type(L, -2) == LUA_TSTRING) { char *k = (char *)lua_tostring(L, -2); if (strequal("max_start_depth", k)) { - // TODO(lewis6991): remove ifdef when min TS version is 0.20.9 -#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH uint32_t max_start_depth = (uint32_t)lua_tointeger(L, -1); ts_query_cursor_set_max_start_depth(cursor, max_start_depth); -#endif } } lua_pop(L, 1); // pop the value; lua_next will pop the key. @@ -1655,8 +1665,10 @@ static int query_inspect(lua_State *L) return 0; } - uint32_t n_pat = ts_query_pattern_count(query); + // TSQueryInfo lua_createtable(L, 0, 2); // [retval] + + uint32_t n_pat = ts_query_pattern_count(query); lua_createtable(L, (int)n_pat, 1); // [retval, patterns] for (size_t i = 0; i < n_pat; i++) { uint32_t len; diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index 16c3aa5e11..035c171a14 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -5,7 +5,9 @@ #include <string.h> #include "luaconf.h" +#include "nvim/api/keysets_defs.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" #include "nvim/linematch.h" #include "nvim/lua/converter.h" @@ -74,7 +76,8 @@ static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb, int *decisions = NULL; size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite); - int lnuma = start_a, lnumb = start_b; + int lnuma = start_a; + int lnumb = start_b; int hunkstarta = lnuma; int hunkstartb = lnumb; @@ -186,136 +189,84 @@ static mmfile_t get_string_arg(lua_State *lstate, int idx) return mf; } -// Helper function for validating option types -static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char *name, Error *err) -{ - if (actType != expType) { - const char *type_str = - expType == kObjectTypeString - ? "string" : (expType == kObjectTypeInteger - ? "integer" : (expType == kObjectTypeBoolean - ? "boolean" : (expType == kObjectTypeLuaRef - ? "function" : "NA"))); - - api_set_error(err, kErrorTypeValidation, "%s is not a %s", name, - type_str); - return true; - } - - return false; -} - static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, xpparam_t *params, int64_t *linematch, Error *err) { - const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err); + Dict(xdl_diff) opts = KEYDICT_INIT; + char *err_param = NULL; + KeySetLink *KeyDict_xdl_diff_get_field(const char *str, size_t len); + nlua_pop_keydict(lstate, &opts, KeyDict_xdl_diff_get_field, &err_param, NULL, err); NluaXdiffMode mode = kNluaXdiffModeUnified; - bool had_on_hunk = false; bool had_result_type_indices = false; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - if (strequal("on_hunk", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeLuaRef, "on_hunk", err)) { - goto exit_1; - } - had_on_hunk = true; - nlua_pushref(lstate, v->data.luaref); - } else if (strequal("result_type", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeString, "result_type", err)) { - goto exit_1; - } - if (strequal("unified", v->data.string.data)) { - // the default - } else if (strequal("indices", v->data.string.data)) { - had_result_type_indices = true; - } else { - api_set_error(err, kErrorTypeValidation, "not a valid result_type"); - goto exit_1; - } - } else if (strequal("algorithm", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeString, "algorithm", err)) { - goto exit_1; - } - if (strequal("myers", v->data.string.data)) { - // default - } else if (strequal("minimal", v->data.string.data)) { - params->flags |= XDF_NEED_MINIMAL; - } else if (strequal("patience", v->data.string.data)) { - params->flags |= XDF_PATIENCE_DIFF; - } else if (strequal("histogram", v->data.string.data)) { - params->flags |= XDF_HISTOGRAM_DIFF; - } else { - api_set_error(err, kErrorTypeValidation, "not a valid algorithm"); - goto exit_1; - } - } else if (strequal("ctxlen", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeInteger, "ctxlen", err)) { - goto exit_1; - } - cfg->ctxlen = (long)v->data.integer; - } else if (strequal("interhunkctxlen", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeInteger, "interhunkctxlen", - err)) { - goto exit_1; - } - cfg->interhunkctxlen = (long)v->data.integer; - } else if (strequal("linematch", k.data)) { - if (v->type == kObjectTypeBoolean) { - *linematch = v->data.boolean ? INT64_MAX : 0; - } else if (v->type == kObjectTypeInteger) { - *linematch = v->data.integer; - } else { - api_set_error(err, kErrorTypeValidation, "linematch must be a boolean or integer"); - goto exit_1; - } + + if (HAS_KEY(&opts, xdl_diff, result_type)) { + if (strequal("unified", opts.result_type.data)) { + // the default + } else if (strequal("indices", opts.result_type.data)) { + had_result_type_indices = true; } else { - struct { - const char *name; - unsigned long value; - } flags[] = { - { "ignore_whitespace", XDF_IGNORE_WHITESPACE }, - { "ignore_whitespace_change", XDF_IGNORE_WHITESPACE_CHANGE }, - { "ignore_whitespace_change_at_eol", XDF_IGNORE_WHITESPACE_AT_EOL }, - { "ignore_cr_at_eol", XDF_IGNORE_CR_AT_EOL }, - { "ignore_blank_lines", XDF_IGNORE_BLANK_LINES }, - { "indent_heuristic", XDF_INDENT_HEURISTIC }, - { NULL, 0 }, - }; - bool key_used = false; - for (size_t j = 0; flags[j].name; j++) { - if (strequal(flags[j].name, k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeBoolean, flags[j].name, - err)) { - goto exit_1; - } - if (v->data.boolean) { - params->flags |= flags[j].value; - } - key_used = true; - break; - } - } + api_set_error(err, kErrorTypeValidation, "not a valid result_type"); + goto exit_1; + } + } - if (key_used) { - continue; - } + if (HAS_KEY(&opts, xdl_diff, algorithm)) { + if (strequal("myers", opts.algorithm.data)) { + // default + } else if (strequal("minimal", opts.algorithm.data)) { + params->flags |= XDF_NEED_MINIMAL; + } else if (strequal("patience", opts.algorithm.data)) { + params->flags |= XDF_PATIENCE_DIFF; + } else if (strequal("histogram", opts.algorithm.data)) { + params->flags |= XDF_HISTOGRAM_DIFF; + } else { + api_set_error(err, kErrorTypeValidation, "not a valid algorithm"); + goto exit_1; + } + } - api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + if (HAS_KEY(&opts, xdl_diff, ctxlen)) { + cfg->ctxlen = (long)opts.ctxlen; + } + + if (HAS_KEY(&opts, xdl_diff, interhunkctxlen)) { + cfg->interhunkctxlen = (long)opts.interhunkctxlen; + } + + if (HAS_KEY(&opts, xdl_diff, linematch)) { + if (opts.linematch.type == kObjectTypeBoolean) { + *linematch = opts.linematch.data.boolean ? INT64_MAX : 0; + } else if (opts.linematch.type == kObjectTypeInteger) { + *linematch = opts.linematch.data.integer; + } else { + api_set_error(err, kErrorTypeValidation, "linematch must be a boolean or integer"); goto exit_1; } } - if (had_on_hunk) { + params->flags |= opts.ignore_whitespace ? XDF_IGNORE_WHITESPACE : 0; + params->flags |= opts.ignore_whitespace_change ? XDF_IGNORE_WHITESPACE_CHANGE : 0; + params->flags |= opts.ignore_whitespace_change_at_eol ? XDF_IGNORE_WHITESPACE_AT_EOL : 0; + params->flags |= opts.ignore_cr_at_eol ? XDF_IGNORE_CR_AT_EOL : 0; + params->flags |= opts.ignore_blank_lines ? XDF_IGNORE_BLANK_LINES : 0; + params->flags |= opts.indent_heuristic ? XDF_INDENT_HEURISTIC : 0; + + if (HAS_KEY(&opts, xdl_diff, on_hunk)) { mode = kNluaXdiffModeOnHunkCB; + nlua_pushref(lstate, opts.on_hunk); + if (lua_type(lstate, -1) != LUA_TFUNCTION) { + api_set_error(err, kErrorTypeValidation, "on_hunk is not a function"); + } } else if (had_result_type_indices) { mode = kNluaXdiffModeLocations; } exit_1: - api_free_dictionary(opts); + api_free_string(opts.result_type); + api_free_string(opts.algorithm); + api_free_luaref(opts.on_hunk); return mode; } diff --git a/src/nvim/macros_defs.h b/src/nvim/macros_defs.h index a7af2f91c3..67da29031c 100644 --- a/src/nvim/macros_defs.h +++ b/src/nvim/macros_defs.h @@ -29,9 +29,6 @@ /// @return `s, sizeof(s) - 1` #define S_LEN(s) (s), (sizeof(s) - 1) -/// LINEEMPTY() - return true if the line is empty -#define LINEEMPTY(p) (*ml_get(p) == NUL) - // toupper() and tolower() that use the current locale. // Careful: Only call TOUPPER_LOC() and TOLOWER_LOC() with a character in the // range 0 - 255. toupper()/tolower() on some systems can't handle others. @@ -54,48 +51,12 @@ // Returns empty string if it is NULL. #define EMPTY_IF_NULL(x) ((x) ? (x) : "") -/// Adjust chars in a language according to 'langmap' option. -/// NOTE that there is no noticeable overhead if 'langmap' is not set. -/// When set the overhead for characters < 256 is small. -/// Don't apply 'langmap' if the character comes from the Stuff buffer or from a -/// mapping and the langnoremap option was set. -/// The do-while is just to ignore a ';' after the macro. -#define LANGMAP_ADJUST(c, condition) \ - do { \ - if (*p_langmap \ - && (condition) \ - && (p_lrm || (vgetc_busy ? typebuf_maplen() == 0 : KeyTyped)) \ - && !KeyStuffed \ - && (c) >= 0) \ - { \ - if ((c) < 256) \ - c = langmap_mapchar[c]; \ - else \ - c = langmap_adjust_mb(c); \ - } \ - } while (0) - #define WRITEBIN "wb" // no CR-LF translation #define READBIN "rb" #define APPENDBIN "ab" #define REPLACE_NORMAL(s) (((s)& REPLACE_FLAG) && !((s)& VREPLACE_FLAG)) -// MB_PTR_ADV(): advance a pointer to the next character, taking care of -// multi-byte characters if needed. Skip over composing chars. -#define MB_PTR_ADV(p) (p += utfc_ptr2len((char *)p)) - -// Advance multi-byte pointer, do not skip over composing chars. -#define MB_CPTR_ADV(p) (p += utf_ptr2len((char *)p)) - -// MB_PTR_BACK(): backup a pointer to the previous character, taking care of -// multi-byte characters if needed. Only use with "p" > "s" ! -#define MB_PTR_BACK(s, p) \ - (p -= utf_head_off((char *)(s), (char *)(p) - 1) + 1) - -// MB_CHAR2BYTES(): convert character to bytes and advance pointer to bytes -#define MB_CHAR2BYTES(c, b) ((b) += utf_char2bytes((c), ((char *)b))) - #define RESET_BINDING(wp) \ do { \ (wp)->w_p_scb = false; \ @@ -150,10 +111,13 @@ #endif #if defined(__clang__) || defined(__GNUC__) +# define EXPECT(cond, value) __builtin_expect((cond), (value)) # define UNREACHABLE __builtin_unreachable() -#elif defined(_MSVC_VER) +#elif defined(_MSC_VER) +# define EXPECT(cond, value) (cond) # define UNREACHABLE __assume(false) #else +# define EXPECT(cond, value) (cond) # define UNREACHABLE #endif diff --git a/src/nvim/main.c b/src/nvim/main.c index 6585bd1df7..ea189aaa0c 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -25,15 +25,19 @@ #include "nvim/arglist.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/decoration.h" #include "nvim/decoration_provider.h" #include "nvim/diff.h" +#include "nvim/drawline.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" @@ -42,12 +46,12 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" +#include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/hashtab.h" @@ -66,18 +70,20 @@ #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fileio.h" +#include "nvim/os/fileio_defs.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/lang.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/signal.h" #include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" @@ -85,6 +91,7 @@ #include "nvim/profile.h" #include "nvim/quickfix.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/shada.h" #include "nvim/statusline.h" #include "nvim/strings.h" @@ -97,10 +104,16 @@ #include "nvim/version.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/winfloat.h" + #ifdef MSWIN # include "nvim/os/os_win_console.h" #endif +#if defined(MSWIN) && !defined(MAKE_LIB) +# include "nvim/mbyte.h" +#endif + // values for "window_layout" enum { WIN_HOR = 1, // "-o" horizontally split windows @@ -138,11 +151,9 @@ void event_init(void) loop_init(&main_loop, NULL); resize_events = multiqueue_new_child(main_loop.events); - // early msgpack-rpc initialization - msgpack_rpc_helpers_init(); input_init(); signal_init(); - // finish mspgack-rpc initialization + // mspgack-rpc initialization channel_init(); terminal_init(); ui_init(); @@ -247,7 +258,7 @@ int main(int argc, char **argv) argv0 = argv[0]; if (!appname_is_valid()) { - os_errmsg("$NVIM_APPNAME must be a name or relative path.\n"); + fprintf(stderr, "$NVIM_APPNAME must be a name or relative path.\n"); exit(1); } @@ -336,7 +347,7 @@ int main(int argc, char **argv) ui_client_forward_stdin = !stdin_isatty; uint64_t rv = ui_client_start_server(params.argc, params.argv); if (!rv) { - os_errmsg("Failed to start Nvim server!\n"); + fprintf(stderr, "Failed to start Nvim server!\n"); os_exit(1); } ui_client_channel_id = rv; @@ -352,7 +363,7 @@ int main(int argc, char **argv) setbuf(stdout, NULL); // NOLINT(bugprone-unsafe-functions) - full_screen = !silent_mode; + full_screen = !silent_mode || exmode_active; // Set the default values for the options that use Rows and Columns. win_init_size(); @@ -385,6 +396,7 @@ int main(int argc, char **argv) } if (ui_client_channel_id) { + time_finish(); ui_client_run(remote_ui); // NORETURN } assert(!ui_client_channel_id && !use_builtin_ui); @@ -411,7 +423,19 @@ int main(int argc, char **argv) params.edit_type = EDIT_STDIN; } - open_script_files(¶ms); + if (params.scriptin) { + if (!open_scriptin(params.scriptin)) { + os_exit(2); + } + } + if (params.scriptout) { + scriptout = os_fopen(params.scriptout, params.scriptout_append ? APPENDBIN : WRITEBIN); + if (scriptout == NULL) { + fprintf(stderr, _("Cannot open for script output: \"")); + fprintf(stderr, "%s\"\n", params.scriptout); + os_exit(2); + } + } nlua_init_defaults(); @@ -615,7 +639,7 @@ int main(int argc, char **argv) // WORKAROUND(mhi): #3023 if (cb_flags & CB_UNNAMEDMASK) { - (void)eval_has_provider("clipboard"); + eval_has_provider("clipboard"); } if (params.luaf != NULL) { @@ -681,6 +705,9 @@ void getout(int exitval) assert(!ui_client_channel_id); exiting = true; + // make sure startuptimes have been flushed + time_finish(); + // On error during Ex mode, exit with a non-zero code. // POSIX requires this, although it's not 100% clear from the standard. if (exmode_active) { @@ -823,8 +850,7 @@ void preserve_exit(const char *errmsg) ui_client_stop(); } if (errmsg != NULL) { - os_errmsg(errmsg); - os_errmsg("\n"); + fprintf(stderr, "%s\n", errmsg); } if (ui_client_channel_id) { os_exit(1); @@ -835,7 +861,7 @@ void preserve_exit(const char *errmsg) FOR_ALL_BUFFERS(buf) { if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) { if (errmsg != NULL) { - os_errmsg("Vim: preserving files...\r\n"); + fprintf(stderr, "Vim: preserving files...\r\n"); } ml_sync_all(false, false, true); // preserve all swap files break; @@ -845,7 +871,7 @@ void preserve_exit(const char *errmsg) ml_close_all(false); // close all memfiles, without deleting if (errmsg != NULL) { - os_errmsg("Vim: Finished.\r\n"); + fprintf(stderr, "Vim: Finished.\r\n"); } getout(1); @@ -910,14 +936,11 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, if (is_ui) { if (!chan) { - os_errmsg("Remote ui failed to start: "); - os_errmsg(connect_error); - os_errmsg("\n"); + fprintf(stderr, "Remote ui failed to start: %s\n", connect_error); os_exit(1); } else if (strequal(server_addr, os_getenv("NVIM"))) { - os_errmsg("Cannot attach UI of :terminal child to its parent. "); - os_errmsg("(Unset $NVIM to skip this check)"); - os_errmsg("\n"); + fprintf(stderr, "%s", "Cannot attach UI of :terminal child to its parent. "); + fprintf(stderr, "%s\n", "(Unset $NVIM to skip this check)"); os_exit(1); } @@ -926,30 +949,29 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, } Array args = ARRAY_DICT_INIT; + kv_resize(args, (size_t)(argc - remote_args)); for (int t_argc = remote_args; t_argc < argc; t_argc++) { - String arg_s = cstr_to_string(argv[t_argc]); - ADD(args, STRING_OBJ(arg_s)); + ADD_C(args, CSTR_AS_OBJ(argv[t_argc])); } Error err = ERROR_INIT; - Array a = ARRAY_DICT_INIT; - ADD(a, INTEGER_OBJ((int)chan)); - ADD(a, CSTR_TO_OBJ(server_addr)); - ADD(a, CSTR_TO_OBJ(connect_error)); - ADD(a, ARRAY_OBJ(args)); + MAXSIZE_TEMP_ARRAY(a, 4); + ADD_C(a, INTEGER_OBJ((int)chan)); + ADD_C(a, CSTR_AS_OBJ(server_addr)); + ADD_C(a, CSTR_AS_OBJ(connect_error)); + ADD_C(a, ARRAY_OBJ(args)); String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)"); - Object o = nlua_exec(s, a, &err); - api_free_array(a); + Object o = nlua_exec(s, a, kRetObject, NULL, &err); + kv_destroy(args); if (ERROR_SET(&err)) { - os_errmsg(err.msg); - os_errmsg("\n"); + fprintf(stderr, "%s\n", err.msg); os_exit(2); } if (o.type == kObjectTypeDictionary) { rvobj.data.dictionary = o.data.dictionary; } else { - os_errmsg("vim._cs_remote returned unexpected value\n"); + fprintf(stderr, "vim._cs_remote returned unexpected value\n"); os_exit(2); } @@ -959,34 +981,33 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, for (size_t i = 0; i < rvobj.data.dictionary.size; i++) { if (strequal(rvobj.data.dictionary.items[i].key.data, "errmsg")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { - os_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); + fprintf(stderr, "vim._cs_remote returned an unexpected type for 'errmsg'\n"); os_exit(2); } - os_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); - os_errmsg("\n"); + fprintf(stderr, "%s\n", rvobj.data.dictionary.items[i].value.data.string.data); os_exit(2); } else if (strequal(rvobj.data.dictionary.items[i].key.data, "result")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { - os_errmsg("vim._cs_remote returned an unexpected type for 'result'\n"); + fprintf(stderr, "vim._cs_remote returned an unexpected type for 'result'\n"); os_exit(2); } - os_msg(rvobj.data.dictionary.items[i].value.data.string.data); + printf("%s", rvobj.data.dictionary.items[i].value.data.string.data); } else if (strequal(rvobj.data.dictionary.items[i].key.data, "tabbed")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { - os_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); + fprintf(stderr, "vim._cs_remote returned an unexpected type for 'tabbed'\n"); os_exit(2); } tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } else if (strequal(rvobj.data.dictionary.items[i].key.data, "should_exit")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { - os_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); + fprintf(stderr, "vim._cs_remote returned an unexpected type for 'should_exit'\n"); os_exit(2); } should_exit = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } } if (should_exit == kNone || tabbed == kNone) { - os_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n"); + fprintf(stderr, "vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n"); os_exit(2); } api_free_object(o); @@ -1008,6 +1029,7 @@ static bool edit_stdin(mparm_T *parmp) && !(embedded_mode && stdin_fd <= 0) && (!exmode_active || parmp->input_istext) && !stdin_isatty + && parmp->edit_type <= EDIT_STDIN && parmp->scriptin == NULL; // `-s -` was not given. return parmp->had_stdin_file || implicit; } @@ -1019,7 +1041,7 @@ static void command_line_scan(mparm_T *parmp) char **argv = parmp->argv; int argv_idx; // index in argv[n][] bool had_minmin = false; // found "--" argument - int want_argument; // option argument with argument + bool want_argument; // option argument with argument int n; argc--; @@ -1078,20 +1100,16 @@ static void command_line_scan(mparm_T *parmp) FileDescriptor fp; const int fof_ret = file_open_fd(&fp, STDOUT_FILENO, kFileWriteOnly); - msgpack_packer *p = msgpack_packer_new(&fp, msgpack_file_write); - if (fof_ret != 0) { semsg(_("E5421: Failed to open stdin: %s"), os_strerror(fof_ret)); } - if (p == NULL) { - emsg(_(e_outofmem)); + String data = api_metadata_raw(); + const ptrdiff_t written_bytes = file_write(&fp, data.data, data.size); + if (written_bytes < 0) { + msgpack_file_write_error((int)written_bytes); } - Object md = DICTIONARY_OBJ(api_metadata()); - msgpack_rpc_from_object(md, p); - - msgpack_packer_free(p); const int ff_ret = file_flush(&fp); if (ff_ret < 0) { msgpack_file_write_error(ff_ret); @@ -1122,7 +1140,7 @@ static void command_line_scan(mparm_T *parmp) } else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) { parmp->use_vimrc = "NONE"; parmp->clean = true; - set_option_value_give_err("shadafile", STATIC_CSTR_AS_OPTVAL("NONE"), 0); + set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0); } else if (STRNICMP(argv[0] + argv_idx, "luamod-dev", 9) == 0) { nlua_disable_preload = true; } else { @@ -1136,7 +1154,7 @@ static void command_line_scan(mparm_T *parmp) } break; case 'A': // "-A" start in Arabic mode. - set_option_value_give_err("arabic", BOOLEAN_OPTVAL(true), 0); + set_option_value_give_err(kOptArabic, BOOLEAN_OPTVAL(true), 0); break; case 'b': // "-b" binary mode. // Needs to be effective before expanding file names, because @@ -1166,8 +1184,8 @@ static void command_line_scan(mparm_T *parmp) usage(); os_exit(0); case 'H': // "-H" start in Hebrew mode: rl + keymap=hebrew set. - set_option_value_give_err("keymap", STATIC_CSTR_AS_OPTVAL("hebrew"), 0); - set_option_value_give_err("rl", BOOLEAN_OPTVAL(true), 0); + set_option_value_give_err(kOptKeymap, STATIC_CSTR_AS_OPTVAL("hebrew"), 0); + set_option_value_give_err(kOptRightleft, BOOLEAN_OPTVAL(true), 0); break; case 'M': // "-M" no changes or writing of files reset_modifiable(); @@ -1247,7 +1265,7 @@ static void command_line_scan(mparm_T *parmp) // default is 10: a little bit verbose p_verbose = get_number_arg(argv[0], &argv_idx, 10); if (argv[0][argv_idx] != NUL) { - set_option_value_give_err("verbosefile", CSTR_AS_OPTVAL(argv[0] + argv_idx), 0); + set_option_value_give_err(kOptVerbosefile, CSTR_AS_OPTVAL(argv[0] + argv_idx), 0); argv_idx = (int)strlen(argv[0]); } break; @@ -1255,7 +1273,7 @@ static void command_line_scan(mparm_T *parmp) // "-w {scriptout}" write to script if (ascii_isdigit((argv[0])[argv_idx])) { n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value_give_err("window", NUMBER_OPTVAL((OptInt)n), 0); + set_option_value_give_err(kOptWindow, NUMBER_OPTVAL((OptInt)n), 0); break; } want_argument = true; @@ -1351,7 +1369,7 @@ static void command_line_scan(mparm_T *parmp) break; case 'i': // "-i {shada}" use for shada - set_option_value_give_err("shadafile", CSTR_AS_OPTVAL(argv[0]), 0); + set_option_value_give_err(kOptShadafile, CSTR_AS_OPTVAL(argv[0]), 0); break; case 'l': // "-l" Lua script: args after "-l". @@ -1361,7 +1379,7 @@ static void command_line_scan(mparm_T *parmp) parmp->no_swap_file = true; parmp->use_vimrc = parmp->use_vimrc ? parmp->use_vimrc : "NONE"; if (p_shadafile == NULL || *p_shadafile == NUL) { - set_option_value_give_err("shadafile", STATIC_CSTR_AS_OPTVAL("NONE"), 0); + set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0); } parmp->luaf = argv[0]; argc--; @@ -1377,7 +1395,7 @@ scripterror: vim_snprintf(IObuff, IOSIZE, _("Attempt to open script file again: \"%s %s\"\n"), argv[-1], argv[0]); - os_errmsg(IObuff); + fprintf(stderr, "%s", IObuff); os_exit(2); } parmp->scriptin = argv[0]; @@ -1397,7 +1415,7 @@ scripterror: if (ascii_isdigit(*(argv[0]))) { argv_idx = 0; n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value_give_err("window", NUMBER_OPTVAL((OptInt)n), 0); + set_option_value_give_err(kOptWindow, NUMBER_OPTVAL((OptInt)n), 0); argv_idx = -1; break; } @@ -1486,9 +1504,16 @@ static void init_params(mparm_T *paramp, int argc, char **argv) /// Initialize global startuptime file if "--startuptime" passed as an argument. static void init_startuptime(mparm_T *paramp) { + bool is_embed = false; + for (int i = 1; i < paramp->argc - 1; i++) { + if (STRICMP(paramp->argv[i], "--embed") == 0) { + is_embed = true; + break; + } + } for (int i = 1; i < paramp->argc - 1; i++) { if (STRICMP(paramp->argv[i], "--startuptime") == 0) { - time_fd = fopen(paramp->argv[i + 1], "a"); + time_init(paramp->argv[i + 1], is_embed ? "Embedded" : "Primary/TUI"); time_start("--- NVIM STARTING ---"); break; } @@ -1548,7 +1573,7 @@ static void handle_quickfix(mparm_T *paramp) { if (paramp->edit_type == EDIT_QF) { if (paramp->use_ef != NULL) { - set_string_option_direct("ef", -1, paramp->use_ef, OPT_FREE, SID_CARG); + set_string_option_direct(kOptErrorfile, paramp->use_ef, 0, SID_CARG); } vim_snprintf(IObuff, IOSIZE, "cfile %s", p_ef); if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) { @@ -1587,7 +1612,7 @@ static void read_stdin(void) bool save_msg_didany = msg_didany; set_buflisted(true); // Create memfile and read from stdin. - (void)open_buffer(true, NULL, 0); + open_buffer(true, NULL, 0); if (buf_is_empty(curbuf) && curbuf->b_next != NULL) { // stdin was empty, go to buffer 2 (e.g. "echo file1 | xargs nvim"). #8561 do_cmdline_cmd("silent! bnext"); @@ -1600,38 +1625,6 @@ static void read_stdin(void) check_swap_exists_action(); } -static void open_script_files(mparm_T *parmp) -{ - if (parmp->scriptin) { - int error; - if (strequal(parmp->scriptin, "-")) { - FileDescriptor *stdin_dup = file_open_stdin(); - scriptin[0] = stdin_dup; - } else { - scriptin[0] = file_open_new(&error, parmp->scriptin, - kFileReadOnly|kFileNonBlocking, 0); - if (scriptin[0] == NULL) { - vim_snprintf(IObuff, IOSIZE, - _("Cannot open for reading: \"%s\": %s\n"), - parmp->scriptin, os_strerror(error)); - os_errmsg(IObuff); - os_exit(2); - } - } - save_typebuf(); - } - - if (parmp->scriptout) { - scriptout = os_fopen(parmp->scriptout, parmp->scriptout_append ? APPENDBIN : WRITEBIN); - if (scriptout == NULL) { - os_errmsg(_("Cannot open for script output: \"")); - os_errmsg(parmp->scriptout); - os_errmsg("\"\n"); - os_exit(2); - } - } -} - // Create the requested number of windows and edit buffers in them. // Also does recovery if "recoverymode" set. static void create_windows(mparm_T *parmp) @@ -1679,7 +1672,7 @@ static void create_windows(mparm_T *parmp) // Don't execute Win/Buf Enter/Leave autocommands here autocmd_no_enter++; autocmd_no_leave++; - int dorewind = true; + bool dorewind = true; while (done++ < 1000) { if (dorewind) { if (parmp->window_layout == WIN_TABS) { @@ -1710,7 +1703,7 @@ static void create_windows(mparm_T *parmp) set_buflisted(true); // create memfile, read file - (void)open_buffer(false, NULL, 0); + open_buffer(false, NULL, 0); if (swap_exists_action == SEA_QUIT) { if (got_int || only_one_window()) { @@ -1732,7 +1725,7 @@ static void create_windows(mparm_T *parmp) } os_breakcheck(); if (got_int) { - (void)vgetc(); // only break the file loading, not the rest + vgetc(); // only break the file loading, not the rest break; } } @@ -1793,7 +1786,7 @@ static void edit_buffers(mparm_T *parmp, char *cwd) p_shm_save = xstrdup(p_shm); snprintf(buf, sizeof(buf), "F%s", p_shm); - set_option_value_give_err("shm", CSTR_AS_OPTVAL(buf), 0); + set_option_value_give_err(kOptShortmess, CSTR_AS_OPTVAL(buf), 0); } } else { if (curwin->w_next == NULL) { // just checking @@ -1811,9 +1804,9 @@ static void edit_buffers(mparm_T *parmp, char *cwd) // Edit file from arg list, if there is one. When "Quit" selected // at the ATTENTION prompt close the window. swap_exists_did_quit = false; - (void)do_ecmd(0, arg_idx < GARGCOUNT - ? alist_name(&GARGLIST[arg_idx]) - : NULL, NULL, NULL, ECMD_LASTL, ECMD_HIDE, curwin); + do_ecmd(0, arg_idx < GARGCOUNT + ? alist_name(&GARGLIST[arg_idx]) + : NULL, NULL, NULL, ECMD_LASTL, ECMD_HIDE, curwin); if (swap_exists_did_quit) { // abort or quit selected if (got_int || only_one_window()) { @@ -1832,13 +1825,13 @@ static void edit_buffers(mparm_T *parmp, char *cwd) } os_breakcheck(); if (got_int) { - (void)vgetc(); // only break the file loading, not the rest + vgetc(); // only break the file loading, not the rest break; } } if (p_shm_save != NULL) { - set_option_value_give_err("shm", CSTR_AS_OPTVAL(p_shm_save), 0); + set_option_value_give_err(kOptShortmess, CSTR_AS_OPTVAL(p_shm_save), 0); xfree(p_shm_save); } @@ -1964,7 +1957,7 @@ static void do_system_initialization(void) #ifdef SYS_VIMRC_FILE // Get system wide defaults, if the file name is defined. - (void)do_source(SYS_VIMRC_FILE, false, DOSO_NONE, NULL); + do_source(SYS_VIMRC_FILE, false, DOSO_NONE, NULL); #endif } @@ -2066,7 +2059,7 @@ static void do_exrc_initialization(void) str = nlua_read_secure(VIMRC_LUA_FILE); if (str != NULL) { Error err = ERROR_INIT; - nlua_exec(cstr_as_string(str), (Array)ARRAY_DICT_INIT, &err); + nlua_exec(cstr_as_string(str), (Array)ARRAY_DICT_INIT, kRetNilBool, NULL, &err); xfree(str); if (ERROR_SET(&err)) { semsg("Error detected while processing %s:", VIMRC_LUA_FILE); @@ -2158,17 +2151,12 @@ static void print_mainerr(const char *errstr, const char *str) signal_stop(); // kill us with CTRL-C here, if you like - os_errmsg(prgname); - os_errmsg(": "); - os_errmsg(_(errstr)); + fprintf(stderr, "%s: %s", prgname, _(errstr)); if (str != NULL) { - os_errmsg(": \""); - os_errmsg(str); - os_errmsg("\""); + fprintf(stderr, ": \"%s\"", str); } - os_errmsg(_("\nMore info with \"")); - os_errmsg(prgname); - os_errmsg(" -h\"\n"); + fprintf(stderr, _("\nMore info with \"")); + fprintf(stderr, "%s -h\"\n", prgname); } /// Prints version information for "nvim -v" or "nvim --version". @@ -2176,7 +2164,7 @@ static void version(void) { // TODO(bfred): not like this? nlua_init(NULL, 0, -1); - info_message = true; // use os_msg(), not os_errmsg() + info_message = true; // use stdout, not stderr list_version(); msg_putchar('\n'); msg_didout = false; @@ -2187,38 +2175,38 @@ static void usage(void) { signal_stop(); // kill us with CTRL-C here, if you like - os_msg(_("Usage:\n")); - os_msg(_(" nvim [options] [file ...]\n")); - os_msg(_("\nOptions:\n")); - os_msg(_(" --cmd <cmd> Execute <cmd> before any config\n")); - os_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n")); - os_msg(_(" -l <script> [args...] Execute Lua <script> (with optional args)\n")); - os_msg(_(" -S <session> Source <session> after loading the first file\n")); - os_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n")); - os_msg(_(" -u <config> Use this config file\n")); - os_msg("\n"); - os_msg(_(" -d Diff mode\n")); - os_msg(_(" -es, -Es Silent (batch) mode\n")); - os_msg(_(" -h, --help Print this help message\n")); - os_msg(_(" -i <shada> Use this shada file\n")); - os_msg(_(" -n No swap file, use memory only\n")); - os_msg(_(" -o[N] Open N windows (default: one per file)\n")); - os_msg(_(" -O[N] Open N vertical windows (default: one per file)\n")); - os_msg(_(" -p[N] Open N tab pages (default: one per file)\n")); - os_msg(_(" -R Read-only (view) mode\n")); - os_msg(_(" -v, --version Print version information\n")); - os_msg(_(" -V[N][file] Verbose [level][file]\n")); - os_msg("\n"); - os_msg(_(" -- Only file names after this\n")); - os_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); - os_msg(_(" --clean \"Factory defaults\" (skip user config and plugins, shada)\n")); - os_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); - os_msg(_(" --headless Don't start a user interface\n")); - os_msg(_(" --listen <address> Serve RPC API from this address\n")); - os_msg(_(" --remote[-subcommand] Execute commands remotely on a server\n")); - os_msg(_(" --server <address> Specify RPC server to send commands to\n")); - os_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); - os_msg(_("\nSee \":help startup-options\" for all options.\n")); + printf(_("Usage:\n")); + printf(_(" nvim [options] [file ...]\n")); + printf(_("\nOptions:\n")); + printf(_(" --cmd <cmd> Execute <cmd> before any config\n")); + printf(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n")); + printf(_(" -l <script> [args...] Execute Lua <script> (with optional args)\n")); + printf(_(" -S <session> Source <session> after loading the first file\n")); + printf(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n")); + printf(_(" -u <config> Use this config file\n")); + printf("\n"); + printf(_(" -d Diff mode\n")); + printf(_(" -es, -Es Silent (batch) mode\n")); + printf(_(" -h, --help Print this help message\n")); + printf(_(" -i <shada> Use this shada file\n")); + printf(_(" -n No swap file, use memory only\n")); + printf(_(" -o[N] Open N windows (default: one per file)\n")); + printf(_(" -O[N] Open N vertical windows (default: one per file)\n")); + printf(_(" -p[N] Open N tab pages (default: one per file)\n")); + printf(_(" -R Read-only (view) mode\n")); + printf(_(" -v, --version Print version information\n")); + printf(_(" -V[N][file] Verbose [level][file]\n")); + printf("\n"); + printf(_(" -- Only file names after this\n")); + printf(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); + printf(_(" --clean \"Factory defaults\" (skip user config and plugins, shada)\n")); + printf(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); + printf(_(" --headless Don't start a user interface\n")); + printf(_(" --listen <address> Serve RPC API from this address\n")); + printf(_(" --remote[-subcommand] Execute commands remotely on a server\n")); + printf(_(" --server <address> Specify RPC server to send commands to\n")); + printf(_(" --startuptime <file> Write startup timing messages to <file>\n")); + printf(_("\nSee \":help startup-options\" for all options.\n")); } // Check the result of the ATTENTION dialog: diff --git a/src/nvim/main.h b/src/nvim/main.h index 6aeb62712a..dedfadf270 100644 --- a/src/nvim/main.h +++ b/src/nvim/main.h @@ -3,6 +3,7 @@ #include <stdbool.h> #include "nvim/event/loop.h" +#include "nvim/types_defs.h" // Maximum number of commands from + or -c arguments. #define MAX_ARG_CMDS 10 diff --git a/src/nvim/map.c b/src/nvim/map.c index be6bf58daa..d7d1a00158 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -118,6 +118,9 @@ void mh_clear(MapHash *h) #define VAL_NAME(x) quasiquote(x, ptr_t) #include "nvim/map_value_impl.c.h" #undef VAL_NAME +#define VAL_NAME(x) quasiquote(x, int) +#include "nvim/map_value_impl.c.h" +#undef VAL_NAME #undef KEY_NAME #define KEY_NAME(x) x##cstr_t diff --git a/src/nvim/map_defs.h b/src/nvim/map_defs.h index 147c03327a..36c851497d 100644 --- a/src/nvim/map_defs.h +++ b/src/nvim/map_defs.h @@ -33,7 +33,8 @@ static inline bool equal_String(String a, String b) if (a.size != b.size) { return false; } - return memcmp(a.data, b.data, a.size) == 0; + + return (a.size == 0) || (memcmp(a.data, b.data, a.size) == 0); } #define Set(type) Set_##type @@ -152,6 +153,7 @@ KEY_DECLS(HlEntry) KEY_DECLS(ColorKey) MAP_DECLS(int, int) +MAP_DECLS(ptr_t, int) MAP_DECLS(int, ptr_t) MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(cstr_t, int) @@ -172,9 +174,14 @@ MAP_DECLS(ColorKey, ColorItem) #define set_put_ref(T, set, key, key_alloc) set_put_##T(set, key, key_alloc) #define set_put_idx(T, set, key, status) mh_put_##T(set, key, status) #define set_del(T, set, key) set_del_##T(set, key) -#define set_destroy(T, set) (xfree((set)->keys), xfree((set)->h.hash)) -#define set_clear(T, set) mh_clear(&(set)->h) #define set_size(set) ((set)->h.size) +#define set_clear(T, set) mh_clear(&(set)->h) +#define set_destroy(T, set) \ + do { \ + xfree((set)->keys); \ + xfree((set)->h.hash); \ + *(set) = (Set(T)) SET_INIT; \ + } while (0) #define map_get(T, U) map_get_##T##U #define map_has(T, map, key) set_has(T, &(map)->set, key) @@ -182,9 +189,13 @@ MAP_DECLS(ColorKey, ColorItem) #define map_ref(T, U) map_ref_##T##U #define map_put_ref(T, U) map_put_ref_##T##U #define map_del(T, U) map_del_##T##U -#define map_destroy(T, map) (set_destroy(T, &(map)->set), xfree((map)->values)) -#define map_clear(T, map) set_clear(T, &(map)->set) #define map_size(map) set_size(&(map)->set) +#define map_clear(T, map) set_clear(T, &(map)->set) +#define map_destroy(T, map) \ + do { \ + set_destroy(T, &(map)->set); \ + XFREE_CLEAR((map)->values); \ + } while (0) #define pmap_get(T) map_get(T, ptr_t) #define pmap_put(T) map_put(T, ptr_t) diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 17593a9121..43a4c10ea7 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -22,19 +22,24 @@ #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" +#include "nvim/eval/userfunc.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_session.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/keycodes.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" #include "nvim/mapping.h" +#include "nvim/mapping_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option_defs.h" @@ -48,6 +53,7 @@ #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/vim_defs.h" /// List used for abbreviations. @@ -138,20 +144,6 @@ mapblock_T *get_buf_maphash_list(int state, int c) return curbuf->b_maphash[MAP_HASH(state, c)]; } -/// Retrieve the mapblock at the index either globally or for a certain buffer -/// -/// @param index The index in the maphash[] -/// @param buf The buffer to get the maphash from. NULL for global -mapblock_T *get_maphash(int index, buf_T *buf) - FUNC_ATTR_PURE -{ - if (index >= MAX_MAPHASH) { - return NULL; - } - - return (buf == NULL) ? maphash[index] : buf->b_maphash[index]; -} - /// Delete one entry from the abbrlist or maphash[]. /// "mpp" is a pointer to the m_next field of the PREVIOUS entry! static void mapblock_free(mapblock_T **mpp) @@ -168,51 +160,47 @@ static void mapblock_free(mapblock_T **mpp) xfree(mp); } -/// Return characters to represent the map mode in an allocated string +/// put characters to represent the map mode in a string buffer /// -/// @return [allocated] NUL-terminated string with characters. -static char *map_mode_to_chars(int mode) - FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET +/// @param[out] buf must be at least 7 bytes (including NUL) +void map_mode_to_chars(int mode, char *buf) + FUNC_ATTR_NONNULL_ALL { - garray_T mapmode; - - ga_init(&mapmode, 1, 7); - + char *p = buf; if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE)) { - ga_append(&mapmode, '!'); // :map! + *p++ = '!'; // :map! } else if (mode & MODE_INSERT) { - ga_append(&mapmode, 'i'); // :imap + *p++ = 'i'; // :imap } else if (mode & MODE_LANGMAP) { - ga_append(&mapmode, 'l'); // :lmap + *p++ = 'l'; // :lmap } else if (mode & MODE_CMDLINE) { - ga_append(&mapmode, 'c'); // :cmap + *p++ = 'c'; // :cmap } else if ((mode & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING)) == (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING)) { - ga_append(&mapmode, ' '); // :map + *p++ = ' '; // :map } else { if (mode & MODE_NORMAL) { - ga_append(&mapmode, 'n'); // :nmap + *p++ = 'n'; // :nmap } if (mode & MODE_OP_PENDING) { - ga_append(&mapmode, 'o'); // :omap + *p++ = 'o'; // :omap } if (mode & MODE_TERMINAL) { - ga_append(&mapmode, 't'); // :tmap + *p++ = 't'; // :tmap } if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT)) { - ga_append(&mapmode, 'v'); // :vmap + *p++ = 'v'; // :vmap } else { if (mode & MODE_VISUAL) { - ga_append(&mapmode, 'x'); // :xmap + *p++ = 'x'; // :xmap } if (mode & MODE_SELECT) { - ga_append(&mapmode, 's'); // :smap + *p++ = 's'; // :smap } } } - ga_append(&mapmode, NUL); - return (char *)mapmode.ga_data; + *p = NUL; } /// @param local true for buffer-local map @@ -231,10 +219,10 @@ static void showmap(mapblock_T *mp, bool local) } } - char *const mapchars = map_mode_to_chars(mp->m_mode); + char mapchars[7]; + map_mode_to_chars(mp->m_mode, mapchars); msg_puts(mapchars); size_t len = strlen(mapchars); - xfree(mapchars); while (++len <= 3) { msg_putchar(' '); @@ -264,7 +252,7 @@ static void showmap(mapblock_T *mp, bool local) // Use false below if we only want things like <Up> to show up as such on // the rhs, and not M-x etc, true gets both -- webb if (mp->m_luaref != LUA_NOREF) { - char *str = nlua_funcref_str(mp->m_luaref); + char *str = nlua_funcref_str(mp->m_luaref, NULL); msg_puts_attr(str, HL_ATTR(HLF_8)); xfree(str); } else if (mp->m_str[0] == NUL) { @@ -304,11 +292,11 @@ static void showmap(mapblock_T *mp, bool local) /// @param[in] orig_rhs Original mapping RHS, with characters to replace. /// @param[in] rhs_lua Lua reference for Lua mappings. /// @param[in] orig_rhs_len `strlen` of orig_rhs. -/// @param[in] cpo_flags See param docs for @ref replace_termcodes. +/// @param[in] cpo_val See param docs for @ref replace_termcodes. /// @param[out] mapargs MapArguments struct holding the replaced strings. static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs_len, const char *const orig_rhs, const size_t orig_rhs_len, - const LuaRef rhs_lua, const int cpo_flags, + const LuaRef rhs_lua, const char *const cpo_val, MapArguments *const mapargs) { char lhs_buf[128]; @@ -320,12 +308,13 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs // replace_termcodes() may move the result to allocated memory, which // needs to be freed later (*lhs_buf and *rhs_buf). // replace_termcodes() also removes CTRL-Vs and sometimes backslashes. - // If something like <C-H> is simplified to 0x08 then mark it as simplified. + // If something like <C-H> is simplified to 0x08 then mark it as simplified + // and also add en entry with a modifier. bool did_simplify = false; const int flags = REPTERM_FROM_PART | REPTERM_DO_LT; char *bufarg = lhs_buf; char *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, 0, - flags, &did_simplify, cpo_flags); + flags, &did_simplify, cpo_val); if (replaced == NULL) { return false; } @@ -333,7 +322,7 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs xstrlcpy(mapargs->lhs, replaced, sizeof(mapargs->lhs)); if (did_simplify) { replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, 0, - flags | REPTERM_NO_SIMPLIFY, NULL, cpo_flags); + flags | REPTERM_NO_SIMPLIFY, NULL, cpo_val); if (replaced == NULL) { return false; } @@ -343,14 +332,14 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs mapargs->alt_lhs_len = 0; } - set_maparg_rhs(orig_rhs, orig_rhs_len, rhs_lua, 0, cpo_flags, mapargs); + set_maparg_rhs(orig_rhs, orig_rhs_len, rhs_lua, 0, cpo_val, mapargs); return true; } /// @see set_maparg_lhs_rhs static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len, - const LuaRef rhs_lua, const scid_T sid, const int cpo_flags, + const LuaRef rhs_lua, const scid_T sid, const char *const cpo_val, MapArguments *const mapargs) { mapargs->rhs_lua = rhs_lua; @@ -366,7 +355,7 @@ static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len } else { char *rhs_buf = NULL; char *replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, sid, - REPTERM_DO_LT, NULL, cpo_flags); + REPTERM_DO_LT, NULL, cpo_val); mapargs->rhs_len = strlen(replaced); // NB: replace_termcodes may produce an empty string even if orig_rhs is non-empty // (e.g. a single ^V, see :h map-empty-rhs) @@ -493,7 +482,7 @@ static int str_to_mapargs(const char *strargs, bool is_unmap, MapArguments *mapa size_t orig_rhs_len = strlen(rhs_start); if (!set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len, rhs_start, orig_rhs_len, LUA_NOREF, - CPO_TO_CPO_FLAGS, mapargs)) { + p_cpo, mapargs)) { return 1; } @@ -1104,7 +1093,7 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo char *buf = NULL; const char *const rhs = replace_termcodes(str, strlen(str), &buf, 0, - REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); + REPTERM_DO_LT, NULL, p_cpo); #define MAPMODE(mode, modechars, chr, modeflags) \ do { \ @@ -1122,7 +1111,7 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo MAPMODE(mode, modechars, 'c', MODE_CMDLINE); #undef MAPMODE - int retval = map_to_exists_mode(rhs, mode, abbr); + bool retval = map_to_exists_mode(rhs, mode, abbr); xfree(buf); return retval; @@ -1138,7 +1127,7 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo /// @param[in] abbr true if checking abbreviations in place of mappings. /// /// @return true if there is at least one mapping with given parameters. -int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr) +bool map_to_exists_mode(const char *const rhs, const int mode, const bool abbr) { bool exp_buffer = false; @@ -1190,16 +1179,16 @@ static bool expand_buffer = false; /// wider than the original description. The caller has to free the string /// afterwards. /// -/// @param cpo_flags Value of various flags present in &cpo +/// @param[in] cpo_val See param docs for @ref replace_termcodes. /// /// @return NULL when there is a problem. -static char *translate_mapping(char *str_in, int cpo_flags) +static char *translate_mapping(const char *const str_in, const char *const cpo_val) { - uint8_t *str = (uint8_t *)str_in; + const uint8_t *str = (const uint8_t *)str_in; garray_T ga; ga_init(&ga, 1, 40); - bool cpo_bslash = cpo_flags & FLAG_CPO_BSLASH; + const bool cpo_bslash = (vim_strchr(cpo_val, CPO_BSLASH) != NULL); for (; *str; str++) { int c = *str; @@ -1378,7 +1367,7 @@ int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***mat continue; } - char *p = translate_mapping(mp->m_keys, CPO_TO_CPO_FLAGS); + char *p = translate_mapping(mp->m_keys, p_cpo); if (p == NULL) { continue; } @@ -1529,7 +1518,6 @@ bool check_abbr(int c, char *ptr, int col, int mincol) : (mp = mp->m_next)) { int qlen = mp->m_keylen; char *q = mp->m_keys; - int match; if (strchr(mp->m_keys, K_SPECIAL) != NULL) { // Might have K_SPECIAL escaped mp->m_keys. @@ -1538,9 +1526,9 @@ bool check_abbr(int c, char *ptr, int col, int mincol) qlen = (int)strlen(q); } // find entries with right mode and keys - match = (mp->m_mode & State) - && qlen == len - && !strncmp(q, ptr, (size_t)len); + int match = (mp->m_mode & State) + && qlen == len + && !strncmp(q, ptr, (size_t)len); if (q != mp->m_keys) { xfree(q); } @@ -1588,7 +1576,7 @@ bool check_abbr(int c, char *ptr, int col, int mincol) } tb[j] = NUL; // insert the last typed char - (void)ins_typebuf((char *)tb, 1, 0, true, mp->m_silent); + ins_typebuf((char *)tb, 1, 0, true, mp->m_silent); } // copy values here, calling eval_map_expr() may make "mp" invalid! @@ -1604,7 +1592,7 @@ bool check_abbr(int c, char *ptr, int col, int mincol) } if (s != NULL) { // insert the to string - (void)ins_typebuf(s, noremap, 0, true, silent); + ins_typebuf(s, noremap, 0, true, silent); // no abbrev. for these chars typebuf.tb_no_abbr_cnt += (int)strlen(s) + j + 1; if (expr) { @@ -1616,7 +1604,7 @@ bool check_abbr(int c, char *ptr, int col, int mincol) tb[1] = NUL; len = clen; // Delete characters instead of bytes while (len-- > 0) { // delete the from string - (void)ins_typebuf((char *)tb, 1, 0, true, silent); + ins_typebuf((char *)tb, 1, 0, true, silent); } return true; } @@ -1653,7 +1641,7 @@ char *eval_map_expr(mapblock_T *mp, int c) if (mp->m_luaref != LUA_NOREF) { Error err = ERROR_INIT; Array args = ARRAY_DICT_INIT; - Object ret = nlua_call_ref(mp->m_luaref, NULL, args, true, &err); + Object ret = nlua_call_ref(mp->m_luaref, NULL, args, kRetObject, NULL, &err); if (ret.type == kObjectTypeString) { p = string_to_cstr(ret.data.string); } @@ -1678,7 +1666,7 @@ char *eval_map_expr(mapblock_T *mp, int c) char *res = NULL; if (replace_keycodes) { - replace_termcodes(p, strlen(p), &res, 0, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); + replace_termcodes(p, strlen(p), &res, 0, REPTERM_DO_LT, NULL, p_cpo); } else { // Escape K_SPECIAL in the result to be able to use the string as typeahead. res = vim_strsave_escape_ks(p); @@ -2078,12 +2066,14 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// /// @return A Dictionary. static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhsrawalt, - const int buffer_value, const bool abbr, const bool compatible) + const int buffer_value, const bool abbr, const bool compatible, + Arena *arena) FUNC_ATTR_NONNULL_ARG(1) { - Dictionary dict = ARRAY_DICT_INIT; - char *const lhs = str2special_save(mp->m_keys, compatible, !compatible); - char *const mapmode = map_mode_to_chars(mp->m_mode); + Dictionary dict = arena_dict(arena, 19); + char *const lhs = str2special_arena(mp->m_keys, compatible, !compatible, arena); + char *mapmode = arena_alloc(arena, 7, false); + map_mode_to_chars(mp->m_mode, mapmode); int noremap_value; if (compatible) { @@ -2097,36 +2087,37 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs } if (mp->m_luaref != LUA_NOREF) { - PUT(dict, "callback", LUAREF_OBJ(api_new_luaref(mp->m_luaref))); + PUT_C(dict, "callback", LUAREF_OBJ(api_new_luaref(mp->m_luaref))); } else { - PUT(dict, "rhs", STRING_OBJ(compatible - ? cstr_to_string(mp->m_orig_str) - : cstr_as_string(str2special_save(mp->m_str, false, true)))); + String rhs = cstr_as_string(compatible + ? mp->m_orig_str + : str2special_arena(mp->m_str, false, true, arena)); + PUT_C(dict, "rhs", STRING_OBJ(rhs)); } if (mp->m_desc != NULL) { - PUT(dict, "desc", CSTR_TO_OBJ(mp->m_desc)); + PUT_C(dict, "desc", CSTR_AS_OBJ(mp->m_desc)); } - PUT(dict, "lhs", CSTR_AS_OBJ(lhs)); - PUT(dict, "lhsraw", CSTR_TO_OBJ(mp->m_keys)); + PUT_C(dict, "lhs", CSTR_AS_OBJ(lhs)); + PUT_C(dict, "lhsraw", CSTR_AS_OBJ(mp->m_keys)); if (lhsrawalt != NULL) { // Also add the value for the simplified entry. - PUT(dict, "lhsrawalt", CSTR_TO_OBJ(lhsrawalt)); - } - PUT(dict, "noremap", INTEGER_OBJ(noremap_value)); - PUT(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0)); - PUT(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0)); - PUT(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0)); - PUT(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid)); - PUT(dict, "scriptversion", INTEGER_OBJ(1)); - PUT(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum)); - PUT(dict, "buffer", INTEGER_OBJ(buffer_value)); - PUT(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0)); + PUT_C(dict, "lhsrawalt", CSTR_AS_OBJ(lhsrawalt)); + } + PUT_C(dict, "noremap", INTEGER_OBJ(noremap_value)); + PUT_C(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0)); + PUT_C(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0)); + PUT_C(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0)); + PUT_C(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid)); + PUT_C(dict, "scriptversion", INTEGER_OBJ(1)); + PUT_C(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum)); + PUT_C(dict, "buffer", INTEGER_OBJ(buffer_value)); + PUT_C(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0)); if (mp->m_replace_keycodes) { - PUT(dict, "replace_keycodes", INTEGER_OBJ(1)); + PUT_C(dict, "replace_keycodes", INTEGER_OBJ(1)); } - PUT(dict, "mode", CSTR_AS_OBJ(mapmode)); - PUT(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0)); - PUT(dict, "mode_bits", INTEGER_OBJ(mp->m_mode)); + PUT_C(dict, "mode", CSTR_AS_OBJ(mapmode)); + PUT_C(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0)); + PUT_C(dict, "mode_bits", INTEGER_OBJ(mp->m_mode)); return dict; } @@ -2169,7 +2160,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) const int mode = get_map_mode((char **)&which, 0); char *keys_simplified = replace_termcodes(keys, strlen(keys), &keys_buf, 0, - flags, &did_simplify, CPO_TO_CPO_FLAGS); + flags, &did_simplify, p_cpo); mapblock_T *mp = NULL; int buffer_local; LuaRef rhs_lua; @@ -2178,8 +2169,8 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (did_simplify) { // When the lhs is being simplified the not-simplified keys are // preferred for printing, like in do_map(). - (void)replace_termcodes(keys, strlen(keys), &alt_keys_buf, 0, - flags | REPTERM_NO_SIMPLIFY, NULL, CPO_TO_CPO_FLAGS); + replace_termcodes(keys, strlen(keys), &alt_keys_buf, 0, + flags | REPTERM_NO_SIMPLIFY, NULL, p_cpo); rhs = check_map(alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); } @@ -2192,16 +2183,16 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) rettv->vval.v_string = str2special_save(rhs, false, false); } } else if (rhs_lua != LUA_NOREF) { - rettv->vval.v_string = nlua_funcref_str(mp->m_luaref); + rettv->vval.v_string = nlua_funcref_str(mp->m_luaref, NULL); } } else { // Return a dictionary. if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) { - Dictionary dict = mapblock_fill_dict(mp, - did_simplify ? keys_simplified : NULL, - buffer_local, abbr, true); - (void)object_to_vim(DICTIONARY_OBJ(dict), rettv, NULL); - api_free_dictionary(dict); + Arena arena = ARENA_EMPTY; + Dictionary dict = mapblock_fill_dict(mp, did_simplify ? keys_simplified : NULL, + buffer_local, abbr, true, &arena); + object_to_vim_take_luaref(&DICTIONARY_OBJ(dict), rettv, true, NULL); + arena_mem_free(arena_finish(&arena)); } else { // Return an empty dictionary. tv_dict_alloc_ret(rettv); @@ -2314,13 +2305,13 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) LuaRef rhs_lua = LUA_NOREF; dictitem_T *callback_di = tv_dict_find(d, S_LEN("callback")); if (callback_di != NULL) { - Object callback_obj = vim_to_object(&callback_di->di_tv); - if (callback_obj.type == kObjectTypeLuaRef && callback_obj.data.luaref != LUA_NOREF) { - rhs_lua = callback_obj.data.luaref; - orig_rhs = ""; - callback_obj.data.luaref = LUA_NOREF; + if (callback_di->di_tv.v_type == VAR_FUNC) { + ufunc_T *fp = find_func(callback_di->di_tv.vval.v_string); + if (fp != NULL && (fp->uf_flags & FC_LUAREF)) { + rhs_lua = api_new_luaref(fp->uf_luaref); + orig_rhs = ""; + } } - api_free_object(callback_obj); } if (lhs == NULL || lhsraw == NULL || orig_rhs == NULL) { emsg(_(e_entries_missing_in_mapset_dict_argument)); @@ -2344,14 +2335,14 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool buffer = tv_dict_get_number(d, "buffer") != 0; // mode from the dict is not used - set_maparg_rhs(orig_rhs, strlen(orig_rhs), rhs_lua, sid, CPO_TO_CPO_FLAGS, &args); + set_maparg_rhs(orig_rhs, strlen(orig_rhs), rhs_lua, sid, p_cpo, &args); mapblock_T **map_table = buffer ? curbuf->b_maphash : maphash; mapblock_T **abbr_table = buffer ? &curbuf->b_first_abbr : &first_abbr; // Delete any existing mapping for this lhs and mode. MapArguments unmap_args = MAP_ARGUMENTS_INIT; - set_maparg_lhs_rhs(lhs, strlen(lhs), "", 0, LUA_NOREF, 0, &unmap_args); + set_maparg_lhs_rhs(lhs, strlen(lhs), "", 0, LUA_NOREF, p_cpo, &unmap_args); unmap_args.buffer = buffer; buf_do_map(MAPTYPE_UNMAP, &unmap_args, mode, is_abbr, curbuf); xfree(unmap_args.rhs); @@ -2399,19 +2390,18 @@ void f_maplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char *keys_buf = NULL; bool did_simplify = false; - char *lhs = str2special_save(mp->m_keys, true, false); - (void)replace_termcodes(lhs, strlen(lhs), &keys_buf, 0, flags, &did_simplify, - CPO_TO_CPO_FLAGS); - xfree(lhs); + Arena arena = ARENA_EMPTY; + char *lhs = str2special_arena(mp->m_keys, true, false, &arena); + replace_termcodes(lhs, strlen(lhs), &keys_buf, 0, flags, &did_simplify, + p_cpo); - Dictionary dict = mapblock_fill_dict(mp, - did_simplify ? keys_buf : NULL, - buffer_local, abbr, true); + Dictionary dict = mapblock_fill_dict(mp, did_simplify ? keys_buf : NULL, + buffer_local, abbr, true, &arena); typval_T d = TV_INITIAL_VALUE; - (void)object_to_vim(DICTIONARY_OBJ(dict), &d, NULL); + object_to_vim_take_luaref(&DICTIONARY_OBJ(dict), &d, true, NULL); assert(d.v_type == VAR_DICT); tv_list_append_dict(rettv->vval.v_list, d.vval.v_dict); - api_free_dictionary(dict); + arena_mem_free(arena_finish(&arena)); xfree(keys_buf); } } @@ -2441,7 +2431,7 @@ void f_mapcheck(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) void add_map(char *lhs, char *rhs, int mode, bool buffer) { MapArguments args = MAP_ARGUMENTS_INIT; - set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, 0, &args); + set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, p_cpo, &args); args.buffer = buffer; buf_do_map(MAPTYPE_NOREMAP, &args, mode, false, curbuf); @@ -2721,7 +2711,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod if (!set_maparg_lhs_rhs(lhs.data, lhs.size, rhs.data, rhs.size, lua_funcref, - CPO_TO_CPO_FLAGS, &parsed_args)) { + p_cpo, &parsed_args)) { api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data); goto fail_and_free; } @@ -2813,20 +2803,28 @@ fail_and_free: /// @param mode The abbreviation for the mode /// @param buf The buffer to get the mapping array. NULL for global /// @returns Array of maparg()-like dictionaries describing mappings -ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf) +ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, Arena *arena) { - Array mappings = ARRAY_DICT_INIT; + ArrayBuilder mappings = KV_INITIAL_VALUE; + kvi_init(mappings); - // Convert the string mode to the integer mode - // that is stored within each mapblock - char *p = mode.data; - int int_mode = get_map_mode(&p, 0); + char *p = mode.size > 0 ? mode.data : "m"; + bool forceit = *p == '!'; + // Convert the string mode to the integer mode stored within each mapblock. + int int_mode = get_map_mode(&p, forceit); + if (forceit) { + assert(p == mode.data); + p++; + } + bool is_abbrev = (int_mode & (MODE_INSERT | MODE_CMDLINE)) != 0 && *p == 'a'; // Determine the desired buffer value int buffer_value = (buf == NULL) ? 0 : buf->handle; - for (int i = 0; i < MAX_MAPHASH; i++) { - for (const mapblock_T *current_maphash = get_maphash(i, buf); + for (int i = 0; i < (is_abbrev ? 1 : MAX_MAPHASH); i++) { + for (const mapblock_T *current_maphash = is_abbrev + ? (buf ? buf->b_first_abbr : first_abbr) + : (buf ? buf->b_maphash[i] : maphash[i]); current_maphash; current_maphash = current_maphash->m_next) { if (current_maphash->m_simplified) { @@ -2834,12 +2832,11 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf) } // Check for correct mode if (int_mode & current_maphash->m_mode) { - ADD(mappings, - DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, - buffer_value, false, false))); + kvi_push(mappings, DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, buffer_value, + is_abbrev, false, arena))); } } } - return mappings; + return arena_take_arraybuilder(arena, &mappings); } diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h index ffe7ab4290..b82117ea86 100644 --- a/src/nvim/mapping.h +++ b/src/nvim/mapping.h @@ -8,11 +8,15 @@ #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep -#include "nvim/mapping_defs.h" // IWYU pragma: export +#include "nvim/mapping_defs.h" // IWYU pragma: keep #include "nvim/option_defs.h" // IWYU pragma: keep #include "nvim/regexp_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "mapping.h.generated.h" +#endif + /// Used for the first argument of do_map() enum { MAPTYPE_MAP = 0, @@ -20,6 +24,23 @@ enum { MAPTYPE_NOREMAP = 2, }; -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "mapping.h.generated.h" -#endif +/// Adjust chars in a language according to 'langmap' option. +/// NOTE that there is no noticeable overhead if 'langmap' is not set. +/// When set the overhead for characters < 256 is small. +/// Don't apply 'langmap' if the character comes from the Stuff buffer or from a +/// mapping and the langnoremap option was set. +/// The do-while is just to ignore a ';' after the macro. +#define LANGMAP_ADJUST(c, condition) \ + do { \ + if (*p_langmap \ + && (condition) \ + && (p_lrm || (vgetc_busy ? typebuf_maplen() == 0 : KeyTyped)) \ + && !KeyStuffed \ + && (c) >= 0) \ + { \ + if ((c) < 256) \ + c = langmap_mapchar[c]; \ + else \ + c = langmap_adjust_mb(c); \ + } \ + } while (0) diff --git a/src/nvim/mapping_defs.h b/src/nvim/mapping_defs.h index 6691c5ac3b..05b8aef23a 100644 --- a/src/nvim/mapping_defs.h +++ b/src/nvim/mapping_defs.h @@ -3,10 +3,8 @@ #include <stdbool.h> #include "nvim/eval/typval_defs.h" -#include "nvim/types_defs.h" -/// Maximum length of key sequence to be mapped. -enum { MAXMAPLEN = 50, }; +enum { MAXMAPLEN = 50, }; ///< Maximum length of key sequence to be mapped. /// Structure used for mappings and abbreviations. typedef struct mapblock mapblock_T; diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 5839cf7a2e..34e35a8277 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -2,6 +2,7 @@ #include <assert.h> #include <limits.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <string.h> @@ -14,28 +15,35 @@ #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/extmark.h" #include "nvim/extmark_defs.h" #include "nvim/fold.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" -#include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" +#include "nvim/os/time.h" +#include "nvim/os/time_defs.h" #include "nvim/path.h" +#include "nvim/pos_defs.h" #include "nvim/quickfix.h" #include "nvim/strings.h" #include "nvim/textobject.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" // This file contains routines to maintain and manipulate marks. @@ -926,8 +934,8 @@ void ex_delmarks(exarg_T *eap) // clear specified marks only const Timestamp timestamp = os_time(); for (char *p = eap->arg; *p != NUL; p++) { - int lower = ASCII_ISLOWER(*p); - int digit = ascii_isdigit(*p); + bool lower = ASCII_ISLOWER(*p); + bool digit = ascii_isdigit(*p); if (lower || digit || ASCII_ISUPPER(*p)) { if (p[1] == '-') { // clear range of marks @@ -1288,12 +1296,12 @@ void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount if (posp->lnum == lnum && posp->col >= mincol) { \ posp->lnum += lnum_amount; \ assert(col_amount > INT_MIN && col_amount <= INT_MAX); \ - if (col_amount < 0 && posp->col <= (colnr_T) - col_amount) { \ + if (col_amount < 0 && posp->col <= -col_amount) { \ posp->col = 0; \ } else if (posp->col < spaces_removed) { \ - posp->col = (int)col_amount + spaces_removed; \ + posp->col = col_amount + spaces_removed; \ } else { \ - posp->col += (colnr_T)col_amount; \ + posp->col += col_amount; \ } \ } \ } diff --git a/src/nvim/mark.h b/src/nvim/mark.h index 3237ae541e..c3661e2e22 100644 --- a/src/nvim/mark.h +++ b/src/nvim/mark.h @@ -1,20 +1,50 @@ #pragma once -#include <stdbool.h> -#include <stddef.h> +#include <locale.h> #include "nvim/ascii_defs.h" -#include "nvim/buffer_defs.h" -#include "nvim/ex_cmds_defs.h" -#include "nvim/extmark_defs.h" +#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep +#include "nvim/extmark_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" #include "nvim/macros_defs.h" -#include "nvim/mark_defs.h" // IWYU pragma: export -#include "nvim/memory.h" +#include "nvim/mark_defs.h" // IWYU pragma: keep #include "nvim/os/time.h" -#include "nvim/pos_defs.h" -/// Set fmark using given value +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "mark.h.generated.h" +#endif + +static inline int mark_global_index(char name) + REAL_FATTR_CONST; +/// Convert mark name to the offset +static inline int mark_global_index(const char name) +{ + return (ASCII_ISUPPER(name) + ? (name - 'A') + : (ascii_isdigit(name) + ? (NMARKS + (name - '0')) + : -1)); +} + +static inline int mark_local_index(char name) + REAL_FATTR_CONST; +/// Convert local mark name to the offset +static inline int mark_local_index(const char name) +{ + return (ASCII_ISLOWER(name) + ? (name - 'a') + : (name == '"' + ? NMARKS + : (name == '^' + ? NMARKS + 1 + : (name == '.' + ? NMARKS + 2 + : -1)))); +} + +/// Global marks (marks with file number or name) +EXTERN xfmark_T namedfm[NGLOBALMARKS] INIT( = { 0 }); + #define SET_FMARK(fmarkp_, mark_, fnum_, view_) \ do { \ fmark_T *const fmarkp__ = fmarkp_; \ @@ -49,74 +79,3 @@ xfmarkp__->fname = fname_; \ SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_, view_); \ } while (0) - -/// Convert mark name to the offset -static inline int mark_global_index(const char name) - FUNC_ATTR_CONST -{ - return (ASCII_ISUPPER(name) - ? (name - 'A') - : (ascii_isdigit(name) - ? (NMARKS + (name - '0')) - : -1)); -} - -/// Convert local mark name to the offset -static inline int mark_local_index(const char name) - FUNC_ATTR_CONST -{ - return (ASCII_ISLOWER(name) - ? (name - 'a') - : (name == '"' - ? NMARKS - : (name == '^' - ? NMARKS + 1 - : (name == '.' - ? NMARKS + 2 - : -1)))); -} - -static inline bool lt(pos_T a, pos_T b) - REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool equalpos(pos_T a, pos_T b) - REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool ltoreq(pos_T a, pos_T b) - REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline void clearpos(pos_T *a) - REAL_FATTR_ALWAYS_INLINE; - -/// Return true if position a is before (less than) position b. -static inline bool lt(pos_T a, pos_T b) -{ - if (a.lnum != b.lnum) { - return a.lnum < b.lnum; - } else if (a.col != b.col) { - return a.col < b.col; - } else { - return a.coladd < b.coladd; - } -} - -/// Return true if position a and b are equal. -static inline bool equalpos(pos_T a, pos_T b) -{ - return (a.lnum == b.lnum) && (a.col == b.col) && (a.coladd == b.coladd); -} - -/// Return true if position a is less than or equal to b. -static inline bool ltoreq(pos_T a, pos_T b) -{ - return lt(a, b) || equalpos(a, b); -} - -/// Clear the pos_T structure pointed to by a. -static inline void clearpos(pos_T *a) -{ - a->lnum = 0; - a->col = 0; - a->coladd = 0; -} - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "mark.h.generated.h" -#endif diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h index 67532d030f..98bdb6ee04 100644 --- a/src/nvim/mark_defs.h +++ b/src/nvim/mark_defs.h @@ -2,7 +2,6 @@ #include "nvim/eval/typval_defs.h" #include "nvim/os/time_defs.h" -#include "nvim/pos_defs.h" // marks: positions in a file // (a normal mark is a lnum/col pair, the same as a file position) @@ -59,7 +58,7 @@ typedef enum { #define TAGSTACKSIZE 20 /// Represents view in which the mark was created -typedef struct fmarkv { +typedef struct { linenr_T topline_offset; ///< Amount of lines from the mark lnum to the top of the window. ///< Use MAXLNUM to indicate that the mark does not have a view. } fmarkv_T; @@ -67,7 +66,7 @@ typedef struct fmarkv { #define INIT_FMARKV { MAXLNUM } /// Structure defining single local mark -typedef struct filemark { +typedef struct { pos_T mark; ///< Cursor position. int fnum; ///< File number. Timestamp timestamp; ///< Time when this mark was last set. @@ -78,12 +77,50 @@ typedef struct filemark { #define INIT_FMARK { { 0, 0, 0 }, 0, 0, INIT_FMARKV, NULL } /// Structure defining extended mark (mark with file name attached) -typedef struct xfilemark { +typedef struct { fmark_T fmark; ///< Actual mark. char *fname; ///< File name, used when fnum == 0. } xfmark_T; #define INIT_XFMARK { INIT_FMARK, NULL } -/// Global marks (marks with file number or name) -EXTERN xfmark_T namedfm[NGLOBALMARKS] INIT( = { 0 }); +/// Set fmark using given value +static inline bool lt(pos_T a, pos_T b) + REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; +/// Return true if position a is before (less than) position b. +static inline bool lt(pos_T a, pos_T b) +{ + if (a.lnum != b.lnum) { + return a.lnum < b.lnum; + } else if (a.col != b.col) { + return a.col < b.col; + } else { + return a.coladd < b.coladd; + } +} + +static inline bool equalpos(pos_T a, pos_T b) + REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; +/// Return true if position a and b are equal. +static inline bool equalpos(pos_T a, pos_T b) +{ + return (a.lnum == b.lnum) && (a.col == b.col) && (a.coladd == b.coladd); +} + +static inline bool ltoreq(pos_T a, pos_T b) + REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; +/// Return true if position a is less than or equal to b. +static inline bool ltoreq(pos_T a, pos_T b) +{ + return lt(a, b) || equalpos(a, b); +} + +static inline void clearpos(pos_T *a) + REAL_FATTR_ALWAYS_INLINE; +/// Clear the pos_T structure pointed to by a. +static inline void clearpos(pos_T *a) +{ + a->lnum = 0; + a->col = 0; + a->coladd = 0; +} diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index b555b3b4ae..0ebebf409e 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -47,21 +47,22 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "klib/kvec.h" -#include "nvim/garray.h" +#include "nvim/macros_defs.h" +#include "nvim/map_defs.h" #include "nvim/marktree.h" #include "nvim/memory.h" #include "nvim/pos_defs.h" // only for debug functions #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/garray.h" #include "nvim/garray_defs.h" -#include "nvim/macros_defs.h" #define T MT_BRANCH_FACTOR -#define ILEN (sizeof(MTNode) + (2 * T) * sizeof(void *)) +#define ILEN (sizeof(MTNode) + sizeof(struct mtnode_inner_s)) #define ID_INCR (((uint64_t)1) << 2) @@ -146,7 +147,8 @@ static inline int marktree_getp_aux(const MTNode *x, MTKey k, bool *match) bool dummy_match; bool *m = match ? match : &dummy_match; - int begin = 0, end = x->n; + int begin = 0; + int end = x->n; if (x->n == 0) { *m = false; return -1; @@ -179,6 +181,8 @@ static MTNode *id2node(MarkTree *b, uint64_t id) return pmap_get(uint64_t)(b->id2node, id); } +#define ptr s->i_ptr +#define meta s->i_meta // put functions // x must be an internal node, which is not full @@ -224,15 +228,17 @@ static inline void split_node(MarkTree *b, MTNode *x, const int i, MTKey next) } if (y->level) { memcpy(z->ptr, &y->ptr[T], sizeof(MTNode *) * T); + memcpy(z->meta, &y->meta[T], sizeof(z->meta[0]) * T); for (int j = 0; j < T; j++) { z->ptr[j]->parent = z; z->ptr[j]->p_idx = (int16_t)j; } } y->n = T - 1; - memmove(&x->ptr[i + 2], &x->ptr[i + 1], - sizeof(MTNode *) * (size_t)(x->n - i)); + memmove(&x->ptr[i + 2], &x->ptr[i + 1], sizeof(MTNode *) * (size_t)(x->n - i)); + memmove(&x->meta[i + 2], &x->meta[i + 1], sizeof(x->meta[0]) * (size_t)(x->n - i)); x->ptr[i + 1] = z; + meta_describe_node(x->meta[i + 1], z); z->parent = x; // == y->parent for (int j = i + 1; j < x->n + 2; j++) { x->ptr[j]->p_idx = (int16_t)j; @@ -244,6 +250,13 @@ static inline void split_node(MarkTree *b, MTNode *x, const int i, MTKey next) refkey(b, x, i); x->n++; + uint32_t meta_inc[4]; + meta_describe_key(meta_inc, x->key[i]); + for (int m = 0; m < kMTMetaCount; m++) { + // y used contain all of z and x->key[i], discount those + x->meta[i][m] -= (x->meta[i + 1][m] + meta_inc[m]); + } + for (int j = 0; j < T - 1; j++) { relative(x->key[i].pos, &z->key[j].pos); } @@ -260,7 +273,7 @@ static inline void split_node(MarkTree *b, MTNode *x, const int i, MTKey next) } // x must not be a full node (even if there might be internal space) -static inline void marktree_putp_aux(MarkTree *b, MTNode *x, MTKey k) +static inline void marktree_putp_aux(MarkTree *b, MTNode *x, MTKey k, uint32_t *meta_inc) { // TODO(bfredl): ugh, make sure this is the _last_ valid (pos, gravity) position, // to minimize movement @@ -283,7 +296,10 @@ static inline void marktree_putp_aux(MarkTree *b, MTNode *x, MTKey k) if (i > 0) { relative(x->key[i - 1].pos, &k.pos); } - marktree_putp_aux(b, x->ptr[i], k); + marktree_putp_aux(b, x->ptr[i], k, meta_inc); + for (int m = 0; m < kMTMetaCount; m++) { + x->meta[i][m] += meta_inc[m]; + } } } @@ -303,7 +319,8 @@ void marktree_put(MarkTree *b, MTKey key, int end_row, int end_col, bool end_rig |(uint16_t)(end_right ? MT_FLAG_RIGHT_GRAVITY : 0)); end_key.pos = (MTPos){ end_row, end_col }; marktree_put_key(b, end_key); - MarkTreeIter itr[1] = { 0 }, end_itr[1] = { 0 }; + MarkTreeIter itr[1] = { 0 }; + MarkTreeIter end_itr[1] = { 0 }; marktree_lookup(b, mt_lookup_key(key), itr); marktree_lookup(b, mt_lookup_key(end_key), end_itr); @@ -413,7 +430,7 @@ void marktree_intersect_pair(MarkTree *b, uint64_t id, MarkTreeIter *itr, MarkTr } } } - marktree_itr_next_skip(b, itr, skip, true, NULL); + marktree_itr_next_skip(b, itr, skip, true, NULL, NULL); } #undef iat } @@ -426,24 +443,75 @@ static MTNode *marktree_alloc_node(MarkTree *b, bool internal) return x; } +// really meta_inc[kMTMetaCount] +static void meta_describe_key_inc(uint32_t *meta_inc, MTKey *k) +{ + if (!mt_end(*k)) { + meta_inc[kMTMetaInline] += (k->flags & MT_FLAG_DECOR_VIRT_TEXT_INLINE) ? 1 : 0; + meta_inc[kMTMetaLines] += (k->flags & MT_FLAG_DECOR_VIRT_LINES) ? 1 : 0; + meta_inc[kMTMetaSignHL] += (k->flags & MT_FLAG_DECOR_SIGNHL) ? 1 : 0; + meta_inc[kMTMetaSignText] += (k->flags & MT_FLAG_DECOR_SIGNTEXT) ? 1 : 0; + } +} + +static void meta_describe_key(uint32_t *meta_inc, MTKey k) +{ + memset(meta_inc, 0, kMTMetaCount * sizeof(*meta_inc)); + meta_describe_key_inc(meta_inc, &k); +} + +// if x is internal, asumes x->meta[..] of children are correct +static void meta_describe_node(uint32_t *meta_node, MTNode *x) +{ + memset(meta_node, 0, kMTMetaCount * sizeof(meta_node[0])); + for (int i = 0; i < x->n; i++) { + meta_describe_key_inc(meta_node, &x->key[i]); + } + if (x->level) { + for (int i = 0; i < x->n + 1; i++) { + for (int m = 0; m < kMTMetaCount; m++) { + meta_node[m] += x->meta[i][m]; + } + } + } +} + +static bool meta_has(const uint32_t *meta_count, MetaFilter meta_filter) +{ + uint32_t count = 0; + for (int m = 0; m < kMTMetaCount; m++) { + count += meta_count[m] & meta_filter[m]; + } + return count > 0; +} + void marktree_put_key(MarkTree *b, MTKey k) { k.flags |= MT_FLAG_REAL; // let's be real. if (!b->root) { b->root = marktree_alloc_node(b, true); } - b->n_keys++; MTNode *r = b->root; if (r->n == 2 * T - 1) { MTNode *s = marktree_alloc_node(b, true); b->root = s; s->level = r->level + 1; s->n = 0; s->ptr[0] = r; + for (int m = 0; m < kMTMetaCount; m++) { + s->meta[0][m] = b->meta_root[m]; + } r->parent = s; r->p_idx = 0; split_node(b, s, 0, k); r = s; } - marktree_putp_aux(b, r, k); + + uint32_t meta_inc[4]; + meta_describe_key(meta_inc, k); + marktree_putp_aux(b, r, k, meta_inc); + for (int m = 0; m < 4; m++) { + b->meta_root[m] += meta_inc[m]; + } + b->n_keys++; } /// INITIATING DELETION PROTOCOL: @@ -495,6 +563,7 @@ uint64_t marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) } } + // 2. if (itr->x->level) { if (rev) { abort(); @@ -509,6 +578,9 @@ uint64_t marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) MTNode *x = itr->x; assert(x->level == 0); MTKey intkey = x->key[itr->i]; + + uint32_t meta_inc[4]; + meta_describe_key(meta_inc, intkey); if (x->n > itr->i + 1) { memmove(&x->key[itr->i], &x->key[itr->i + 1], sizeof(MTKey) * (size_t)(x->n - itr->i - 1)); @@ -554,11 +626,16 @@ uint64_t marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) } } + for (int m = 0; m < kMTMetaCount; m++) { + p->meta[lnode->p_idx][m] -= meta_inc[m]; + } + lnode = p; ilvl--; } while (lnode != cur); MTKey deleted = cur->key[curi]; + meta_describe_key(meta_inc, deleted); cur->key[curi] = intkey; refkey(b, cur, curi); // if `did_bubble` then we already added `start_id` to some parent @@ -583,6 +660,20 @@ uint64_t marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) itr->i--; } + MTNode *lnode = cur; + while (lnode->parent) { + uint32_t *meta_p = lnode->parent->meta[lnode->p_idx]; + for (int m = 0; m < kMTMetaCount; m++) { + meta_p[m] -= meta_inc[m]; + } + + lnode = lnode->parent; + } + for (int m = 0; m < kMTMetaCount; m++) { + assert(b->meta_root[m] >= meta_inc[m]); + b->meta_root[m] -= meta_inc[m]; + } + // 5. bool itr_dirty = false; int rlvl = itr->lvl - 1; @@ -643,6 +734,10 @@ uint64_t marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) if (b->root->level) { MTNode *oldroot = b->root; b->root = b->root->ptr[0]; + for (int m = 0; m < kMTMetaCount; m++) { + assert(b->meta_root[m] == oldroot->meta[0][m]); + } + b->root->parent = NULL; marktree_free_node(b, oldroot); } else { @@ -679,13 +774,44 @@ uint64_t marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) return other; } +void marktree_revise_flags(MarkTree *b, MarkTreeIter *itr, uint16_t new_flags) +{ + uint32_t meta_old[4]; + meta_describe_key(meta_old, rawkey(itr)); + rawkey(itr).flags &= (uint16_t) ~MT_FLAG_EXTERNAL_MASK; + rawkey(itr).flags |= new_flags; + + uint32_t meta_new[4]; + meta_describe_key(meta_new, rawkey(itr)); + + if (!memcmp(meta_old, meta_new, sizeof(meta_old))) { + return; + } + + MTNode *lnode = itr->x; + while (lnode->parent) { + uint32_t *meta_p = lnode->parent->meta[lnode->p_idx]; + for (int m = 0; m < kMTMetaCount; m++) { + meta_p[m] += meta_new[m] - meta_old[m]; + } + + lnode = lnode->parent; + } + + for (int m = 0; m < kMTMetaCount; m++) { + b->meta_root[m] += meta_new[m] - meta_old[m]; + } +} + /// similar to intersect_common but modify x and y in place to retain /// only the items which are NOT in common static void intersect_merge(Intersection *restrict m, Intersection *restrict x, Intersection *restrict y) { - size_t xi = 0, yi = 0; - size_t xn = 0, yn = 0; + size_t xi = 0; + size_t yi = 0; + size_t xn = 0; + size_t yn = 0; while (xi < kv_size(*x) && yi < kv_size(*y)) { if (kv_A(*x, xi) == kv_A(*y, yi)) { // TODO(bfredl): kvi_pushp is actually quite complex, break out kvi_resize() to a function? @@ -717,8 +843,10 @@ static void intersect_merge(Intersection *restrict m, Intersection *restrict x, static void intersect_mov(Intersection *restrict x, Intersection *restrict y, Intersection *restrict w, Intersection *restrict d) { - size_t wi = 0, yi = 0; - size_t wn = 0, yn = 0; + size_t wi = 0; + size_t yi = 0; + size_t wn = 0; + size_t yn = 0; size_t xi = 0; while (wi < kv_size(*w) || xi < kv_size(*x)) { if (wi < kv_size(*w) && (xi >= kv_size(*x) || kv_A(*x, xi) >= kv_A(*w, wi))) { @@ -810,7 +938,8 @@ bool intersect_mov_test(const uint64_t *x, size_t nx, const uint64_t *y, size_t /// intersection: i = x & y static void intersect_common(Intersection *i, Intersection *x, Intersection *y) { - size_t xi = 0, yi = 0; + size_t xi = 0; + size_t yi = 0; while (xi < kv_size(*x) && yi < kv_size(*y)) { if (kv_A(*x, xi) == kv_A(*y, yi)) { kvi_push(*i, kv_A(*x, xi)); @@ -827,7 +956,8 @@ static void intersect_common(Intersection *i, Intersection *x, Intersection *y) // inplace union: x |= y static void intersect_add(Intersection *x, Intersection *y) { - size_t xi = 0, yi = 0; + size_t xi = 0; + size_t yi = 0; while (xi < kv_size(*x) && yi < kv_size(*y)) { if (kv_A(*x, xi) == kv_A(*y, yi)) { xi++; @@ -854,7 +984,8 @@ static void intersect_add(Intersection *x, Intersection *y) // inplace asymmetric difference: x &= ~y static void intersect_sub(Intersection *restrict x, Intersection *restrict y) { - size_t xi = 0, yi = 0; + size_t xi = 0; + size_t yi = 0; size_t xn = 0; while (xi < kv_size(*x) && yi < kv_size(*y)) { if (kv_A(*x, xi) == kv_A(*y, yi)) { @@ -898,11 +1029,12 @@ static void bubble_up(MTNode *x) static MTNode *merge_node(MarkTree *b, MTNode *p, int i) { - MTNode *x = p->ptr[i], *y = p->ptr[i + 1]; - Intersection m; - kvi_init(m); + MTNode *x = p->ptr[i]; + MTNode *y = p->ptr[i + 1]; + Intersection mi; + kvi_init(mi); - intersect_merge(&m, &x->intersect, &y->intersect); + intersect_merge(&mi, &x->intersect, &y->intersect); x->key[x->n] = p->key[i]; refkey(b, x, x->n); @@ -910,6 +1042,9 @@ static MTNode *merge_node(MarkTree *b, MTNode *p, int i) relative(p->key[i - 1].pos, &x->key[x->n].pos); } + uint32_t meta_inc[4]; + meta_describe_key(meta_inc, x->key[x->n]); + memmove(&x->key[x->n + 1], y->key, (size_t)y->n * sizeof(MTKey)); for (int k = 0; k < y->n; k++) { refkey(b, x, x->n + 1 + k); @@ -919,6 +1054,7 @@ static MTNode *merge_node(MarkTree *b, MTNode *p, int i) // bubble down: ranges that intersected old-x but not old-y or vice versa // must be moved to their respective children memmove(&x->ptr[x->n + 1], y->ptr, ((size_t)y->n + 1) * sizeof(MTNode *)); + memmove(&x->meta[x->n + 1], y->meta, ((size_t)y->n + 1) * sizeof(y->meta[0])); for (int k = 0; k < x->n + 1; k++) { // TODO(bfredl): dedicated impl for "Z |= Y" for (size_t idx = 0; idx < kv_size(x->intersect); idx++) { @@ -937,9 +1073,14 @@ static MTNode *merge_node(MarkTree *b, MTNode *p, int i) } } x->n += y->n + 1; + for (int m = 0; m < kMTMetaCount; m++) { + // x now contains everything of y plus old p->key[i] + p->meta[i][m] += (p->meta[i + 1][m] + meta_inc[m]); + } + memmove(&p->key[i], &p->key[i + 1], (size_t)(p->n - i - 1) * sizeof(MTKey)); - memmove(&p->ptr[i + 1], &p->ptr[i + 2], - (size_t)(p->n - i - 1) * sizeof(MTKey *)); + memmove(&p->ptr[i + 1], &p->ptr[i + 2], (size_t)(p->n - i - 1) * sizeof(MTKey *)); + memmove(&p->meta[i + 1], &p->meta[i + 2], (size_t)(p->n - i - 1) * sizeof(p->meta[0])); for (int j = i + 1; j < p->n; j++) { // note: one has been deleted p->ptr[j]->p_idx = (int16_t)j; } @@ -950,7 +1091,7 @@ static MTNode *merge_node(MarkTree *b, MTNode *p, int i) // move of a kvec_withinit_t, messy! // TODO(bfredl): special case version of intersect_merge(x_out, x_in_m_out, y) to avoid this - kvi_move(&x->intersect, &m); + kvi_move(&x->intersect, &mi); return x; } @@ -975,20 +1116,39 @@ void kvi_move(Intersection *dest, Intersection *src) // key inside x, if x is the first leaf) static void pivot_right(MarkTree *b, MTPos p_pos, MTNode *p, const int i) { - MTNode *x = p->ptr[i], *y = p->ptr[i + 1]; + MTNode *x = p->ptr[i]; + MTNode *y = p->ptr[i + 1]; memmove(&y->key[1], y->key, (size_t)y->n * sizeof(MTKey)); if (y->level) { memmove(&y->ptr[1], y->ptr, ((size_t)y->n + 1) * sizeof(MTNode *)); + memmove(&y->meta[1], y->meta, ((size_t)y->n + 1) * sizeof(y->meta[0])); for (int j = 1; j < y->n + 2; j++) { y->ptr[j]->p_idx = (int16_t)j; } } + y->key[0] = p->key[i]; refkey(b, y, 0); p->key[i] = x->key[x->n - 1]; refkey(b, p, i); + + uint32_t meta_inc_y[4]; + meta_describe_key(meta_inc_y, y->key[0]); + uint32_t meta_inc_x[4]; + meta_describe_key(meta_inc_x, p->key[i]); + + for (int m = 0; m < kMTMetaCount; m++) { + p->meta[i + 1][m] += meta_inc_y[m]; + p->meta[i][m] -= meta_inc_x[m]; + } + if (x->level) { y->ptr[0] = x->ptr[x->n]; + memcpy(y->meta[0], x->meta[x->n], sizeof(y->meta[0])); + for (int m = 0; m < kMTMetaCount; m++) { + p->meta[i + 1][m] += y->meta[0][m]; + p->meta[i][m] -= y->meta[0][m]; + } y->ptr[0]->parent = y; y->ptr[0]->p_idx = 0; } @@ -1040,7 +1200,8 @@ static void pivot_right(MarkTree *b, MTPos p_pos, MTNode *p, const int i) static void pivot_left(MarkTree *b, MTPos p_pos, MTNode *p, int i) { - MTNode *x = p->ptr[i], *y = p->ptr[i + 1]; + MTNode *x = p->ptr[i]; + MTNode *y = p->ptr[i + 1]; // reverse from how we "always" do it. but pivot_left // is just the inverse of pivot_right, so reverse it literally. @@ -1056,14 +1217,30 @@ static void pivot_left(MarkTree *b, MTPos p_pos, MTNode *p, int i) refkey(b, x, x->n); p->key[i] = y->key[0]; refkey(b, p, i); + + uint32_t meta_inc_x[4]; + meta_describe_key(meta_inc_x, x->key[x->n]); + uint32_t meta_inc_y[4]; + meta_describe_key(meta_inc_y, p->key[i]); + for (int m = 0; m < kMTMetaCount; m++) { + p->meta[i][m] += meta_inc_x[m]; + p->meta[i + 1][m] -= meta_inc_y[m]; + } + if (x->level) { x->ptr[x->n + 1] = y->ptr[0]; + memcpy(x->meta[x->n + 1], y->meta[0], sizeof(y->meta[0])); + for (int m = 0; m < kMTMetaCount; m++) { + p->meta[i + 1][m] -= y->meta[0][m]; + p->meta[i][m] += y->meta[0][m]; + } x->ptr[x->n + 1]->parent = x; x->ptr[x->n + 1]->p_idx = (int16_t)(x->n + 1); } memmove(y->key, &y->key[1], (size_t)(y->n - 1) * sizeof(MTKey)); if (y->level) { memmove(y->ptr, &y->ptr[1], (size_t)y->n * sizeof(MTNode *)); + memmove(y->meta, &y->meta[1], (size_t)y->n * sizeof(y->meta[0])); for (int j = 0; j < y->n; j++) { // note: last item deleted y->ptr[j]->p_idx = (int16_t)j; } @@ -1117,8 +1294,8 @@ void marktree_clear(MarkTree *b) b->root = NULL; } map_destroy(uint64_t, b->id2node); - *b->id2node = (PMap(uint64_t)) MAP_INIT; b->n_keys = 0; + memset(b->meta_root, 0, kMTMetaCount * sizeof(b->meta_root[0])); assert(b->n_nodes == 0); } @@ -1219,11 +1396,11 @@ void marktree_restore_pair(MarkTree *b, MTKey key) bool marktree_itr_get(MarkTree *b, int32_t row, int col, MarkTreeIter *itr) { - return marktree_itr_get_ext(b, MTPos(row, col), itr, false, false, NULL); + return marktree_itr_get_ext(b, MTPos(row, col), itr, false, false, NULL, NULL); } bool marktree_itr_get_ext(MarkTree *b, MTPos p, MarkTreeIter *itr, bool last, bool gravity, - MTPos *oldbase) + MTPos *oldbase, MetaFilter meta_filter) { if (b->n_keys == 0) { itr->x = NULL; @@ -1246,6 +1423,12 @@ bool marktree_itr_get_ext(MarkTree *b, MTPos p, MarkTreeIter *itr, bool last, bo if (itr->x->level == 0) { break; } + if (meta_filter) { + if (!meta_has(itr->x->meta[itr->i], meta_filter)) { + // this takes us to the interal position after the first rejected node + break; + } + } itr->s[itr->lvl].i = itr->i; itr->s[itr->lvl].oldcol = itr->pos.col; @@ -1264,7 +1447,8 @@ bool marktree_itr_get_ext(MarkTree *b, MTPos p, MarkTreeIter *itr, bool last, bo if (last) { return marktree_itr_prev(b, itr); } else if (itr->i >= itr->x->n) { - return marktree_itr_next(b, itr); + // no need for "meta_filter" here, this just goes up one step + return marktree_itr_next_skip(b, itr, true, false, NULL, NULL); } return true; } @@ -1321,16 +1505,21 @@ int marktree_itr_last(MarkTree *b, MarkTreeIter *itr) bool marktree_itr_next(MarkTree *b, MarkTreeIter *itr) { - return marktree_itr_next_skip(b, itr, false, false, NULL); + return marktree_itr_next_skip(b, itr, false, false, NULL, NULL); } static bool marktree_itr_next_skip(MarkTree *b, MarkTreeIter *itr, bool skip, bool preload, - MTPos oldbase[]) + MTPos oldbase[], MetaFilter meta_filter) { if (!itr->x) { return false; } itr->i++; + if (meta_filter && itr->x->level > 0) { + if (!meta_has(itr->x->meta[itr->i], meta_filter)) { + skip = true; + } + } if (itr->x->level == 0 || skip) { if (preload && itr->x->level == 0 && skip) { // skip rest of this leaf node @@ -1372,14 +1561,98 @@ static bool marktree_itr_next_skip(MarkTree *b, MarkTreeIter *itr, bool skip, bo if (preload && itr->x->level) { itr->i = -1; break; - } else { - itr->i = 0; + } + itr->i = 0; + if (meta_filter && itr->x->level) { + if (!meta_has(itr->x->meta[0], meta_filter)) { + // itr->x has filtered keys but x->ptr[0] does not, don't enter the latter + break; + } } } } return true; } +bool marktree_itr_get_filter(MarkTree *b, int32_t row, int col, int stop_row, int stop_col, + MetaFilter meta_filter, MarkTreeIter *itr) +{ + if (!meta_has(b->meta_root, meta_filter)) { + return false; + } + if (!marktree_itr_get_ext(b, MTPos(row, col), itr, false, false, NULL, meta_filter)) { + return false; + } + + return marktree_itr_check_filter(b, itr, stop_row, stop_col, meta_filter); +} + +/// use after marktree_itr_get_overlap() to continue in a filtered fashion +/// +/// not strictly needed but steps out to the right parent node where there +/// might be "start" keys matching the filter ("end" keys are properly handled +/// by marktree_itr_step_overlap() already) +bool marktree_itr_step_out_filter(MarkTree *b, MarkTreeIter *itr, MetaFilter meta_filter) +{ + if (!meta_has(b->meta_root, meta_filter)) { + itr->x = NULL; + return false; + } + + while (itr->x && itr->x->parent) { + if (meta_has(itr->x->parent->meta[itr->x->p_idx], meta_filter)) { + return true; + } + + itr->i = itr->x->n; + + // no filter needed, just reuse the code path for step to parent + marktree_itr_next_skip(b, itr, true, false, NULL, NULL); + } + + return itr->x; +} + +bool marktree_itr_next_filter(MarkTree *b, MarkTreeIter *itr, int stop_row, int stop_col, + MetaFilter meta_filter) +{ + if (!marktree_itr_next_skip(b, itr, false, false, NULL, meta_filter)) { + return false; + } + + return marktree_itr_check_filter(b, itr, stop_row, stop_col, meta_filter); +} + +const uint32_t meta_map[4] = { MT_FLAG_DECOR_VIRT_TEXT_INLINE, MT_FLAG_DECOR_VIRT_LINES, + MT_FLAG_DECOR_SIGNHL, MT_FLAG_DECOR_SIGNTEXT }; +static bool marktree_itr_check_filter(MarkTree *b, MarkTreeIter *itr, int stop_row, int stop_col, + MetaFilter meta_filter) +{ + MTPos stop_pos = MTPos(stop_row, stop_col); + + uint32_t key_filter = 0; + for (int m = 0; m < kMTMetaCount; m++) { + key_filter |= meta_map[m]&meta_filter[m]; + } + + while (true) { + if (pos_leq(stop_pos, marktree_itr_pos(itr))) { + itr->x = NULL; + return false; + } + + MTKey k = rawkey(itr); + if (!mt_end(k) && (k.flags & key_filter)) { + return true; + } + + // this skips subtrees, but not keys, thus the outer loop + if (!marktree_itr_next_skip(b, itr, false, false, NULL, meta_filter)) { + return false; + } + } +} + bool marktree_itr_prev(MarkTree *b, MarkTreeIter *itr) { if (!itr->x) { @@ -1558,7 +1831,7 @@ bool marktree_itr_step_overlap(MarkTree *b, MarkTreeIter *itr, MTPair *pair) } unrelative(itr->pos, &k.pos); MTKey start = marktree_lookup(b, id, NULL); - if (pos_less(itr->intersect_pos, start.pos)) { + if (pos_leq(itr->intersect_pos, start.pos)) { continue; } *pair = mtpair_from(start, k); @@ -1590,6 +1863,33 @@ static void swap_keys(MarkTree *b, MarkTreeIter *itr1, MarkTreeIter *itr2, Damag kvi_push(*damage, ((Damage){ mt_lookup_key(rawkey(itr2)), itr2->x, itr1->x, itr2->i, itr1->i })); } + + uint32_t meta_inc_1[4]; + meta_describe_key(meta_inc_1, rawkey(itr1)); + uint32_t meta_inc_2[4]; + meta_describe_key(meta_inc_2, rawkey(itr2)); + + if (memcmp(meta_inc_1, meta_inc_2, sizeof(meta_inc_1)) != 0) { + MTNode *x1 = itr1->x; + MTNode *x2 = itr2->x; + while (x1 != x2) { + if (x1->level <= x2->level) { + // as the root node uniquely has the highest level, x1 cannot be it + uint32_t *meta_node = x1->parent->meta[x1->p_idx]; + for (int m = 0; m < kMTMetaCount; m++) { + meta_node[m] += meta_inc_2[m] - meta_inc_1[m]; + } + x1 = x1->parent; + } + if (x2->level < x1->level) { + uint32_t *meta_node = x2->parent->meta[x2->p_idx]; + for (int m = 0; m < kMTMetaCount; m++) { + meta_node[m] += meta_inc_1[m] - meta_inc_2[m]; + } + x2 = x2->parent; + } + } + } } MTKey key1 = rawkey(itr1); @@ -1604,7 +1904,8 @@ static void swap_keys(MarkTree *b, MarkTreeIter *itr1, MarkTreeIter *itr2, Damag static int damage_cmp(const void *s1, const void *s2) { - Damage *d1 = (Damage *)s1, *d2 = (Damage *)s2; + Damage *d1 = (Damage *)s1; + Damage *d2 = (Damage *)s2; assert(d1->id != d2->id); return d1->id > d2->id ? 1 : -1; } @@ -1620,11 +1921,12 @@ bool marktree_splice(MarkTree *b, int32_t start_line, int start_col, int old_ext bool same_line = old_extent.row == 0 && new_extent.row == 0; unrelative(start, &old_extent); unrelative(start, &new_extent); - MarkTreeIter itr[1] = { 0 }, enditr[1] = { 0 }; + MarkTreeIter itr[1] = { 0 }; + MarkTreeIter enditr[1] = { 0 }; MTPos oldbase[MT_MAX_DEPTH] = { 0 }; - marktree_itr_get_ext(b, start, itr, false, true, oldbase); + marktree_itr_get_ext(b, start, itr, false, true, oldbase, NULL); if (!itr->x) { // den e FÄRDIG return false; @@ -1637,7 +1939,7 @@ bool marktree_splice(MarkTree *b, int32_t start_line, int start_col, int old_ext if (!pos_leq(old_extent, ipos) || (old_extent.row == ipos.row && old_extent.col == ipos.col && !mt_right(rawkey(itr)))) { - marktree_itr_get_ext(b, old_extent, enditr, true, true, NULL); + marktree_itr_get_ext(b, old_extent, enditr, true, true, NULL, NULL); assert(enditr->x); // "assert" (itr <= enditr) } else { @@ -1692,7 +1994,7 @@ continue_same_node: oldbase[itr->lvl + 1] = rawkey(itr).pos; unrelative(oldbase[itr->lvl], &oldbase[itr->lvl + 1]); rawkey(itr).pos = loc_start; - marktree_itr_next_skip(b, itr, false, false, oldbase); + marktree_itr_next_skip(b, itr, false, false, oldbase, NULL); } else { rawkey(itr).pos = loc_start; if (itr->i < itr->x->n - 1) { @@ -1725,7 +2027,7 @@ past_continue_same_node: oldbase[itr->lvl + 1] = oldpos; unrelative(oldbase[itr->lvl], &oldbase[itr->lvl + 1]); - marktree_itr_next_skip(b, itr, false, false, oldbase); + marktree_itr_next_skip(b, itr, false, false, oldbase, NULL); } else { if (itr->i < itr->x->n - 1) { itr->i++; @@ -1760,7 +2062,7 @@ past_continue_same_node: if (done) { break; } - marktree_itr_next_skip(b, itr, true, false, NULL); + marktree_itr_next_skip(b, itr, true, false, NULL, NULL); } if (kv_size(damage)) { @@ -1825,11 +2127,12 @@ past_continue_same_node: void marktree_move_region(MarkTree *b, int start_row, colnr_T start_col, int extent_row, colnr_T extent_col, int new_row, colnr_T new_col) { - MTPos start = { start_row, start_col }, size = { extent_row, extent_col }; + MTPos start = { start_row, start_col }; + MTPos size = { extent_row, extent_col }; MTPos end = size; unrelative(start, &end); MarkTreeIter itr[1] = { 0 }; - marktree_itr_get_ext(b, start, itr, false, true, NULL); + marktree_itr_get_ext(b, start, itr, false, true, NULL, NULL); kvec_t(MTKey) saved = KV_INITIAL_VALUE; while (itr->x) { MTKey k = marktree_itr_current(itr); @@ -1957,13 +2260,10 @@ MTPos marktree_get_altpos(MarkTree *b, MTKey mark, MarkTreeIter *itr) return marktree_get_alt(b, mark, itr).pos; } +/// @return alt mark for a paired mark or mark itself for unpaired mark MTKey marktree_get_alt(MarkTree *b, MTKey mark, MarkTreeIter *itr) { - MTKey end = MT_INVALID_KEY; - if (mt_paired(mark)) { - end = marktree_lookup_ns(b, mark.ns, mark.id, !mt_end(mark), itr); - } - return end; + return mt_paired(mark) ? marktree_lookup_ns(b, mark.ns, mark.id, !mt_end(mark), itr) : mark; } static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr) @@ -1984,9 +2284,12 @@ static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr) // for unit test void marktree_put_test(MarkTree *b, uint32_t ns, uint32_t id, int row, int col, bool right_gravity, - int end_row, int end_col, bool end_right) + int end_row, int end_col, bool end_right, bool meta_inline) { - uint16_t flags = mt_flags(right_gravity, false, false, false); + uint16_t flags = mt_flags(right_gravity, false, false, false, false); + // The specific choice is irrelevant here, we pick one counted decor + // type to test the counting and filtering logic. + flags |= meta_inline ? MT_FLAG_DECOR_VIRT_TEXT_INLINE : 0; MTKey key = { { row, col }, ns, id, flags, { .hl = DECOR_HIGHLIGHT_INLINE_INIT } }; marktree_put(b, key, end_row, end_col, end_right); } @@ -2021,7 +2324,8 @@ void marktree_check(MarkTree *b) MTPos dummy; bool last_right = false; - size_t nkeys = marktree_check_node(b, b->root, &dummy, &last_right); + + size_t nkeys = marktree_check_node(b, b->root, &dummy, &last_right, b->meta_root); assert(b->n_keys == nkeys); assert(b->n_keys == map_size(b->id2node)); #else @@ -2031,7 +2335,8 @@ void marktree_check(MarkTree *b) } #ifndef NDEBUG -size_t marktree_check_node(MarkTree *b, MTNode *x, MTPos *last, bool *last_right) +size_t marktree_check_node(MarkTree *b, MTNode *x, MTPos *last, bool *last_right, + const uint32_t *meta_node_ref) { assert(x->n <= 2 * T - 1); // TODO(bfredl): too strict if checking "in repair" post-delete tree. @@ -2040,7 +2345,7 @@ size_t marktree_check_node(MarkTree *b, MTNode *x, MTPos *last, bool *last_right for (int i = 0; i < x->n; i++) { if (x->level) { - n_keys += marktree_check_node(b, x->ptr[i], last, last_right); + n_keys += marktree_check_node(b, x->ptr[i], last, last_right, x->meta[i]); } else { *last = (MTPos) { 0, 0 }; } @@ -2057,7 +2362,7 @@ size_t marktree_check_node(MarkTree *b, MTNode *x, MTPos *last, bool *last_right } if (x->level) { - n_keys += marktree_check_node(b, x->ptr[x->n], last, last_right); + n_keys += marktree_check_node(b, x->ptr[x->n], last, last_right, x->meta[x->n]); unrelative(x->key[x->n - 1].pos, last); for (int i = 0; i < x->n + 1; i++) { @@ -2072,6 +2377,13 @@ size_t marktree_check_node(MarkTree *b, MTNode *x, MTPos *last, bool *last_right } else if (x->n > 0) { *last = x->key[x->n - 1].pos; } + + uint32_t meta_node[4]; + meta_describe_node(meta_node, x); + for (int m = 0; m < kMTMetaCount; m++) { + assert(meta_node_ref[m] == meta_node[m]); + } + return n_keys; } @@ -2201,7 +2513,12 @@ String mt_inspect(MarkTree *b, bool keys, bool dot) return ga_take_string(ga); } -void mt_inspect_node(MarkTree *b, garray_T *ga, bool keys, MTNode *n, MTPos off) +static inline uint64_t mt_dbg_id(uint64_t id) +{ + return (id >> 1) & 0xffffffff; +} + +static void mt_inspect_node(MarkTree *b, garray_T *ga, bool keys, MTNode *n, MTPos off) { static char buf[1024]; GA_PUT("["); @@ -2241,7 +2558,7 @@ void mt_inspect_node(MarkTree *b, garray_T *ga, bool keys, MTNode *n, MTPos off) ga_concat(ga, "]"); } -void mt_inspect_dotfile_node(MarkTree *b, garray_T *ga, MTNode *n, MTPos off, char *parent) +static void mt_inspect_dotfile_node(MarkTree *b, garray_T *ga, MTNode *n, MTPos off, char *parent) { static char buf[1024]; char namebuf[64]; diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index c76359d3f9..8e5cf30ff3 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -1,69 +1,16 @@ #pragma once #include <stdbool.h> -#include <stddef.h> +#include <stddef.h> // IWYU pragma: keep #include <stdint.h> -#include "klib/kvec.h" +#include "nvim/buffer_defs.h" #include "nvim/decoration_defs.h" -#include "nvim/garray_defs.h" // IWYU pragma: keep -#include "nvim/map_defs.h" +#include "nvim/marktree_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep // only for debug functions: #include "nvim/api/private/defs.h" // IWYU pragma: keep -#define MT_MAX_DEPTH 20 -#define MT_BRANCH_FACTOR 10 -// note max branch is actually 2*MT_BRANCH_FACTOR -// and strictly this is ceil(log2(2*MT_BRANCH_FACTOR + 1)) -// as we need a pseudo-index for "right before this node" -#define MT_LOG2_BRANCH 5 - -typedef struct { - int32_t row; - int32_t col; -} MTPos; -#define MTPos(r, c) ((MTPos){ .row = (r), .col = (c) }) - -typedef struct mtnode_s MTNode; - -typedef struct { - MTPos pos; - int lvl; - MTNode *x; - int i; - struct { - int oldcol; - int i; - } s[MT_MAX_DEPTH]; - - size_t intersect_idx; - MTPos intersect_pos; - MTPos intersect_pos_x; -} MarkTreeIter; - -#define marktree_itr_valid(itr) ((itr)->x != NULL) -// access raw key: flags in MT_FLAG_EXTERNAL_MASK and decor_data are safe to modify. -#define mt_itr_rawkey(itr) ((itr)->x->key[(itr)->i]) - -// Internal storage -// -// NB: actual marks have flags > 0, so we can use (row,col,0) pseudo-key for -// "space before (row,col)" -typedef struct { - MTPos pos; - uint32_t ns; - uint32_t id; - uint16_t flags; - DecorInlineData decor_data; // "ext" tag in flags -} MTKey; - -typedef struct { - MTKey start; - MTPos end_pos; - bool end_right_gravity; -} MTPair; - #define MT_INVALID_KEY (MTKey) { { -1, -1 }, 0, 0, 0, { .hl = DECOR_HIGHLIGHT_INLINE_INIT } } #define MT_FLAG_REAL (((uint16_t)1) << 0) @@ -88,6 +35,8 @@ typedef struct { #define MT_FLAG_DECOR_VIRT_LINES (((uint16_t)1) << 11) #define MT_FLAG_DECOR_VIRT_TEXT_INLINE (((uint16_t)1) << 12) +#define MT_FLAG_SCOPED (((uint16_t)1) << 13) + // These _must_ be last to preserve ordering of marks #define MT_FLAG_RIGHT_GRAVITY (((uint16_t)1) << 14) #define MT_FLAG_LAST (((uint16_t)1) << 15) @@ -97,7 +46,7 @@ typedef struct { | MT_FLAG_DECOR_VIRT_TEXT_INLINE) #define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_NO_UNDO \ - | MT_FLAG_INVALIDATE | MT_FLAG_INVALID) + | MT_FLAG_INVALIDATE | MT_FLAG_INVALID | MT_FLAG_SCOPED) // this is defined so that start and end of the same range have adjacent ids #define MARKTREE_END_FLAG ((uint64_t)1) @@ -161,12 +110,24 @@ static inline bool mt_decor_sign(MTKey key) return key.flags & (MT_FLAG_DECOR_SIGNTEXT | MT_FLAG_DECOR_SIGNHL); } -static inline uint16_t mt_flags(bool right_gravity, bool no_undo, bool invalidate, bool decor_ext) +static inline bool mt_scoped(MTKey key) +{ + return key.flags & MT_FLAG_SCOPED; +} + +static inline bool mt_scoped_in_win(MTKey key, win_T *wp) +{ + return !mt_scoped(key) || set_has(uint32_t, &wp->w_ns_set, key.ns); +} + +static inline uint16_t mt_flags(bool right_gravity, bool no_undo, bool invalidate, bool decor_ext, + bool scoped) { return (uint16_t)((right_gravity ? MT_FLAG_RIGHT_GRAVITY : 0) | (no_undo ? MT_FLAG_NO_UNDO : 0) | (invalidate ? MT_FLAG_INVALIDATE : 0) - | (decor_ext ? MT_FLAG_DECOR_EXT : 0)); + | (decor_ext ? MT_FLAG_DECOR_EXT : 0) + | (scoped ? MT_FLAG_SCOPED : 0)); } static inline MTPair mtpair_from(MTKey start, MTKey end) @@ -179,31 +140,11 @@ static inline DecorInline mt_decor(MTKey key) return (DecorInline){ .ext = key.flags & MT_FLAG_DECOR_EXT, .data = key.decor_data }; } -typedef kvec_withinit_t(uint64_t, 4) Intersection; - -struct mtnode_s { - int32_t n; - int16_t level; - int16_t p_idx; // index in parent - Intersection intersect; - // TODO(bfredl): we could consider having a only-sometimes-valid - // index into parent for faster "cached" lookup. - MTNode *parent; - MTKey key[2 * MT_BRANCH_FACTOR - 1]; - MTNode *ptr[]; -}; - -static inline uint64_t mt_dbg_id(uint64_t id) +static inline DecorVirtText *mt_decor_virt(MTKey mark) { - return (id>>1)&0xffffffff; + return (mark.flags & MT_FLAG_DECOR_EXT) ? mark.decor_data.ext.vt : NULL; } -typedef struct { - MTNode *root; - size_t n_keys, n_nodes; - PMap(uint64_t) id2node[1]; -} MarkTree; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "marktree.h.generated.h" #endif diff --git a/src/nvim/marktree_defs.h b/src/nvim/marktree_defs.h new file mode 100644 index 0000000000..d43130db6f --- /dev/null +++ b/src/nvim/marktree_defs.h @@ -0,0 +1,103 @@ +#pragma once + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "nvim/decoration_defs.h" +#include "nvim/map_defs.h" + +enum { + MT_MAX_DEPTH = 20, + MT_BRANCH_FACTOR = 10, + // note max branch is actually 2*MT_BRANCH_FACTOR + // and strictly this is ceil(log2(2*MT_BRANCH_FACTOR + 1)) + // as we need a pseudo-index for "right before this node" + MT_LOG2_BRANCH = 5, +}; + +typedef struct { + int32_t row; + int32_t col; +} MTPos; +#define MTPos(r, c) ((MTPos){ .row = (r), .col = (c) }) + +// Currently there are four counts, which makes for a uint32_t[4] per node +// which makes for nice autovectorization into a single XMM or NEON register +typedef enum { + kMTMetaInline, + kMTMetaLines, + kMTMetaSignHL, + kMTMetaSignText, + kMTMetaCount, // sentinel, must be last +} MetaIndex; + +#define kMTFilterSelect ((uint32_t)-1) + +// a filter should be set to kMTFilterSelect for the selected kinds, zero otherwise +typedef const uint32_t *MetaFilter; + +typedef struct mtnode_s MTNode; + +typedef struct { + MTPos pos; + int lvl; + MTNode *x; + int i; + struct { + int oldcol; + int i; + } s[MT_MAX_DEPTH]; + + size_t intersect_idx; + MTPos intersect_pos; + MTPos intersect_pos_x; +} MarkTreeIter; + +#define marktree_itr_valid(itr) ((itr)->x != NULL) +// access raw key: flags in MT_FLAG_EXTERNAL_MASK and decor_data are safe to modify. +#define mt_itr_rawkey(itr) ((itr)->x->key[(itr)->i]) + +// Internal storage +// +// NB: actual marks have flags > 0, so we can use (row,col,0) pseudo-key for +// "space before (row,col)" +typedef struct { + MTPos pos; + uint32_t ns; + uint32_t id; + uint16_t flags; + DecorInlineData decor_data; // "ext" tag in flags +} MTKey; + +typedef struct { + MTKey start; + MTPos end_pos; + bool end_right_gravity; +} MTPair; + +typedef kvec_withinit_t(uint64_t, 4) Intersection; + +// part of mtnode_s which is only allocated for inner nodes: +// pointer to children as well as their meta counts +struct mtnode_inner_s { + MTNode *i_ptr[2 * MT_BRANCH_FACTOR]; + uint32_t i_meta[2 * MT_BRANCH_FACTOR][kMTMetaCount]; +}; + +struct mtnode_s { + int32_t n; + int16_t level; + int16_t p_idx; // index in parent + Intersection intersect; + MTNode *parent; + MTKey key[2 * MT_BRANCH_FACTOR - 1]; + struct mtnode_inner_s s[]; +}; + +typedef struct { + MTNode *root; + uint32_t meta_root[kMTMetaCount]; + size_t n_keys, n_nodes; + PMap(uint64_t) id2node[1]; +} MarkTree; diff --git a/src/nvim/match.c b/src/nvim/match.c index 0a7c264d4f..c8837969b6 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -16,14 +16,15 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/macros_defs.h" #include "nvim/match.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -451,10 +452,8 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_ } else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL || (shl->rm.endpos[0].lnum == 0 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) { - char *ml; - matchcol = shl->rm.startpos[0].col; - ml = ml_get_buf(shl->buf, lnum) + matchcol; + char *ml = ml_get_buf(shl->buf, lnum) + matchcol; if (*ml == NUL) { matchcol++; shl->lnum = 0; @@ -669,7 +668,7 @@ bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char **lin /// is endcol. /// Return the updated search_attr. int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char **line, match_T *search_hl, - int *has_match_conc, int *match_conc, int lcs_eol_one, bool *on_last_col, + int *has_match_conc, int *match_conc, bool lcs_eol_todo, bool *on_last_col, bool *search_attr_from_match) { matchitem_T *cur = wp->w_match_head; // points to the match list @@ -790,7 +789,7 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char **line, match_T } } // Only highlight one character after the last column. - if (*(*line + col) == NUL && (wp->w_p_list && lcs_eol_one == -1)) { + if (*(*line + col) == NUL && (wp->w_p_list && !lcs_eol_todo)) { search_attr = 0; } return search_attr; diff --git a/src/nvim/math.c b/src/nvim/math.c index 96ff1bef10..9a0825823c 100644 --- a/src/nvim/math.c +++ b/src/nvim/math.c @@ -7,10 +7,11 @@ #include "nvim/math.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "math.c.generated.h" // IWYU pragma: export +# include "math.c.generated.h" #endif int xfpclassify(double d) + FUNC_ATTR_CONST { uint64_t m; @@ -29,11 +30,39 @@ int xfpclassify(double d) } int xisinf(double d) + FUNC_ATTR_CONST { return FP_INFINITE == xfpclassify(d); } int xisnan(double d) + FUNC_ATTR_CONST { return FP_NAN == xfpclassify(d); } + +/// Count trailing zeroes at the end of bit field. +int xctz(uint64_t x) +{ + // If x == 0, that means all bits are zeroes. + if (x == 0) { + return 8 * sizeof(x); + } + + // Use compiler builtin if possible. +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 4)) + return __builtin_ctzll(x); +#else + int count = 0; + // Set x's trailing zeroes to ones and zero the rest. + x = (x ^ (x - 1)) >> 1; + + // Increment count until there are just zero bits remaining. + while (x) { + count++; + x >>= 1; + } + + return count; +#endif +} diff --git a/src/nvim/math.h b/src/nvim/math.h index c88ce1e03d..0321d0c9c6 100644 --- a/src/nvim/math.h +++ b/src/nvim/math.h @@ -1,5 +1,14 @@ #pragma once +#include <stdbool.h> +#include <stdint.h> + +/// Check if number is a power of two +static inline bool is_power_of_two(uint64_t x) +{ + return x != 0 && ((x & (x - 1)) == 0); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "math.h.generated.h" #endif diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index f2883cc5c7..c7a56209e4 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -32,7 +32,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include <wctype.h> #include "auto/config.h" @@ -46,9 +46,10 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/iconv_defs.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" @@ -444,24 +445,26 @@ int mb_get_class_tab(const char *p, const uint64_t *const chartab) static bool intable(const struct interval *table, size_t n_items, int c) FUNC_ATTR_PURE { + assert(n_items > 0); // first quick check for Latin1 etc. characters if (c < table[0].first) { return false; } + assert(n_items <= SIZE_MAX / 2); // binary search in table - int bot = 0; - int top = (int)(n_items - 1); - while (top >= bot) { - int mid = (bot + top) / 2; + size_t bot = 0; + size_t top = n_items; + do { + size_t mid = (bot + top) >> 1; if (table[mid].last < c) { bot = mid + 1; } else if (table[mid].first > c) { - top = mid - 1; + top = mid; } else { return true; } - } + } while (top > bot); return false; } @@ -475,32 +478,28 @@ static bool intable(const struct interval *table, size_t n_items, int c) /// gen_unicode_tables.lua, which must be manually invoked as needed. int utf_char2cells(int c) { - // Use the value from setcellwidths() at 0x80 and higher, unless the - // character is not printable. - if (c >= 0x80 && vim_isprintc(c)) { - int n = cw_value(c); - if (n != 0) { - return n; - } + if (c < 0x80) { + return 1; } - if (c >= 0x100) { - if (!utf_printable(c)) { - return 6; // unprintable, displays <xxxx> - } - if (intable(doublewidth, ARRAY_SIZE(doublewidth), c)) { - return 2; - } - if (p_emoji && intable(emoji_wide, ARRAY_SIZE(emoji_wide), c)) { - return 2; - } - } else if (c >= 0x80 && !vim_isprintc(c)) { - // Characters below 0x100 are influenced by 'isprint' option. - return 4; // unprintable, displays <xx> + if (!vim_isprintc(c)) { + assert(c <= 0xFFFF); + // unprintable is displayed either as <xx> or <xxxx> + return c > 0xFF ? 6 : 4; + } + + int n = cw_value(c); + if (n != 0) { + return n; } - if (c >= 0x80 && *p_ambw == 'd' - && intable(ambiguous, ARRAY_SIZE(ambiguous), c)) { + if (intable(doublewidth, ARRAY_SIZE(doublewidth), c)) { + return 2; + } + if (p_emoji && intable(emoji_wide, ARRAY_SIZE(emoji_wide), c)) { + return 2; + } + if (*p_ambw == 'd' && intable(ambiguous, ARRAY_SIZE(ambiguous), c)) { return 2; } @@ -527,6 +526,74 @@ int utf_ptr2cells(const char *p) return 1; } +/// Convert a UTF-8 byte sequence to a character number. +/// Doesn't handle ascii! only multibyte and illegal sequences. +/// +/// @param[in] p String to convert. +/// @param[in] len Length of the character in bytes, 0 or 1 if illegal. +/// +/// @return Unicode codepoint. A negative value when the sequence is illegal. +int32_t utf_ptr2CharInfo_impl(uint8_t const *p, uintptr_t const len) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ +// uint8_t is a reminder for clang to use smaller cmp +#define CHECK \ + do { \ + if (EXPECT((uint8_t)(cur & 0xC0U) != 0x80U, false)) { \ + return -1; \ + } \ + } while (0) + + static uint32_t const corrections[] = { + (1U << 31), // invalid - set invalid bits (safe to add as first 2 bytes + (1U << 31), // won't affect highest bit in normal ret) + -(0x80U + (0xC0U << 6)), // multibyte - subtract added UTF8 bits (1..10xxx and 10xxx) + -(0x80U + (0x80U << 6) + (0xE0U << 12)), + -(0x80U + (0x80U << 6) + (0x80U << 12) + (0xF0U << 18)), + -(0x80U + (0x80U << 6) + (0x80U << 12) + (0x80U << 18) + (0xF8U << 24)), + -(0x80U + (0x80U << 6) + (0x80U << 12) + (0x80U << 18) + (0x80U << 24)), // + (0xFCU << 30) + }; + + // len is 0-6, but declared uintptr_t to avoid zeroing out upper bits + uint32_t const corr = corrections[len]; + uint8_t cur; + + // reading second byte unconditionally, safe for invalid + // as it cannot be the last byte, not safe for ascii + uint32_t code_point = ((uint32_t)p[0] << 6) + (cur = p[1]); + CHECK; + if ((uint32_t)len < 3) { + goto ret; // len == 0, 1, 2 + } + + code_point = (code_point << 6) + (cur = p[2]); + CHECK; + if ((uint32_t)len == 3) { + goto ret; + } + + code_point = (code_point << 6) + (cur = p[3]); + CHECK; + if ((uint32_t)len == 4) { + goto ret; + } + + code_point = (code_point << 6) + (cur = p[4]); + CHECK; + if ((uint32_t)len == 5) { + goto ret; + } + + code_point = (code_point << 6) + (cur = p[5]); + CHECK; + // len == 6 + +ret: + return (int32_t)(code_point + corr); + +#undef CHECK +} + /// Like utf_ptr2cells(), but limit string length to "size". /// For an empty string or truncated character returns 1. int utf_ptr2cells_len(const char *p, int size) @@ -596,45 +663,62 @@ size_t mb_string2cells_len(const char *str, size_t size) /// /// @return Unicode codepoint or byte value. int utf_ptr2char(const char *const p_in) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { uint8_t *p = (uint8_t *)p_in; - if (p[0] < 0x80) { // Be quick for ASCII. - return p[0]; + + uint32_t const v0 = p[0]; + if (EXPECT(v0 < 0x80U, true)) { // Be quick for ASCII. + return (int)v0; } - const uint8_t len = utf8len_tab_zero[p[0]]; - if (len > 1 && (p[1] & 0xc0) == 0x80) { - if (len == 2) { - return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f); - } - if ((p[2] & 0xc0) == 0x80) { - if (len == 3) { - return (((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) - + (p[2] & 0x3f)); - } - if ((p[3] & 0xc0) == 0x80) { - if (len == 4) { - return (((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) - + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f)); - } - if ((p[4] & 0xc0) == 0x80) { - if (len == 5) { - return (((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18) - + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6) - + (p[4] & 0x3f)); - } - if ((p[5] & 0xc0) == 0x80 && len == 6) { - return (((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24) - + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12) - + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f)); - } - } - } - } + const uint8_t len = utf8len_tab[v0]; + if (EXPECT(len < 2, false)) { + return (int)v0; } - // Illegal value: just return the first byte. - return p[0]; + +#define CHECK(v) \ + do { \ + if (EXPECT((uint8_t)((v) & 0xC0U) != 0x80U, false)) { \ + return (int)v0; \ + } \ + } while (0) +#define LEN_RETURN(len_v, result) \ + do { \ + if (len == (len_v)) { \ + return (int)(result); \ + } \ + } while (0) +#define S(s) ((uint32_t)0x80U << (s)) + + uint32_t const v1 = p[1]; + CHECK(v1); + LEN_RETURN(2, (v0 << 6) + v1 - ((0xC0U << 6) + S(0))); + + uint32_t const v2 = p[2]; + CHECK(v2); + LEN_RETURN(3, (v0 << 12) + (v1 << 6) + v2 - ((0xE0U << 12) + S(6) + S(0))); + + uint32_t const v3 = p[3]; + CHECK(v3); + LEN_RETURN(4, (v0 << 18) + (v1 << 12) + (v2 << 6) + v3 + - ((0xF0U << 18) + S(12) + S(6) + S(0))); + + uint32_t const v4 = p[4]; + CHECK(v4); + LEN_RETURN(5, (v0 << 24) + (v1 << 18) + (v2 << 12) + (v3 << 6) + v4 + - ((0xF8U << 24) + S(18) + S(12) + S(6) + S(0))); + + uint32_t const v5 = p[5]; + CHECK(v5); + // len == 6 + return (int)((v0 << 30) + (v1 << 24) + (v2 << 18) + (v3 << 12) + (v4 << 6) + v5 + // - (0xFCU << 30) + - (S(24) + S(18) + S(12) + S(6) + S(0))); + +#undef S +#undef CHECK +#undef LEN_RETURN } // Convert a UTF-8 byte sequence to a wide character. @@ -721,6 +805,16 @@ bool utf_composinglike(const char *p1, const char *p2) return arabic_combine(utf_ptr2char(p1), c2); } +/// Check if the next character is a composing character when it +/// comes after the first. For Arabic sometimes "ab" is replaced with "c", which +/// behaves like a composing character. +/// returns false for negative values +bool utf_char_composinglike(int32_t const first, int32_t const next) + FUNC_ATTR_PURE +{ + return utf_iscomposing(next) || arabic_combine(first, next); +} + /// Get the screen char at the beginning of a string /// /// Caller is expected to check for things like unprintable chars etc @@ -987,17 +1081,61 @@ int utf_char2bytes(const int c, char *const buf) } } -// Return true if "c" is a composing UTF-8 character. This means it will be -// drawn on top of the preceding character. -// Based on code from Markus Kuhn. +/// Return true if "c" is a composing UTF-8 character. +/// This means it will be drawn on top of the preceding character. +/// Based on code from Markus Kuhn. +/// Returns false for negative values. bool utf_iscomposing(int c) { return intable(combining, ARRAY_SIZE(combining), c); } +#ifdef __SSE2__ + +# include <emmintrin.h> + +// Return true for characters that can be displayed in a normal way. +// Only for characters of 0x100 and above! +bool utf_printable(int c) + FUNC_ATTR_CONST +{ + if (c < 0x180B || c > 0xFFFF) { + return c != 0x70F; + } + +# define L(v) ((int16_t)((v) - 1)) // lower bound (exclusive) +# define H(v) ((int16_t)(v)) // upper bound (inclusive) + + // Boundaries of unprintable characters. + // Some values are negative when converted to int16_t. + // Ranges must not wrap around when converted to int16_t. + __m128i const lo = _mm_setr_epi16(L(0x180b), L(0x200b), L(0x202a), L(0x2060), + L(0xd800), L(0xfeff), L(0xfff9), L(0xfffe)); + + __m128i const hi = _mm_setr_epi16(H(0x180e), H(0x200f), H(0x202e), H(0x206f), + H(0xdfff), H(0xfeff), H(0xfffb), H(0xffff)); + +# undef L +# undef H + + __m128i value = _mm_set1_epi16((int16_t)c); + + // Using _mm_cmplt_epi16() is less optimal, since it would require + // swapping operands (sse2 only has cmpgt instruction), + // and only the second operand can be a memory location. + + // Character is printable when it is above/below both bounds of each range + // (corresponding bits in both masks are equal). + return _mm_movemask_epi8(_mm_cmpgt_epi16(value, lo)) + == _mm_movemask_epi8(_mm_cmpgt_epi16(value, hi)); +} + +#else + // Return true for characters that can be displayed in a normal way. // Only for characters of 0x100 and above! bool utf_printable(int c) + FUNC_ATTR_PURE { // Sorted list of non-overlapping intervals. // 0xd800-0xdfff is reserved for UTF-16, actually illegal. @@ -1010,6 +1148,8 @@ bool utf_printable(int c) return !intable(nonprint, ARRAY_SIZE(nonprint), c); } +#endif + // Get class of a Unicode character. // 0: white space // 1: punctuation @@ -1183,6 +1323,9 @@ int utf_fold(int a) // invalid values or can't handle latin1 when the locale is C. // Speed is most important here. +// Note: UnicodeData.txt does not define U+1E9E as being the corresponding upper +// case letter for U+00DF (ß), however it is part of the toLower table + /// Return the upper-case equivalent of "a", which is a UCS-4 character. Use /// simple case folding. int mb_toupper(int a) @@ -1422,7 +1565,8 @@ int utf16_to_utf8(const wchar_t *utf16, int utf16len, char **utf8) void mb_utflen(const char *s, size_t len, size_t *codepoints, size_t *codeunits) FUNC_ATTR_NONNULL_ALL { - size_t count = 0, extra = 0; + size_t count = 0; + size_t extra = 0; size_t clen; for (size_t i = 0; i < len; i += clen) { clen = (size_t)utf_ptr2len_len(s + i, (int)(len - i)); @@ -1740,99 +1884,66 @@ void mb_copy_char(const char **const fp, char **const tp) *fp += l; } -/// Return the offset from "p_in" to the first byte of a character. When "p_in" is +/// Return the offset from "p" to the first byte of a character. When "p" is /// at the start of a character 0 is returned, otherwise the offset to the next /// character. Can start anywhere in a stream of bytes. -int mb_off_next(const char *base, const char *p_in) +int mb_off_next(const char *base, const char *p) { - const uint8_t *p = (uint8_t *)p_in; - int i; + int head_off = utf_head_off(base, p); - if (*p < 0x80) { // be quick for ASCII + if (head_off == 0) { return 0; } - // Find the next character that isn't 10xx.xxxx - for (i = 0; (p[i] & 0xc0) == 0x80; i++) {} - if (i > 0) { - int j; - // Check for illegal sequence. - for (j = 0; p - j > (uint8_t *)base; j++) { - if ((p[-j] & 0xc0) != 0x80) { - break; - } - } - if (utf8len_tab[p[-j]] != i + j) { - return 0; - } - } - return i; + return utfc_ptr2len(p - head_off) - head_off; } -/// Return the offset from `p_in` to the last byte of the codepoint it points -/// to. Can start anywhere in a stream of bytes. +/// Returns the offset in bytes from "p_in" to the first and one-past-end bytes +/// of the codepoint it points to. +/// "p_in" can point anywhere in a stream of bytes. +/// "p_len" limits number of bytes after "p_in". /// Note: Counts individual codepoints of composed characters separately. -int utf_cp_tail_off(const char *base, const char *p_in) +CharBoundsOff utf_cp_bounds_len(char const *base, char const *p_in, int p_len) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { - const uint8_t *p = (uint8_t *)p_in; - int i; - int j; - - if (*p == NUL) { - return 0; + assert(base <= p_in && p_len > 0); + uint8_t const *const b = (uint8_t *)base; + uint8_t const *const p = (uint8_t *)p_in; + if (*p < 0x80U) { // be quick for ASCII + return (CharBoundsOff){ 0, 1 }; } - // Find the last character that is 10xx.xxxx - for (i = 0; (p[i + 1] & 0xc0) == 0x80; i++) {} - - // Check for illegal sequence. - for (j = 0; p_in - j > base; j++) { - if ((p[-j] & 0xc0) != 0x80) { - break; + int const max_first_off = -MIN((int)(p - b), MB_MAXCHAR - 1); + int first_off = 0; + for (; utf_is_trail_byte(p[first_off]); first_off--) { + if (first_off == max_first_off) { // failed to find first byte + return (CharBoundsOff){ 0, 1 }; } } - if (utf8len_tab[p[-j]] != i + j + 1) { - return 0; + int const max_end_off = utf8len_tab[p[first_off]] + first_off; + if (max_end_off <= 0 || max_end_off > p_len) { // illegal or incomplete sequence + return (CharBoundsOff){ 0, 1 }; } - return i; -} -/// Return the offset from "p" to the first byte of the codepoint it points -/// to. Can start anywhere in a stream of bytes. -/// Note: Unlike `utf_head_off`, this counts individual codepoints of composed characters -/// separately. -/// -/// @param[in] base Pointer to start of string -/// @param[in] p Pointer to byte for which to return the offset to the previous codepoint -// -/// @return 0 if invalid sequence, else number of bytes to previous codepoint -int utf_cp_head_off(const char *base, const char *p) -{ - int i; - - if (*p == NUL) { - return 0; - } - - // Find the first character that is not 10xx.xxxx - for (i = 0; p - i >= base; i++) { - if (((uint8_t)p[-i] & 0xc0) != 0x80) { - break; + for (int end_off = 1; end_off < max_end_off; end_off++) { + if (!utf_is_trail_byte(p[end_off])) { // not enough trail bytes + return (CharBoundsOff){ 0, 1 }; } } - // Find the last character that is 10xx.xxxx (condition terminates on NUL) - int j = 1; - while (((uint8_t)p[j] & 0xc0) == 0x80) { - j++; - } + return (CharBoundsOff){ .begin_off = (int8_t)-first_off, .end_off = (int8_t)max_end_off }; +} - // Check for illegal sequence. - if (utf8len_tab[(uint8_t)p[-i]] != j + i) { - return 0; - } - return i; +/// Returns the offset in bytes from "p_in" to the first and one-past-end bytes +/// of the codepoint it points to. +/// "p_in" can point anywhere in a stream of bytes. +/// Stream must be NUL-terminated. +/// Note: Counts individual codepoints of composed characters separately. +CharBoundsOff utf_cp_bounds(char const *base, char const *p_in) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL +{ + return utf_cp_bounds_len(base, p_in, INT_MAX); } // Find the next illegal byte sequence. @@ -2250,7 +2361,7 @@ void *my_iconv_open(char *to, char *from) // stops for no apparent reason after about 8160 characters. char *p = tobuf; size_t tolen = ICONV_TESTLEN; - (void)iconv(fd, NULL, NULL, &p, &tolen); + iconv(fd, NULL, NULL, &p, &tolen); if (p == NULL) { iconv_working = kBroken; iconv_close(fd); @@ -2651,8 +2762,10 @@ static int tv_nr_compare(const void *a1, const void *a2) { const listitem_T *const li1 = tv_list_first(*(const list_T **)a1); const listitem_T *const li2 = tv_list_first(*(const list_T **)a2); + const varnumber_T n1 = TV_LIST_ITEM_TV(li1)->vval.v_number; + const varnumber_T n2 = TV_LIST_ITEM_TV(li2)->vval.v_number; - return (int)(TV_LIST_ITEM_TV(li1)->vval.v_number - TV_LIST_ITEM_TV(li2)->vval.v_number); + return n1 == n2 ? 0 : n1 > n2 ? 1 : -1; } /// "setcellwidths()" function @@ -2802,3 +2915,14 @@ char *get_encoding_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) return (char *)enc_canon_table[idx].name; } + +/// Compare strings +/// +/// @param[in] ic True if case is to be ignored. +/// +/// @return 0 if s1 == s2, <0 if s1 < s2, >0 if s1 > s2. +int mb_strcmp_ic(bool ic, const char *s1, const char *s2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (ic ? mb_stricmp(s1, s2) : strcmp(s1, s2)); +} diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 49c323282d..ddac040aae 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -2,15 +2,24 @@ #include <stdbool.h> #include <stdint.h> -#include <string.h> +#include <sys/types.h> // IWYU pragma: keep +#include <uv.h> // IWYU pragma: keep #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" -#include "nvim/mbyte_defs.h" // IWYU pragma: export -#include "nvim/os/os_defs.h" // IWYU pragma: export +#include "nvim/macros_defs.h" +#include "nvim/mbyte_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "mbyte.h.generated.h" +#endif + +enum { + kInvalidByteCells = 4, +}; + // Return byte length of character that starts with byte "b". // Returns 1 for a single-byte character. // MB_BYTE2LEN_CHECK() can be used to count a special key as one byte. @@ -22,19 +31,100 @@ extern const uint8_t utf8len_tab_zero[256]; extern const uint8_t utf8len_tab[256]; -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "mbyte.h.generated.h" -#endif +// Use our own character-case definitions, because the current locale may +// differ from what the .spl file uses. +// These must not be called with negative number! +// Multi-byte implementation. For Unicode we can call utf_*(), but don't do +// that for ASCII, because we don't want to use 'casemap' here. Otherwise use +// the "w" library function for characters above 255. +#define SPELL_TOFOLD(c) ((c) >= 128 ? utf_fold(c) : (int)spelltab.st_fold[c]) + +#define SPELL_TOUPPER(c) ((c) >= 128 ? mb_toupper(c) : (int)spelltab.st_upper[c]) + +#define SPELL_ISUPPER(c) ((c) >= 128 ? mb_isupper(c) : spelltab.st_isu[c]) + +// MB_PTR_ADV(): advance a pointer to the next character, taking care of +// multi-byte characters if needed. Skip over composing chars. +#define MB_PTR_ADV(p) (p += utfc_ptr2len((char *)p)) -static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; +// MB_PTR_BACK(): backup a pointer to the previous character, taking care of +// multi-byte characters if needed. Only use with "p" > "s" ! +#define MB_PTR_BACK(s, p) \ + (p -= utf_head_off((char *)(s), (char *)(p) - 1) + 1) -/// Compare strings +/// Check whether a given UTF-8 byte is a trailing byte (10xx.xxxx). +static inline bool utf_is_trail_byte(uint8_t byte) + REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; + +static inline bool utf_is_trail_byte(uint8_t const byte) +{ + // uint8_t is for clang to use smaller cmp + return (uint8_t)(byte & 0xC0U) == 0x80U; +} + +static inline CharInfo utf_ptr2CharInfo(char const *p_in) + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; + +/// Convert a UTF-8 byte sequence to a Unicode code point. +/// Handles ascii, multibyte sequiences and illegal sequences. /// -/// @param[in] ic True if case is to be ignored. +/// @param[in] p_in String to convert. +/// +/// @return information abouth the character. When the sequence is illegal, +/// "value" is negative, "len" is 1. +static inline CharInfo utf_ptr2CharInfo(char const *const p_in) +{ + uint8_t const *const p = (uint8_t const *)p_in; + uint8_t const first = *p; + if (first < 0x80) { + return (CharInfo){ .value = first, .len = 1 }; + } else { + int len = utf8len_tab[first]; + int32_t const code_point = utf_ptr2CharInfo_impl(p, (uintptr_t)len); + if (code_point < 0) { + len = 1; + } + return (CharInfo){ .value = code_point, .len = len }; + } +} + +static inline StrCharInfo utfc_next(StrCharInfo cur) + REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE; + +/// Return information about the next character. +/// Composing and combining characters are considered a part of the current character. /// -/// @return 0 if s1 == s2, <0 if s1 < s2, >0 if s1 > s2. -static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) +/// @param[in] cur Information about the current character in the string. +static inline StrCharInfo utfc_next(StrCharInfo cur) +{ + int32_t prev_code = cur.chr.value; + uint8_t *next = (uint8_t *)(cur.ptr + cur.chr.len); + + while (true) { + if (EXPECT(*next < 0x80U, true)) { + return (StrCharInfo){ + .ptr = (char *)next, + .chr = (CharInfo){ .value = *next, .len = 1 }, + }; + } + uint8_t const next_len = utf8len_tab[*next]; + int32_t const next_code = utf_ptr2CharInfo_impl(next, (uintptr_t)next_len); + if (!utf_char_composinglike(prev_code, next_code)) { + return (StrCharInfo){ + .ptr = (char *)next, + .chr = (CharInfo){ .value = next_code, .len = (next_code < 0 ? 1 : next_len) }, + }; + } + + prev_code = next_code; + next += next_len; + } +} + +static inline StrCharInfo utf_ptr2StrCharInfo(char *ptr) + REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE; + +static inline StrCharInfo utf_ptr2StrCharInfo(char *ptr) { - return (ic ? mb_stricmp(s1, s2) : strcmp(s1, s2)); + return (StrCharInfo){ .ptr = ptr, .chr = utf_ptr2CharInfo(ptr) }; } diff --git a/src/nvim/mbyte_defs.h b/src/nvim/mbyte_defs.h index 2904047223..8670a0595d 100644 --- a/src/nvim/mbyte_defs.h +++ b/src/nvim/mbyte_defs.h @@ -1,16 +1,18 @@ #pragma once #include <stdbool.h> +#include <stdint.h> #include "nvim/iconv_defs.h" -/// Maximum number of bytes in a multi-byte character. It can be one 32-bit -/// character of up to 6 bytes, or one 16-bit character of up to three bytes -/// plus six following composing characters of three bytes each. -enum { MB_MAXBYTES = 21, }; - -/// max length of an unicode char -enum { MB_MAXCHAR = 6, }; +enum { + /// Maximum number of bytes in a multi-byte character. It can be one 32-bit + /// character of up to 6 bytes, or one 16-bit character of up to three bytes + /// plus six following composing characters of three bytes each. + MB_MAXBYTES = 21, + /// max length of an unicode char + MB_MAXCHAR = 6, +}; /// properties used in enc_canon_table[] (first three mutually exclusive) enum { @@ -54,3 +56,18 @@ typedef struct { bool vc_fail; ///< What to do with invalid characters: if true, fail, ///< otherwise use '?'. } vimconv_T; + +typedef struct { + int32_t value; ///< Code point. + int len; ///< Length in bytes. +} CharInfo; + +typedef struct { + char *ptr; ///< Pointer to the first byte of the character. + CharInfo chr; ///< Information about the character. +} StrCharInfo; + +typedef struct { + int8_t begin_off; ///< Offset to the first byte of the codepoint. + int8_t end_off; ///< Offset to one past the end byte of the codepoint. +} CharBoundsOff; diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index d989600d45..fb9f2eb8df 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -47,7 +47,7 @@ #include "nvim/assert_defs.h" #include "nvim/buffer_defs.h" #include "nvim/fileio.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/map_defs.h" #include "nvim/memfile.h" @@ -56,10 +56,12 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" -#include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" #define MEMFILE_PAGE_SIZE 4096 /// default page size @@ -204,7 +206,7 @@ void mf_close_file(buf_T *buf, bool getlines) if (getlines) { // get all blocks in memory by accessing all lines (clumsy!) for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - (void)ml_get_buf(buf, lnum); + ml_get_buf(buf, lnum); } } @@ -271,7 +273,7 @@ bhdr_T *mf_new(memfile_T *mfp, bool negative, unsigned page_count) // Init the data to all zero, to avoid reading uninitialized data. // This also avoids that the passwd file ends up in the swap file! - (void)memset(hp->bh_data, 0, (size_t)mfp->mf_page_size * page_count); + memset(hp->bh_data, 0, (size_t)mfp->mf_page_size * page_count); return hp; } @@ -390,7 +392,7 @@ int mf_sync(memfile_T *mfp, int flags) // Then we only try to write blocks within the existing file. If that also // fails then we give up. int status = OK; - bhdr_T *hp; + bhdr_T *hp = NULL; // note, "last" block is typically earlier in the hash list map_foreach_value(&mfp->mf_hash, hp, { if (((flags & MFS_ALL) || hp->bh_bnum >= 0) @@ -757,7 +759,7 @@ static bool mf_do_open(memfile_T *mfp, char *fname, int flags) return false; } - (void)os_set_cloexec(mfp->mf_fd); + os_set_cloexec(mfp->mf_fd); return true; } diff --git a/src/nvim/memfile.h b/src/nvim/memfile.h index 687ac042a4..17913e5fb2 100644 --- a/src/nvim/memfile.h +++ b/src/nvim/memfile.h @@ -1,7 +1,7 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep -#include "nvim/memfile_defs.h" // IWYU pragma: export +#include "nvim/memfile_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep /// flags for mf_sync() enum { diff --git a/src/nvim/memfile_defs.h b/src/nvim/memfile_defs.h index db68ecf039..5caf13c9fb 100644 --- a/src/nvim/memfile_defs.h +++ b/src/nvim/memfile_defs.h @@ -5,8 +5,6 @@ #include <stdlib.h> #include "nvim/map_defs.h" -#include "nvim/pos_defs.h" -#include "nvim/types_defs.h" /// A block number. /// @@ -26,7 +24,7 @@ typedef int64_t blocknr_T; /// The free list is a single linked list, not sorted. /// The blocks in the free list have no block of memory allocated and /// the contents of the block in the file (if any) is irrelevant. -typedef struct bhdr { +typedef struct { blocknr_T bh_bnum; ///< key used in hash table void *bh_data; ///< pointer to memory (for used block) @@ -44,7 +42,7 @@ typedef enum { } mfdirty_T; /// A memory file. -typedef struct memfile { +typedef struct { char *mf_fname; ///< name of the file char *mf_ffname; ///< idem, full path int mf_fd; ///< file descriptor diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 3c671121b7..6b2f26b2d8 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -39,7 +39,6 @@ #include <stddef.h> #include <stdio.h> #include <string.h> -#include <sys/types.h> #include <time.h> #include <uv.h> @@ -47,6 +46,7 @@ #include "klib/kvec.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" @@ -56,11 +56,11 @@ #include "nvim/eval/typval.h" #include "nvim/ex_cmds_defs.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/input.h" #include "nvim/macros_defs.h" #include "nvim/main.h" @@ -68,16 +68,21 @@ #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memfile.h" +#include "nvim/memfile_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/process.h" #include "nvim/os/time.h" +#include "nvim/os/time_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/spell.h" @@ -331,7 +336,7 @@ int ml_open(buf_T *buf) b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; b0p->b0_flags = (char)(get_fileformat(buf) + 1); set_b0_fname(b0p, buf); - (void)os_get_username(b0p->b0_uname, B0_UNAME_SIZE); + os_get_username(b0p->b0_uname, B0_UNAME_SIZE); b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL; os_get_hostname(b0p->b0_hname, B0_HNAME_SIZE); b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL; @@ -345,7 +350,7 @@ int ml_open(buf_T *buf) // is created. mf_put(mfp, hp, true, false); if (!buf->b_help && !buf->b_spell) { - (void)mf_sync(mfp, 0); + mf_sync(mfp, 0); } // Fill in root pointer block and write page 1. @@ -451,7 +456,7 @@ void ml_setname(buf_T *buf) emsg(_("E301: Oops, lost the swap file!!!")); return; } - (void)os_set_cloexec(mfp->mf_fd); + os_set_cloexec(mfp->mf_fd); } if (!success) { emsg(_("E302: Could not rename swap file")); @@ -486,7 +491,7 @@ void ml_open_file(buf_T *buf) if (buf->b_spell) { char *fname = vim_tempname(); if (fname != NULL) { - (void)mf_open_file(mfp, fname); // consumes fname! + mf_open_file(mfp, fname); // consumes fname! } buf->b_may_swap = false; return; @@ -530,8 +535,8 @@ void ml_open_file(buf_T *buf) if (*p_dir != NUL && mfp->mf_fname == NULL) { need_wait_return = true; // call wait_return() later no_wait_return++; - (void)semsg(_("E303: Unable to open swap file for \"%s\", recovery impossible"), - buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname); + semsg(_("E303: Unable to open swap file for \"%s\", recovery impossible"), + buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname); no_wait_return--; } @@ -795,7 +800,7 @@ void ml_recover(bool checkext) i = 1; } else { // several swapfiles found, choose // list the names of the swapfiles - (void)recover_names(fname, true, NULL, 0, NULL); + recover_names(fname, true, NULL, 0, NULL); msg_putchar('\n'); msg_puts(_("Enter number of swap file to use (0 to quit): ")); i = get_number(false, NULL); @@ -804,7 +809,7 @@ void ml_recover(bool checkext) } } // get the swapfile name that will be used - (void)recover_names(fname, false, NULL, i, &fname_used); + recover_names(fname, false, NULL, i, &fname_used); } if (fname_used == NULL) { goto theend; // user chose invalid number. @@ -975,7 +980,7 @@ void ml_recover(bool checkext) set_fileformat(b0_ff - 1, OPT_LOCAL); } if (b0_fenc != NULL) { - set_option_value_give_err("fenc", CSTR_AS_OPTVAL(b0_fenc), OPT_LOCAL); + set_option_value_give_err(kOptFileencoding, CSTR_AS_OPTVAL(b0_fenc), OPT_LOCAL); xfree(b0_fenc); } unchanged(curbuf, true, true); @@ -1292,7 +1297,7 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn // Isolate a directory name from *dirp and put it in dir_name (we know // it is large enough, so use 31000 for length). // Advance dirp to next directory name. - (void)copy_option_part(&dirp, dir_name, 31000, ","); + copy_option_part(&dirp, dir_name, 31000, ","); if (dir_name[0] == '.' && dir_name[1] == NUL) { // check current dir if (fname == NULL) { @@ -1403,7 +1408,7 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn msg_puts(". "); msg_puts(path_tail(files[i])); msg_putchar('\n'); - (void)swapfile_info(files[i]); + swapfile_info(files[i]); } } else { msg_puts(_(" -- none --\n")); @@ -1642,7 +1647,7 @@ static bool swapfile_unchanged(char *fname) return ret; } -static int recov_file_names(char **names, char *path, int prepend_dot) +static int recov_file_names(char **names, char *path, bool prepend_dot) FUNC_ATTR_NONNULL_ALL { int num_names = 0; @@ -1689,9 +1694,9 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL) { continue; // no file } - ml_flush_line(buf); // flush buffered line + ml_flush_line(buf, false); // flush buffered line // flush locked block - (void)ml_find_line(buf, 0, ML_FLUSH); + ml_find_line(buf, 0, ML_FLUSH); if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp) && buf->b_ffname != NULL) { // If the original file does not exist anymore or has been changed @@ -1707,8 +1712,8 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) } } if (buf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES) { - (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0) - | (do_fsync && bufIsChanged(buf) ? MFS_FLUSH : 0)); + mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0) + | (do_fsync && bufIsChanged(buf) ? MFS_FLUSH : 0)); if (check_char && os_char_avail()) { // character available now break; } @@ -1724,7 +1729,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) /// changed or deleted. /// /// @param message if true, the success of preserving is reported. -void ml_preserve(buf_T *buf, int message, bool do_fsync) +void ml_preserve(buf_T *buf, bool message, bool do_fsync) { memfile_T *mfp = buf->b_ml.ml_mfp; int got_int_save = got_int; @@ -1740,8 +1745,8 @@ void ml_preserve(buf_T *buf, int message, bool do_fsync) // before. got_int = false; - ml_flush_line(buf); // flush buffered line - (void)ml_find_line(buf, 0, ML_FLUSH); // flush locked block + ml_flush_line(buf, false); // flush buffered line + ml_find_line(buf, 0, ML_FLUSH); // flush locked block int status = mf_sync(mfp, MFS_ALL | (do_fsync ? MFS_FLUSH : 0)); // stack is invalid after mf_sync(.., MFS_ALL) @@ -1768,7 +1773,7 @@ void ml_preserve(buf_T *buf, int message, bool do_fsync) CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum"); lnum = buf->b_ml.ml_locked_high + 1; } - (void)ml_find_line(buf, 0, ML_FLUSH); // flush locked block + ml_find_line(buf, 0, ML_FLUSH); // flush locked block // sync the updated pointer blocks if (mf_sync(mfp, MFS_ALL | (do_fsync ? MFS_FLUSH : 0)) == FAIL) { status = FAIL; @@ -1857,7 +1862,7 @@ static char *ml_get_buf_impl(buf_T *buf, linenr_T lnum, bool will_change) siemsg(_(e_ml_get_invalid_lnum_nr), (int64_t)lnum); recursive--; } - ml_flush_line(buf); + ml_flush_line(buf, false); errorret: STRCPY(questions, "???"); buf->b_ml.ml_line_lnum = lnum; @@ -1876,7 +1881,7 @@ errorret: // Don't use the last used line when 'swapfile' is reset, need to load all // blocks. if (buf->b_ml.ml_line_lnum != lnum) { - ml_flush_line(buf); + ml_flush_line(buf, false); // Find the data block containing the line. // This also fills the stack with the blocks from the root to the data @@ -1959,7 +1964,7 @@ int ml_append(linenr_T lnum, char *line, colnr_T len, bool newfile) } if (curbuf->b_ml.ml_line_lnum != 0) { - ml_flush_line(curbuf); + ml_flush_line(curbuf, false); } return ml_append_int(curbuf, lnum, line, len, newfile, false); } @@ -1979,7 +1984,7 @@ int ml_append_buf(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfi } if (buf->b_ml.ml_line_lnum != 0) { - ml_flush_line(buf); + ml_flush_line(buf, false); } return ml_append_int(buf, lnum, line, len, newfile, false); } @@ -1989,7 +1994,8 @@ int ml_append_buf(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfi /// @param len length of line, including NUL, or 0 /// @param newfile flag, see above /// @param mark mark the new line -static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfile, int mark) +static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfile, + bool mark) { // lnum out of range if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL) { @@ -2242,7 +2248,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo // pointer blocks is done below int lineadd = buf->b_ml.ml_locked_lineadd; buf->b_ml.ml_locked_lineadd = 0; - (void)ml_find_line(buf, 0, ML_FLUSH); // flush data block + ml_find_line(buf, 0, ML_FLUSH); // flush data block // update pointer blocks for the new data block for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; stack_idx--) { @@ -2289,8 +2295,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo // fix line count for rest of blocks in the stack ml_lineadd(buf, lineadd); // fix stack itself - buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high += - lineadd; + buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high += lineadd; (buf->b_ml.ml_stack_top)++; } @@ -2418,7 +2423,7 @@ void ml_add_deleted_len_buf(buf_T *buf, char *ptr, ssize_t len) int ml_replace(linenr_T lnum, char *line, bool copy) { - return ml_replace_buf(curbuf, lnum, line, copy); + return ml_replace_buf(curbuf, lnum, line, copy, false); } /// Replace line "lnum", with buffering, in current buffer. @@ -2433,7 +2438,7 @@ int ml_replace(linenr_T lnum, char *line, bool copy) /// changed_lines(), unless update_screen(UPD_NOT_VALID) is used. /// /// @return FAIL for failure, OK otherwise -int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy) +int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy, bool noalloc) { if (line == NULL) { // just checking... return FAIL; @@ -2445,12 +2450,13 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy) } if (copy) { + assert(!noalloc); line = xstrdup(line); } if (buf->b_ml.ml_line_lnum != lnum) { // another line is buffered, flush it - ml_flush_line(buf); + ml_flush_line(buf, false); } if (kv_size(buf->update_callbacks)) { @@ -2464,6 +2470,13 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy) buf->b_ml.ml_line_ptr = line; buf->b_ml.ml_line_lnum = lnum; buf->b_ml.ml_flags = (buf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY; + if (noalloc) { + // TODO(bfredl): this is a bit of a hack. but replacing lines in a loop is really common, + // and allocating a separate scratch buffer for each line which is immediately freed adds + // a lot of noise. A more general refactor could be to use a _fixed_ scratch buffer for + // all lines up to $REASONABLE_SIZE . + ml_flush_line(buf, true); + } return OK; } @@ -2478,7 +2491,7 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy) /// @return FAIL for failure, OK otherwise int ml_delete(linenr_T lnum, bool message) { - ml_flush_line(curbuf); + ml_flush_line(curbuf, false); return ml_delete_int(curbuf, lnum, message); } @@ -2491,7 +2504,7 @@ int ml_delete(linenr_T lnum, bool message) /// @return FAIL for failure, OK otherwise int ml_delete_buf(buf_T *buf, linenr_T lnum, bool message) { - ml_flush_line(buf); + ml_flush_line(buf, false); return ml_delete_int(buf, lnum, message); } @@ -2511,7 +2524,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) set_keep_msg(_(no_lines_msg), 0); } - int i = ml_replace_buf(buf, 1, "", true); + int i = ml_replace_buf(buf, 1, "", true, false); buf->b_ml.ml_flags |= ML_EMPTY; return i; @@ -2719,7 +2732,7 @@ size_t ml_flush_deleted_bytes(buf_T *buf, size_t *codepoints, size_t *codeunits) } /// flush ml_line if necessary -static void ml_flush_line(buf_T *buf) +static void ml_flush_line(buf_T *buf, bool noalloc) { static bool entered = false; @@ -2788,15 +2801,18 @@ static void ml_flush_line(buf_T *buf) // that has only one line. // Don't forget to copy the mark! // How about handling errors??? - (void)ml_append_int(buf, lnum, new_line, new_len, false, - (int)(dp->db_index[idx] & DB_MARKED)); - (void)ml_delete_int(buf, lnum, false); + ml_append_int(buf, lnum, new_line, new_len, false, + (int)(dp->db_index[idx] & DB_MARKED)); + ml_delete_int(buf, lnum, false); } } - xfree(new_line); + if (!noalloc) { + xfree(new_line); + } entered = false; } else if (buf->b_ml.ml_flags & ML_ALLOCATED) { + assert(!noalloc); // caller must set ML_LINE_DIRTY with noalloc, handled above xfree(buf->b_ml.ml_line_ptr); } @@ -3208,7 +3224,7 @@ static void attention_message(buf_T *buf, char *fname) assert(buf->b_fname != NULL); no_wait_return++; - (void)emsg(_("E325: ATTENTION")); + emsg(_("E325: ATTENTION")); msg_puts(_("\nFound a swap file by the name \"")); msg_home_replace(fname); msg_puts("\"\n"); @@ -3311,7 +3327,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ // First allocate some memory to put the directory name in. const size_t dir_len = strlen(*dirp) + 1; char *dir_name = xmalloc(dir_len); - (void)copy_option_part(dirp, dir_name, dir_len, ","); + copy_option_part(dirp, dir_name, dir_len, ","); // We try different swapfile names until we find one that does not exist yet. char *fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name); @@ -3348,7 +3364,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ if (!recoverymode && buf_fname != NULL && !buf->b_help && !(buf->b_flags & BF_DUMMY)) { int fd; ZeroBlock b0; - int differ = false; + bool differ = false; // Try to read block 0 from the swapfile to get the original file name (and inode number). fd = os_open(fname, O_RDONLY, 0); @@ -3387,7 +3403,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ // Show the ATTENTION message when: // - there is an old swapfile for the current file // - the buffer was not recovered - if (differ == false && !(curbuf->b_flags & BF_RECOVERED) + if (!differ && !(curbuf->b_flags & BF_RECOVERED) && vim_strchr(p_shm, SHM_ATTENTION) == NULL) { sea_choice_T choice = SEA_CHOICE_NONE; @@ -3898,7 +3914,7 @@ int ml_find_line_or_offset(buf_T *buf, linenr_T lnum, int *offp, bool no_ff) // was never cached to start with anyway). bool can_cache = (lnum != 0 && !ffdos && buf->b_ml.ml_line_lnum == lnum); if (lnum == 0 || buf->b_ml.ml_line_lnum < lnum || !no_ff) { - ml_flush_line(curbuf); + ml_flush_line(curbuf, false); } else if (can_cache && buf->b_ml.ml_line_offset > 0) { return (int)buf->b_ml.ml_line_offset; } @@ -3908,7 +3924,7 @@ int ml_find_line_or_offset(buf_T *buf, linenr_T lnum, int *offp, bool no_ff) || lnum < 0) { // memline is currently empty. Although if it is loaded, // it behaves like there is one empty line. - if (!ffdos && buf->b_ml.ml_mfp && (lnum == 1 || lnum == 2)) { + if (no_ff && buf->b_ml.ml_mfp && (lnum == 1 || lnum == 2)) { return lnum - 1; } return -1; @@ -4025,7 +4041,7 @@ void goto_byte(int cnt) { int boff = cnt; - ml_flush_line(curbuf); // cached line may be dirty + ml_flush_line(curbuf, false); // cached line may be dirty setpcmark(); if (boff) { boff--; diff --git a/src/nvim/memline.h b/src/nvim/memline.h index e70a8e423e..23cbf75c19 100644 --- a/src/nvim/memline.h +++ b/src/nvim/memline.h @@ -1,10 +1,14 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep -#include "nvim/memline_defs.h" // IWYU pragma: export +#include "nvim/ascii_defs.h" +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep +#include "nvim/memline_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memline.h.generated.h" #endif + +/// LINEEMPTY() - return true if the line is empty +#define LINEEMPTY(p) (*ml_get(p) == NUL) diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h index f95dc7a2e5..1a217c96d4 100644 --- a/src/nvim/memline_defs.h +++ b/src/nvim/memline_defs.h @@ -1,20 +1,21 @@ #pragma once #include "nvim/memfile_defs.h" +#include "nvim/pos_defs.h" /// /// When searching for a specific line, we remember what blocks in the tree /// are the branches leading to that block. This is stored in ml_stack. Each /// entry is a pointer to info in a block (may be data block or pointer block) /// -typedef struct info_pointer { +typedef struct { blocknr_T ip_bnum; // block number linenr_T ip_low; // lowest lnum in this block linenr_T ip_high; // highest lnum in this block int ip_index; // index for block with current lnum } infoptr_T; // block/index pair -typedef struct ml_chunksize { +typedef struct { int mlcs_numlines; int mlcs_totalsize; } chunksize_T; @@ -39,7 +40,7 @@ typedef struct ml_chunksize { /// memory. With this structure it is roughly (N * 128) pointer /// moves, where N is the height (typically 1-3). /// -typedef struct memline { +typedef struct { linenr_T ml_line_count; // number of lines in the buffer memfile_T *ml_mfp; // pointer to associated memfile diff --git a/src/nvim/memory.c b/src/nvim/memory.c index df6c81fe0d..37e53e4453 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -9,29 +9,38 @@ #include <time.h> #include "nvim/api/extmark.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/ui.h" #include "nvim/arglist.h" #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" +#include "nvim/channel.h" #include "nvim/context.h" #include "nvim/decoration_provider.h" #include "nvim/drawline.h" #include "nvim/eval.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/main.h" +#include "nvim/map_defs.h" #include "nvim/mapping.h" #include "nvim/memfile.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option_vars.h" +#include "nvim/os/input.h" #include "nvim/sign.h" #include "nvim/state_defs.h" +#include "nvim/statusline.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_client.h" +#include "nvim/ui_compositor.h" #include "nvim/usercmd.h" #include "nvim/vim_defs.h" @@ -279,7 +288,8 @@ void strchrsub(char *str, char c, char x) void memchrsub(void *data, char c, char x, size_t len) FUNC_ATTR_NONNULL_ALL { - char *p = data, *end = (char *)data + len; + char *p = data; + char *end = (char *)data + len; while ((p = memchr(p, c, (size_t)(end - p)))) { *p++ = x; } @@ -314,7 +324,8 @@ size_t memcnt(const void *data, char c, size_t len) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE { size_t cnt = 0; - const char *ptr = data, *end = ptr + len; + const char *ptr = data; + const char *end = ptr + len; while ((ptr = memchr(ptr, c, (size_t)(end - ptr))) != NULL) { cnt++; ptr++; // Skip the instance of c. @@ -508,6 +519,13 @@ bool strequal(const char *a, const char *b) return (a == NULL && b == NULL) || (a && b && strcmp(a, b) == 0); } +/// Returns true if first `n` characters of strings `a` and `b` are equal. Arguments may be NULL. +bool strnequal(const char *a, const char *b, size_t n) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (a == NULL && b == NULL) || (a && b && strncmp(a, b, n) == 0); +} + // Avoid repeating the error message many times (they take 1 second each). // Did_outofmem_msg is reset when a character is read. void do_outofmem_msg(size_t size) @@ -536,7 +554,6 @@ void time_to_bytes(time_t time_, uint8_t buf[8]) } } -#define ARENA_BLOCK_SIZE 4096 #define REUSE_MAX 4 static struct consumed_blk *arena_reuse_blk; @@ -565,17 +582,26 @@ ArenaMem arena_finish(Arena *arena) return res; } -void alloc_block(Arena *arena) +/// allocate a block of ARENA_BLOCK_SIZE +/// +/// free it with free_block +void *alloc_block(void) { - struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk; if (arena_reuse_blk_count > 0) { - arena->cur_blk = (char *)arena_reuse_blk; + void *retval = (char *)arena_reuse_blk; arena_reuse_blk = arena_reuse_blk->prev; arena_reuse_blk_count--; + return retval; } else { arena_alloc_count++; - arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE); + return xmalloc(ARENA_BLOCK_SIZE); } +} + +void arena_alloc_block(Arena *arena) +{ + struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk; + arena->cur_blk = alloc_block(); arena->pos = 0; arena->size = ARENA_BLOCK_SIZE; struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true); @@ -597,7 +623,7 @@ void *arena_alloc(Arena *arena, size_t size, bool align) return xmalloc(size); } if (!arena->cur_blk) { - alloc_block(arena); + arena_alloc_block(arena); } size_t alloc_pos = align ? arena_align_offset(arena->pos) : arena->pos; if (alloc_pos + size > arena->size) { @@ -619,7 +645,7 @@ void *arena_alloc(Arena *arena, size_t size, bool align) cur_blk->prev = fix_blk; return alloc + aligned_hdr_size; } else { - alloc_block(arena); // resets arena->pos + arena_alloc_block(arena); // resets arena->pos alloc_pos = align ? arena_align_offset(arena->pos) : arena->pos; } } @@ -629,17 +655,27 @@ void *arena_alloc(Arena *arena, size_t size, bool align) return mem; } +void free_block(void *block) +{ + if (arena_reuse_blk_count < REUSE_MAX) { + struct consumed_blk *reuse_blk = block; + reuse_blk->prev = arena_reuse_blk; + arena_reuse_blk = reuse_blk; + arena_reuse_blk_count++; + } else { + xfree(block); + } +} + void arena_mem_free(ArenaMem mem) { struct consumed_blk *b = mem; // peel of the first block, as it is guaranteed to be ARENA_BLOCK_SIZE, // not a custom fix_blk - if (arena_reuse_blk_count < REUSE_MAX && b != NULL) { + if (b != NULL) { struct consumed_blk *reuse_blk = b; b = b->prev; - reuse_blk->prev = arena_reuse_blk; - arena_reuse_blk = reuse_blk; - arena_reuse_blk_count++; + free_block(reuse_blk); } while (b) { @@ -649,14 +685,20 @@ void arena_mem_free(ArenaMem mem) } } -char *arena_memdupz(Arena *arena, const char *buf, size_t size) +char *arena_allocz(Arena *arena, size_t size) { char *mem = arena_alloc(arena, size + 1, false); - memcpy(mem, buf, size); mem[size] = NUL; return mem; } +char *arena_memdupz(Arena *arena, const char *buf, size_t size) +{ + char *mem = arena_allocz(arena, size); + memcpy(mem, buf, size); + return mem; +} + #if defined(EXITFREE) # include "nvim/autocmd.h" @@ -705,13 +747,6 @@ void free_all_mem(void) do_cmdline_cmd("tabonly!"); } - if (!ONE_WINDOW) { - // to keep things simple, don't perform this - // ritual inside a float - curwin = firstwin; - do_cmdline_cmd("only!"); - } - // Free all spell info. spell_free_all(); @@ -739,6 +774,7 @@ void free_all_mem(void) free_all_marks(); alist_clear(&global_alist); free_homedir(); + free_envmap(); free_users(); free_search_patterns(); free_old_sub(); @@ -773,6 +809,25 @@ void free_all_mem(void) // Free all option values. Must come after closing windows. free_all_options(); + // Free all buffers. Reset 'autochdir' to avoid accessing things that + // were freed already. + // Must be after eval_clear to avoid it trying to access b:changedtick after + // freeing it. + p_acd = false; + for (buf = firstbuf; buf != NULL;) { + bufref_T bufref; + set_bufref(&bufref, buf); + nextbuf = buf->b_next; + + // Since options (in addition to other stuff) have been freed above we need to ensure no + // callbacks are called, so free them before closing the buffer. + buf_free_callbacks(buf); + + close_buffer(NULL, buf, DOBUF_WIPE, false, false); + // Didn't work, try next one. + buf = bufref_valid(&bufref) ? nextbuf : firstbuf; + } + // Clear registers. clear_registers(); ResetRedobuff(); @@ -793,31 +848,19 @@ void free_all_mem(void) } } + channel_free_all_mem(); eval_clear(); api_extmark_free_all_mem(); ctx_free_all(); - // Free all buffers. Reset 'autochdir' to avoid accessing things that - // were freed already. - // Must be after eval_clear to avoid it trying to access b:changedtick after - // freeing it. - p_acd = false; - for (buf = firstbuf; buf != NULL;) { - bufref_T bufref; - set_bufref(&bufref, buf); - nextbuf = buf->b_next; - - // Since options (in addition to other stuff) have been freed above we need to ensure no - // callbacks are called, so free them before closing the buffer. - buf_free_callbacks(buf); - - close_buffer(NULL, buf, DOBUF_WIPE, false, false); - // Didn't work, try next one. - buf = bufref_valid(&bufref) ? nextbuf : firstbuf; - } + map_destroy(int, &buffer_handles); + map_destroy(int, &window_handles); + map_destroy(int, &tabpage_handles); // free screenlines (can't display anything now!) grid_free_all_mem(); + stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size); + xfree(tab_page_click_defs); clear_hl_tables(false); @@ -825,8 +868,15 @@ void free_all_mem(void) decor_free_all_mem(); drawline_free_all_mem(); + input_free_all_mem(); + + if (ui_client_channel_id) { + ui_client_free_all_mem(); + } + remote_ui_free_all_mem(); ui_free_all_mem(); + ui_comp_free_all_mem(); nlua_free_all_mem(); rpc_free_all_mem(); diff --git a/src/nvim/memory.h b/src/nvim/memory.h index ffdc4c7366..0788670142 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -6,7 +6,7 @@ #include "auto/config.h" #include "nvim/macros_defs.h" -#include "nvim/memory_defs.h" // IWYU pragma: export +#include "nvim/memory_defs.h" // IWYU pragma: keep /// `malloc()` function signature typedef void *(*MemMalloc)(size_t); @@ -45,6 +45,8 @@ EXTERN size_t arena_alloc_count INIT( = 0); ((v).capacity = (s), \ (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true)) +#define ARENA_BLOCK_SIZE 4096 + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.h.generated.h" #endif diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 3252a73970..4ca2a61ab1 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -8,22 +8,28 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/menu_defs.h" @@ -32,6 +38,7 @@ #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" @@ -69,7 +76,7 @@ void ex_menu(exarg_T *eap) char *map_to; // command mapped to the menu entry int noremap; bool silent = false; - int unmenu; + bool unmenu; char *map_buf; char *p; int i; @@ -226,7 +233,7 @@ void ex_menu(exarg_T *eap) } else { map_buf = NULL; map_to = replace_termcodes(map_to, strlen(map_to), &map_buf, 0, - REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); + REPTERM_DO_LT, NULL, p_cpo); } menuarg.modes = modes; menuarg.noremap[0] = noremap; @@ -868,7 +875,7 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for char *after_dot; char *p; char *path_name = NULL; - int unmenu; + bool unmenu; vimmenu_T *menu; xp->xp_context = EXPAND_UNSUCCESSFUL; @@ -969,7 +976,7 @@ char *get_menu_name(expand_T *xp, int idx) { static vimmenu_T *menu = NULL; char *str; - static int should_advance = false; + static bool should_advance = false; if (idx == 0) { // first call: start at first item menu = expand_menu; @@ -1132,7 +1139,7 @@ static bool menu_namecmp(const char *const name, const char *const mname) /// to whether the command is a "nore" command. /// @param[out] unmenu If not NULL, the flag it points to is set according /// to whether the command is an "unmenu" command. -int get_menu_cmd_modes(const char *cmd, bool forceit, int *noremap, int *unmenu) +int get_menu_cmd_modes(const char *cmd, bool forceit, int *noremap, bool *unmenu) { int modes; @@ -1335,9 +1342,9 @@ bool menu_is_toolbar(const char *const name) return strncmp(name, "ToolBar", 7) == 0; } -/// Return true if the name is a menu separator identifier: Starts and ends -/// with '-' -int menu_is_separator(char *name) +/// @return true if the name is a menu separator identifier: Starts and ends +/// with '-' +bool menu_is_separator(char *name) { return name[0] == '-' && name[strlen(name) - 1] == '-'; } @@ -1345,7 +1352,7 @@ int menu_is_separator(char *name) /// True if a popup menu or starts with \ref MNU_HIDDEN_CHAR /// /// @return true if the menu is hidden -static int menu_is_hidden(char *name) +static bool menu_is_hidden(char *name) { return (name[0] == MNU_HIDDEN_CHAR) || (menu_is_popup(name) && name[5] != NUL); diff --git a/src/nvim/menu.h b/src/nvim/menu.h index 9644386003..08f5601ca3 100644 --- a/src/nvim/menu.h +++ b/src/nvim/menu.h @@ -2,7 +2,7 @@ #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep -#include "nvim/menu_defs.h" // IWYU pragma: export +#include "nvim/menu_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/message.c b/src/nvim/message.c index 3268ff389a..175e3b5d03 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -8,8 +8,10 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> +#include "klib/kvec.h" +#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" @@ -18,25 +20,28 @@ #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_eval.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/indent.h" #include "nvim/input.h" #include "nvim/keycodes.h" #include "nvim/log.h" #include "nvim/main.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" @@ -50,11 +55,13 @@ #include "nvim/pos_defs.h" #include "nvim/regexp.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" +#include "nvim/ui_defs.h" #include "nvim/vim_defs.h" // To be able to scroll back at the "more" and "hit-enter" prompts we need to @@ -87,7 +94,7 @@ MessageHistoryEntry *last_msg_hist = NULL; static int msg_hist_len = 0; static FILE *verbose_fd = NULL; -static int verbose_did_open = false; +static bool verbose_did_open = false; bool keep_msg_more = false; // keep_msg was set by msgmore() @@ -122,7 +129,7 @@ bool keep_msg_more = false; // keep_msg was set by msgmore() // Set: When the ruler or typeahead display is overwritten, // scrolling the screen for some message. // keep_msg Message to be displayed after redrawing the screen, in -// main_loop(). +// Normal mode main loop. // This is an allocated string or NULL when not used. // Extended msg state, currently used for external UIs with ext_messages @@ -144,9 +151,8 @@ static int msg_grid_pos_at_flush = 0; static void ui_ext_msg_set_pos(int row, bool scrolled) { - char buf[MB_MAXCHAR + 1]; - size_t size = (size_t)utf_char2bytes(curwin->w_p_fcs_chars.msgsep, buf); - buf[size] = '\0'; + char buf[MAX_SCHAR_SIZE]; + size_t size = schar_get(buf, curwin->w_p_fcs_chars.msgsep); ui_call_msg_set_pos(msg_grid.handle, row, scrolled, (String){ .data = buf, .size = size }); } @@ -204,8 +210,13 @@ void msg_grid_validate(void) msg_grid_adj.target = &default_grid; redraw_cmdline = true; } else if (msg_grid.chars && !msg_scrolled && msg_grid_pos != max_rows) { + int diff = msg_grid_pos - max_rows; msg_grid_set_pos(max_rows, false); + if (diff > 0) { + grid_clear(&msg_grid_adj, Rows - diff, Rows, 0, Columns, HL_ATTR(HLF_MSG)); + } } + msg_grid_adj.cols = Columns; if (msg_grid.chars && !msg_scrolled && cmdline_row < msg_grid_pos) { // TODO(bfredl): this should already be the case, but fails in some @@ -226,10 +237,10 @@ int verb_msg(const char *s) } /// Displays the string 's' on the status line -/// When terminal not initialized (yet) os_errmsg(..) is used. +/// When terminal not initialized (yet) printf("%s", ..) is used. /// /// @return true if wait_return() not called -int msg(const char *s, const int attr) +bool msg(const char *s, const int attr) FUNC_ATTR_NONNULL_ARG(1) { return msg_attr_keep(s, attr, false, false); @@ -290,7 +301,6 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) FUNC_ATTR_NONNULL_ALL { static int entered = 0; - char *buf = NULL; if (keep && multiline) { // Not implemented. 'multiline' is only used by nvim-added messages, @@ -329,7 +339,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) // Truncate the message if needed. msg_start(); - buf = msg_strtrunc(s, false); + char *buf = msg_strtrunc(s, false); if (buf != NULL) { s = buf; } @@ -343,7 +353,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) if (need_clear) { msg_clr_eos(); } - int retval = msg_end(); + bool retval = msg_end(); if (keep && retval && vim_strsize(s) < (Rows - cmdline_row - 1) * Columns + sc_col) { set_keep_msg(s, 0); @@ -462,7 +472,7 @@ void trunc_string(const char *s, char *buf, int room_in, int buflen) } } else if (e + 3 < buflen) { // set the middle and copy the last part - memmove(buf + e, "...", (size_t)3); + memmove(buf + e, "...", 3); len = (int)strlen(s + i) + 1; if (len >= buflen - e - 3) { len = buflen - e - 3 - 1; @@ -749,7 +759,7 @@ bool emsg_multiline(const char *s, bool multiline) /// emsg() - display an error message /// /// Rings the bell, if appropriate, and calls message() to do the real work -/// When terminal not initialized (yet) os_errmsg(..) is used. +/// When terminal not initialized (yet) fprintf(stderr, "%s", ..) is used. /// /// @return true if wait_return() not called bool emsg(const char *s) @@ -841,7 +851,7 @@ void siemsg(const char *s, ...) va_list ap; va_start(ap, s); - (void)semsgv(s, ap); + semsgv(s, ap); va_end(ap); #ifdef ABORT_ON_INTERNAL_ERROR msg_putchar('\n'); // avoid overwriting the error message @@ -859,7 +869,7 @@ void internal_error(const char *where) static void msg_semsg_event(void **argv) { char *s = argv[0]; - (void)emsg(s); + emsg(s); xfree(s); } @@ -872,13 +882,13 @@ void msg_schedule_semsg(const char *const fmt, ...) va_end(ap); char *s = xstrdup(IObuff); - loop_schedule_deferred(&main_loop, event_create(msg_semsg_event, 1, s)); + loop_schedule_deferred(&main_loop, event_create(msg_semsg_event, s)); } static void msg_semsg_multiline_event(void **argv) { char *s = argv[0]; - (void)emsg_multiline(s, true); + emsg_multiline(s, true); xfree(s); } @@ -890,7 +900,7 @@ void msg_schedule_semsg_multiline(const char *const fmt, ...) va_end(ap); char *s = xstrdup(IObuff); - loop_schedule_deferred(&main_loop, event_create(msg_semsg_multiline_event, 1, s)); + loop_schedule_deferred(&main_loop, event_create(msg_semsg_multiline_event, s)); } /// Like msg(), but truncate to a single line if p_shm contains 't', or when @@ -906,7 +916,7 @@ char *msg_trunc(char *s, bool force, int attr) char *ts = msg_may_trunc(force, s); msg_hist_off = true; - int n = msg(ts, attr); + bool n = msg(ts, attr); msg_hist_off = false; if (n) { @@ -971,7 +981,7 @@ static void add_msg_hist_multiattr(const char *s, int len, int attr, bool multil // Don't let the message history get too big while (msg_hist_len > MAX_MSG_HIST_LEN) { - (void)delete_first_msg(); + delete_first_msg(); } // allocate an entry and add the message at the end of the history @@ -1036,7 +1046,7 @@ void ex_messages(exarg_T *eap) int keep = eap->addr_count == 0 ? 0 : eap->line2; while (msg_hist_len > keep) { - (void)delete_first_msg(); + delete_first_msg(); } return; } @@ -1251,7 +1261,7 @@ void wait_return(int redraw) // Avoid that the mouse-up event causes visual mode to start. if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE || c == K_X1MOUSE || c == K_X2MOUSE) { - (void)jump_to_mouse(MOUSE_SETPOS, NULL, 0); + jump_to_mouse(MOUSE_SETPOS, NULL, 0); } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) { // Put the character back in the typeahead buffer. Don't use the // stuff buffer, because lmaps wouldn't work. @@ -1703,6 +1713,39 @@ char *str2special_save(const char *const str, const bool replace_spaces, const b return (char *)ga.ga_data; } +/// Convert string, replacing key codes with printables +/// +/// Used for lhs or rhs of mappings. +/// +/// @param[in] str String to convert. +/// @param[in] replace_spaces Convert spaces into `<Space>`, normally used for +/// lhs of mapping and keytrans(), but not rhs. +/// @param[in] replace_lt Convert `<` into `<lt>`. +/// +/// @return [allocated] Converted string. +char *str2special_arena(const char *str, bool replace_spaces, bool replace_lt, Arena *arena) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_NONNULL_RET +{ + const char *p = str; + size_t len = 0; + while (*p) { + len += strlen(str2special(&p, replace_spaces, replace_lt)); + } + + char *buf = arena_alloc(arena, len + 1, false); + size_t pos = 0; + p = str; + while (*p) { + const char *s = str2special(&p, replace_spaces, replace_lt); + size_t s_len = strlen(s); + memcpy(buf + pos, s, s_len); + pos += s_len; + } + buf[pos] = NUL; + return buf; +} + /// Convert character, replacing key with printable representation. /// /// @param[in,out] sp String to convert. Is advanced to the next key code. @@ -1798,14 +1841,14 @@ void str2specialbuf(const char *sp, char *buf, size_t len) } /// print line for :print or :list command -void msg_prt_line(const char *s, int list) +void msg_prt_line(const char *s, bool list) { - int c; + schar_T sc; int col = 0; int n_extra = 0; - int c_extra = 0; - int c_final = 0; - const char *p_extra = NULL; // init to make SASC shut up + schar_T sc_extra = 0; + schar_T sc_final = 0; + const char *p_extra = NULL; // init to make SASC shut up. ASCII only! int n; int attr = 0; const char *lead = NULL; @@ -1847,13 +1890,13 @@ void msg_prt_line(const char *s, int list) while (!got_int) { if (n_extra > 0) { n_extra--; - if (n_extra == 0 && c_final) { - c = c_final; - } else if (c_extra) { - c = c_extra; + if (n_extra == 0 && sc_final) { + sc = sc_final; + } else if (sc_extra) { + sc = sc_extra; } else { assert(p_extra != NULL); - c = (unsigned char)(*p_extra++); + sc = schar_from_ascii((unsigned char)(*p_extra++)); } } else if ((l = utfc_ptr2len(s)) > 1) { col += utf_ptr2cells(s); @@ -1861,10 +1904,8 @@ void msg_prt_line(const char *s, int list) if (l >= MB_MAXBYTES) { xstrlcpy(buf, "?", sizeof(buf)); } else if (curwin->w_p_lcs_chars.nbsp != NUL && list - && (utf_ptr2char(s) == 160 - || utf_ptr2char(s) == 0x202f)) { - int len = utf_char2bytes(curwin->w_p_lcs_chars.nbsp, buf); - buf[len] = NUL; + && (utf_ptr2char(s) == 160 || utf_ptr2char(s) == 0x202f)) { + schar_get(buf, curwin->w_p_lcs_chars.nbsp); } else { memmove(buf, s, (size_t)l); buf[l] = NUL; @@ -1874,7 +1915,9 @@ void msg_prt_line(const char *s, int list) continue; } else { attr = 0; - c = (uint8_t)(*s++); + int c = (uint8_t)(*s++); + sc_extra = NUL; + sc_final = NUL; if (list) { in_multispace = c == ' ' && (*s == ' ' || (col > 0 && s[-2] == ' ')); @@ -1884,74 +1927,72 @@ void msg_prt_line(const char *s, int list) } if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) { // tab amount depends on current column - n_extra = tabstop_padding(col, - curbuf->b_p_ts, + n_extra = tabstop_padding(col, curbuf->b_p_ts, curbuf->b_p_vts_array) - 1; if (!list) { - c = ' '; - c_extra = ' '; - c_final = NUL; + sc = schar_from_ascii(' '); + sc_extra = schar_from_ascii(' '); } else { - c = (n_extra == 0 && curwin->w_p_lcs_chars.tab3) - ? curwin->w_p_lcs_chars.tab3 - : curwin->w_p_lcs_chars.tab1; - c_extra = curwin->w_p_lcs_chars.tab2; - c_final = curwin->w_p_lcs_chars.tab3; + sc = (n_extra == 0 && curwin->w_p_lcs_chars.tab3) + ? curwin->w_p_lcs_chars.tab3 + : curwin->w_p_lcs_chars.tab1; + sc_extra = curwin->w_p_lcs_chars.tab2; + sc_final = curwin->w_p_lcs_chars.tab3; attr = HL_ATTR(HLF_0); } - } else if (c == 160 && list && curwin->w_p_lcs_chars.nbsp != NUL) { - c = curwin->w_p_lcs_chars.nbsp; - attr = HL_ATTR(HLF_0); } else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) { p_extra = ""; - c_extra = NUL; - c_final = NUL; n_extra = 1; - c = curwin->w_p_lcs_chars.eol; + sc = curwin->w_p_lcs_chars.eol; attr = HL_ATTR(HLF_AT); s--; } else if (c != NUL && (n = byte2cells(c)) > 1) { n_extra = n - 1; p_extra = transchar_byte_buf(NULL, c); - c_extra = NUL; - c_final = NUL; - c = (unsigned char)(*p_extra++); + sc = schar_from_ascii(*p_extra++); // Use special coloring to be able to distinguish <hex> from // the same in plain text. attr = HL_ATTR(HLF_0); } else if (c == ' ') { if (lead != NULL && s <= lead && in_multispace && curwin->w_p_lcs_chars.leadmultispace != NULL) { - c = curwin->w_p_lcs_chars.leadmultispace[multispace_pos++]; + sc = curwin->w_p_lcs_chars.leadmultispace[multispace_pos++]; if (curwin->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) { multispace_pos = 0; } attr = HL_ATTR(HLF_0); } else if (lead != NULL && s <= lead && curwin->w_p_lcs_chars.lead != NUL) { - c = curwin->w_p_lcs_chars.lead; + sc = curwin->w_p_lcs_chars.lead; attr = HL_ATTR(HLF_0); } else if (trail != NULL && s > trail) { - c = curwin->w_p_lcs_chars.trail; + sc = curwin->w_p_lcs_chars.trail; attr = HL_ATTR(HLF_0); } else if (in_multispace && curwin->w_p_lcs_chars.multispace != NULL) { - c = curwin->w_p_lcs_chars.multispace[multispace_pos++]; + sc = curwin->w_p_lcs_chars.multispace[multispace_pos++]; if (curwin->w_p_lcs_chars.multispace[multispace_pos] == NUL) { multispace_pos = 0; } attr = HL_ATTR(HLF_0); } else if (list && curwin->w_p_lcs_chars.space != NUL) { - c = curwin->w_p_lcs_chars.space; + sc = curwin->w_p_lcs_chars.space; attr = HL_ATTR(HLF_0); + } else { + sc = schar_from_ascii(' '); // SPACE! } + } else { + sc = schar_from_ascii(c); } } - if (c == NUL) { + if (sc == NUL) { break; } - msg_putchar_attr(c, attr); + // TODO(bfredl): this is such baloney. need msg_put_schar + char buf[MAX_SCHAR_SIZE]; + schar_get(buf, sc); + msg_puts_attr(buf, attr); col++; } msg_clr_eos(); @@ -2310,7 +2351,7 @@ void msg_scroll_up(bool may_throttle, bool zerocmd) msg_grid.dirty_col[msg_grid.rows - 1] = 0; } - grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', HL_ATTR(HLF_MSG)); + grid_clear(&msg_grid_adj, Rows - 1, Rows, 0, Columns, HL_ATTR(HLF_MSG)); } /// Send throttled message output to UI clients @@ -2354,7 +2395,7 @@ void msg_scroll_flush(void) for (int i = MAX(Rows - MAX(delta, 1), 0); i < Rows; i++) { int row = i - msg_grid_pos; assert(row >= 0); - ui_line(&msg_grid, row, 0, msg_grid.dirty_col[row], msg_grid.cols, + ui_line(&msg_grid, row, false, 0, msg_grid.dirty_col[row], msg_grid.cols, HL_ATTR(HLF_MSG), false); msg_grid.dirty_col[row] = 0; } @@ -2411,9 +2452,7 @@ static void inc_msg_scrolled(void) xfree(tofree); } msg_scrolled++; - if (must_redraw < UPD_VALID) { - must_redraw = UPD_VALID; - } + set_must_redraw(UPD_VALID); } static msgchunk_T *last_msgchunk = NULL; // last displayed text @@ -2524,7 +2563,7 @@ void sb_text_end_cmdline(void) /// Clear any text remembered for scrolling back. /// When "all" is false keep the last line. /// Called when redrawing the screen. -void clear_sb_text(int all) +void clear_sb_text(bool all) { msgchunk_T *mp; msgchunk_T **lastp; @@ -2548,11 +2587,9 @@ void clear_sb_text(int all) /// "g<" command. void show_sb_text(void) { - msgchunk_T *mp; - // Only show something if there is more than one line, otherwise it looks // weird, typing a command without output results in one line. - mp = msg_sb_start(last_msgchunk); + msgchunk_T *mp = msg_sb_start(last_msgchunk); if (mp == NULL || mp->sb_prev == NULL) { vim_beep(BO_MESS); } else { @@ -2638,9 +2675,9 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) memcpy(p, s, (size_t)len); *(p + len) = '\0'; if (info_message) { - os_msg(buf); + printf("%s", buf); } else { - os_errmsg(buf); + fprintf(stderr, "%s", buf); } } @@ -2662,13 +2699,13 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) /// otherwise it's NUL. /// /// @return true when jumping ahead to "confirm_msg_tail". -static int do_more_prompt(int typed_char) +static bool do_more_prompt(int typed_char) { static bool entered = false; int used_typed_char = typed_char; int oldState = State; int c; - int retval = false; + bool retval = false; bool to_redraw = false; msgchunk_T *mp_last = NULL; msgchunk_T *mp; @@ -2827,16 +2864,14 @@ static int do_more_prompt(int typed_char) if (toscroll == -1 && !to_redraw) { grid_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns); - grid_fill(&msg_grid_adj, 0, 1, 0, Columns, ' ', ' ', - HL_ATTR(HLF_MSG)); + grid_clear(&msg_grid_adj, 0, 1, 0, Columns, HL_ATTR(HLF_MSG)); // display line at top - (void)disp_sb_line(0, mp); + disp_sb_line(0, mp); } else { // redisplay all lines // TODO(bfredl): this case is not optimized (though only concerns - // event fragmentization, not unnecessary scroll events). - grid_fill(&msg_grid_adj, 0, Rows, 0, Columns, ' ', ' ', - HL_ATTR(HLF_MSG)); + // event fragmentation, not unnecessary scroll events). + grid_clear(&msg_grid_adj, 0, Rows, 0, Columns, HL_ATTR(HLF_MSG)); for (int i = 0; mp != NULL && i < Rows - 1; i++) { mp = disp_sb_line(i, mp); msg_scrolled++; @@ -2862,8 +2897,7 @@ static int do_more_prompt(int typed_char) // scroll up, display line at bottom msg_scroll_up(true, false); inc_msg_scrolled(); - grid_fill(&msg_grid_adj, Rows - 2, Rows - 1, 0, Columns, ' ', ' ', - HL_ATTR(HLF_MSG)); + grid_clear(&msg_grid_adj, Rows - 2, Rows - 1, 0, Columns, HL_ATTR(HLF_MSG)); mp_last = disp_sb_line(Rows - 2, mp_last); toscroll--; } @@ -2871,8 +2905,7 @@ static int do_more_prompt(int typed_char) if (toscroll <= 0) { // displayed the requested text, more prompt again - grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', - HL_ATTR(HLF_MSG)); + grid_clear(&msg_grid_adj, Rows - 1, Rows, 0, Columns, HL_ATTR(HLF_MSG)); msg_moremsg(false); continue; } @@ -2885,8 +2918,7 @@ static int do_more_prompt(int typed_char) } // clear the --more-- message - grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', - HL_ATTR(HLF_MSG)); + grid_clear(&msg_grid_adj, Rows - 1, Rows, 0, Columns, HL_ATTR(HLF_MSG)); redraw_cmdline = true; clear_cmdline = false; mode_displayed = false; @@ -2902,41 +2934,7 @@ static int do_more_prompt(int typed_char) return retval; } -#if defined(MSWIN) -/// Headless (no UI) error message handler. -static void do_msg(const char *str, bool errmsg) -{ - static bool did_err = false; - assert(str != NULL); - wchar_t *utf16str; - int r = utf8_to_utf16(str, -1, &utf16str); - if (r != 0 && !did_err) { - did_err = true; - fprintf(stderr, "utf8_to_utf16 failed: %d", r); - ELOG("utf8_to_utf16 failed: %d", r); - } else if (r == 0) { - if (errmsg) { - fwprintf(stderr, L"%ls", utf16str); - } else { - wprintf(L"%ls", utf16str); - } - xfree(utf16str); - } -} - -void os_errmsg(const char *str) -{ - do_msg(str, true); -} - -/// Headless (no UI) message handler. -void os_msg(const char *str) -{ - do_msg(str, false); -} -#endif // MSWIN - -void msg_moremsg(int full) +void msg_moremsg(bool full) { int attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M)); grid_line_start(&msg_grid_adj, Rows - 1); @@ -3004,10 +3002,8 @@ void msg_clr_eos_force(void) } } - grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, - ' ', ' ', HL_ATTR(HLF_MSG)); - grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, - ' ', ' ', HL_ATTR(HLF_MSG)); + grid_clear(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, HL_ATTR(HLF_MSG)); + grid_clear(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, HL_ATTR(HLF_MSG)); redraw_cmdline = true; // overwritten the command line if (msg_row < Rows - 1 || msg_col == 0) { @@ -3028,7 +3024,7 @@ void msg_clr_cmdline(void) /// call wait_return() if the message does not fit in the available space /// /// @return true if wait_return() not called. -int msg_end(void) +bool msg_end(void) { // If the string is larger than the window, // or the ruler option is set and we run into it, @@ -3056,7 +3052,7 @@ void msg_ext_ui_flush(void) msg_ext_emit_chunk(); if (msg_ext_chunks.size > 0) { - ui_call_msg_show(cstr_as_string((char *)msg_ext_kind), + ui_call_msg_show(cstr_as_string(msg_ext_kind), msg_ext_chunks, msg_ext_overwrite); if (!msg_ext_overwrite) { msg_ext_visible++; @@ -3102,9 +3098,7 @@ void msg_ext_clear_later(void) { if (msg_ext_is_visible()) { msg_ext_need_clear = true; - if (must_redraw < UPD_VALID) { - must_redraw = UPD_VALID; - } + set_must_redraw(UPD_VALID); } } @@ -3394,7 +3388,6 @@ int do_dialog(int type, const char *title, const char *message, const char *butt const char *textfield, int ex_cmd) { int retval = 0; - char *hotkeys; int i; if (silent_mode // No dialogs in silent mode ("ex -s") @@ -3413,7 +3406,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt // Since we wait for a keypress, don't make the // user press RETURN as well afterwards. no_wait_return++; - hotkeys = msg_show_console_dialog(message, buttons, dfltbutton); + char *hotkeys = msg_show_console_dialog(message, buttons, dfltbutton); while (true) { // Get a typed character directly from the user. diff --git a/src/nvim/message.h b/src/nvim/message.h index adbb40277b..c11c33c039 100644 --- a/src/nvim/message.h +++ b/src/nvim/message.h @@ -3,13 +3,11 @@ #include <errno.h> #include <stdbool.h> #include <stddef.h> // IWYU pragma: keep -#include <stdio.h> -#include "klib/kvec.h" -#include "nvim/api/private/defs.h" #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/grid_defs.h" #include "nvim/macros_defs.h" +#include "nvim/message_defs.h" // IWYU pragma: keep /// Types of dialogs passed to do_dialog(). enum { @@ -30,25 +28,7 @@ enum { VIM_DISCARDALL = 6, }; -/// special attribute addition: Put message in history -enum { MSG_HIST = 0x1000, }; - -typedef struct { - String text; - int attr; -} HlMessageChunk; - -typedef kvec_t(HlMessageChunk) HlMessage; - -/// Message history for `:messages` -typedef struct msg_hist { - struct msg_hist *next; ///< Next message. - char *msg; ///< Message text. - const char *kind; ///< Message kind (for msg_ext) - int attr; ///< Message highlighting. - bool multiline; ///< Multiline message. - HlMessage multiattr; ///< multiattr message. -} MessageHistoryEntry; +enum { MSG_HIST = 0x1000, }; ///< special attribute addition: Put message in history /// First message extern MessageHistoryEntry *first_msg_hist; @@ -84,10 +64,3 @@ EXTERN int msg_listdo_overwrite INIT( = 0); // Prefer using semsg(), because perror() may send the output to the wrong // destination and mess up the screen. #define PERROR(msg) (void)semsg("%s: %s", (msg), strerror(errno)) - -#ifndef MSWIN -/// Headless (no UI) error message handler. -# define os_errmsg(str) fprintf(stderr, "%s", (str)) -/// Headless (no UI) message handler. -# define os_msg(str) printf("%s", (str)) -#endif diff --git a/src/nvim/message_defs.h b/src/nvim/message_defs.h new file mode 100644 index 0000000000..e60e60b3be --- /dev/null +++ b/src/nvim/message_defs.h @@ -0,0 +1,22 @@ +#pragma once + +#include <stdbool.h> + +#include "nvim/api/private/defs.h" + +typedef struct { + String text; + int attr; +} HlMessageChunk; + +typedef kvec_t(HlMessageChunk) HlMessage; + +/// Message history for `:messages` +typedef struct msg_hist { + struct msg_hist *next; ///< Next message. + char *msg; ///< Message text. + const char *kind; ///< Message kind (for msg_ext) + int attr; ///< Message highlighting. + bool multiline; ///< Multiline message. + HlMessage multiattr; ///< multiattr message. +} MessageHistoryEntry; diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 8fe3864424..506a428243 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -15,14 +15,15 @@ #include "nvim/eval/typval.h" #include "nvim/ex_docmd.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" -#include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/menu.h" @@ -38,7 +39,9 @@ #include "nvim/pos_defs.h" #include "nvim/search.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/statusline.h" +#include "nvim/statusline_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" @@ -133,6 +136,26 @@ static void move_tab_to_mouse(void) tabpage_move(tabnr); } } +/// Close the current or specified tab page. +/// +/// @param c1 tabpage number, or 999 for the current tabpage +static void mouse_tab_close(int c1) +{ + tabpage_T *tp; + + if (c1 == 999) { + tp = curtab; + } else { + tp = find_tabpage(c1); + } + if (tp == curtab) { + if (first_tabpage->tp_next != NULL) { + tabpage_close(false); + } + } else if (tp != NULL) { + tabpage_close_other(tp, false); + } +} static bool got_click = false; // got a click some time back @@ -189,7 +212,7 @@ static void call_click_def_func(StlClickDefinition *click_defs, int col, int whi } }; typval_T rettv; - (void)call_vim_function(click_defs[col].func, ARRAY_SIZE(argv), argv, &rettv); + call_vim_function(click_defs[col].func, ARRAY_SIZE(argv), argv, &rettv); tv_clear(&rettv); // Make sure next click does not register as drag when callback absorbs the release event. got_click = false; @@ -231,7 +254,7 @@ static int get_fpos_of_mouse(pos_T *mpos) return IN_STATUS_LINE; } - if (winrow == -1 && wp->w_winbar_height != 0) { + if (winrow < 0 && winrow + wp->w_winbar_height >= 0) { return MOUSE_WINBAR; } @@ -484,43 +507,32 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent) if (is_click && cmdwin_type == 0 && mouse_col < Columns) { in_tab_line = true; c1 = tab_page_click_defs[mouse_col].tabnr; + switch (tab_page_click_defs[mouse_col].type) { case kStlClickDisabled: break; - case kStlClickTabClose: { - tabpage_T *tp; - - // Close the current or specified tab page. - if (c1 == 999) { - tp = curtab; - } else { - tp = find_tabpage(c1); - } - if (tp == curtab) { - if (first_tabpage->tp_next != NULL) { - tabpage_close(false); - } - } else if (tp != NULL) { - tabpage_close_other(tp, false); - } - break; - } case kStlClickTabSwitch: - if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { - // double click opens new page - end_visual_mode(); - tabpage_new(); - tabpage_move(c1 == 0 ? 9999 : c1 - 1); - } else { - // Go to specified tab page, or next one if not clicking - // on a label. - goto_tabpage(c1); - - // It's like clicking on the status line of a window. - if (curwin != old_curwin) { + if (which_button != MOUSE_MIDDLE) { + if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { + // double click opens new page end_visual_mode(); + tabpage_new(); + tabpage_move(c1 == 0 ? 9999 : c1 - 1); + } else { + // Go to specified tab page, or next one if not clicking + // on a label. + goto_tabpage(c1); + + // It's like clicking on the status line of a window. + if (curwin != old_curwin) { + end_visual_mode(); + } } + break; } + FALLTHROUGH; + case kStlClickTabClose: + mouse_tab_close(c1); break; case kStlClickFuncRun: call_click_def_func(tab_page_click_defs, mouse_col, which_button); @@ -1012,7 +1024,7 @@ void do_mousescroll(cmdarg_T *cap) // Vertical scrolling if ((State & MODE_NORMAL) && shift_or_ctrl) { // whole page up or down - (void)onepage(cap->arg ? FORWARD : BACKWARD, 1); + onepage(cap->arg ? FORWARD : BACKWARD, 1); } else { if (shift_or_ctrl) { // whole page up or down @@ -1032,7 +1044,7 @@ void do_mousescroll(cmdarg_T *cap) if (leftcol < 0) { leftcol = 0; } - (void)do_mousescroll_horiz(leftcol); + do_mousescroll_horiz(leftcol); } } @@ -1251,7 +1263,7 @@ retnomove: bool below_window = grid == DEFAULT_GRID_HANDLE && row + wp->w_winbar_height >= wp->w_height; on_status_line = below_window && row + wp->w_winbar_height - wp->w_height + 1 == 1; on_sep_line = grid == DEFAULT_GRID_HANDLE && col >= wp->w_width && col - wp->w_width + 1 == 1; - on_winbar = row == -1 && wp->w_winbar_height != 0; + on_winbar = row < 0 && row + wp->w_winbar_height >= 0; on_statuscol = !below_window && !on_status_line && !on_sep_line && !on_winbar && *wp->w_p_stc != NUL && (wp->w_p_rl @@ -1323,18 +1335,18 @@ retnomove: && !sep_line_offset && (wp->w_p_rl ? col < wp->w_width_inner - fdc - : col >= fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1)) + : col >= fdc + (wp != cmdwin_win ? 0 : 1)) && (flags & MOUSE_MAY_STOP_VIS)))) { end_visual_mode(); redraw_curbuf_later(UPD_INVERTED); // delete the inversion } - if (cmdwin_type != 0 && wp != curwin) { + if (cmdwin_type != 0 && wp != cmdwin_win) { // A click outside the command-line window: Use modeless // selection if possible. Allow dragging the status lines. sep_line_offset = 0; row = 0; col += wp->w_wincol; - wp = curwin; + wp = cmdwin_win; } // Only change window focus when not clicking on or dragging the // status line. Do change focus when releasing the mouse button @@ -1418,7 +1430,7 @@ retnomove: break; } first = false; - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin->w_topline, &curwin->w_topline, NULL); if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { curwin->w_topfill++; } else { @@ -1568,9 +1580,6 @@ void nv_mousescroll(cmdarg_T *cap) // Call the common mouse scroll function shared with other modes. do_mousescroll(cap); - if (curwin != old_curwin && curwin->w_p_cul) { - redraw_for_cursorline(curwin); - } curwin->w_redr_status = true; curwin = old_curwin; curbuf = curwin->w_buffer; @@ -1579,7 +1588,7 @@ void nv_mousescroll(cmdarg_T *cap) /// Mouse clicks and drags. void nv_mouse(cmdarg_T *cap) { - (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0); + do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0); } /// Compute the position in the buffer line from the posn on the screen in @@ -1628,7 +1637,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) break; // Position is in this buffer line. } - (void)hasFoldingWin(win, lnum, NULL, &lnum, true, NULL); + hasFoldingWin(win, lnum, NULL, &lnum, true, NULL); if (lnum == win->w_buffer->b_ml.ml_line_count) { retval = true; @@ -1722,7 +1731,7 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp) } else if (*gridp > 1) { win_T *wp = get_win_by_grid_handle(*gridp); if (wp && wp->w_grid_alloc.chars - && !(wp->w_floating && !wp->w_float_config.focusable)) { + && !(wp->w_floating && !wp->w_config.focusable)) { *rowp = MIN(*rowp - wp->w_grid.row_offset, wp->w_grid.rows - 1); *colp = MIN(*colp - wp->w_grid.col_offset, wp->w_grid.cols - 1); return wp; @@ -1753,22 +1762,23 @@ colnr_T vcol2col(win_T *wp, linenr_T lnum, colnr_T vcol, colnr_T *coladdp) { // try to advance to the specified column char *line = ml_get_buf(wp->w_buffer, lnum); - chartabsize_T cts; - init_chartabsize_arg(&cts, wp, lnum, 0, line, line); - while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) { - int size = win_lbr_chartabsize(&cts, NULL); - if (cts.cts_vcol + size > vcol) { + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, wp, lnum, line); + StrCharInfo ci = utf_ptr2StrCharInfo(line); + int cur_vcol = 0; + while (cur_vcol < vcol && *ci.ptr != NUL) { + int next_vcol = cur_vcol + win_charsize(cstype, cur_vcol, ci.ptr, ci.chr.value, &csarg).width; + if (next_vcol > vcol) { break; } - cts.cts_vcol += size; - MB_PTR_ADV(cts.cts_ptr); + cur_vcol = next_vcol; + ci = utfc_next(ci); } - clear_chartabsize_arg(&cts); if (coladdp != NULL) { - *coladdp = vcol - cts.cts_vcol; + *coladdp = vcol - cur_vcol; } - return (colnr_T)(cts.cts_ptr - line); + return (colnr_T)(ci.ptr - line); } /// Set UI mouse depending on current mode and 'mouse'. @@ -1850,13 +1860,13 @@ static void mouse_check_grid(colnr_T *vcolp, int *flagsp) int click_col = mouse_col; // XXX: this doesn't change click_grid if it is 1, even with multigrid - win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col); - // Only use vcols[] after the window was redrawn. Mainly matters - // for tests, a user would not click before redrawing. - if (wp == NULL || wp->w_redr_type != 0) { + if (mouse_find_win(&click_grid, &click_row, &click_col) != curwin + // Only use vcols[] after the window was redrawn. Mainly matters + // for tests, a user would not click before redrawing. + || curwin->w_redr_type != 0) { return; } - ScreenGrid *gp = &wp->w_grid; + ScreenGrid *gp = &curwin->w_grid; int start_row = 0; int start_col = 0; grid_adjust(&gp, &start_row, &start_col); @@ -1892,12 +1902,12 @@ static void mouse_check_grid(colnr_T *vcolp, int *flagsp) if (eol_vcol < 0) { // Empty line or whole line before w_leftcol, // with columns before buffer text - eol_vcol = wp->w_leftcol - 1; + eol_vcol = curwin->w_leftcol - 1; } *vcolp = eol_vcol + (int)(off - off_r); } else { // Empty line or whole line before w_leftcol - *vcolp = click_col - start_col + wp->w_leftcol; + *vcolp = click_col - start_col + curwin->w_leftcol; } } else if (col_from_screen >= 0) { // Use the virtual column from vcols[], it is accurate also after @@ -1941,7 +1951,7 @@ void f_getmousepos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) winrow = row + 1 + wp->w_winrow_off; // Adjust by 1 for top border wincol = col + 1 + wp->w_wincol_off; // Adjust by 1 for left border if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) { - (void)mouse_comp_pos(wp, &row, &col, &lnum); + mouse_comp_pos(wp, &row, &col, &lnum); col = vcol2col(wp, lnum, col, &coladd); column = col + 1; } diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h index 928b3e360b..2dcb3469da 100644 --- a/src/nvim/mouse.h +++ b/src/nvim/mouse.h @@ -1,10 +1,9 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/normal_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep -#include "nvim/vim_defs.h" +#include "nvim/vim_defs.h" // IWYU pragma: keep /// jump_to_mouse() returns one of first five these values, possibly with /// some of the other five added. diff --git a/src/nvim/move.c b/src/nvim/move.c index 9ed3978490..551aa1bd4d 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -15,6 +15,7 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/drawscreen.h" @@ -22,14 +23,14 @@ #include "nvim/eval/typval.h" #include "nvim/eval/window.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/macros_defs.h" -#include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/message.h" @@ -41,7 +42,6 @@ #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/search.h" -#include "nvim/sign_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/vim_defs.h" @@ -135,29 +135,55 @@ static void comp_botline(win_T *wp) win_check_anchored_floats(wp); } -/// Redraw when w_virtcol changes and 'cursorcolumn' is set or 'cursorlineopt' -/// contains "screenline" or when the "CurSearch" highlight is in use. -/// Also when concealing is on and 'concealcursor' is active. +/// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set. +/// Also when concealing is on and 'concealcursor' is not active. +static void redraw_for_cursorline(win_T *wp) + FUNC_ATTR_NONNULL_ALL +{ + if ((wp->w_valid & VALID_CROW) == 0 && !pum_visible() + && (wp->w_p_rnu || win_cursorline_standout(wp))) { + // win_line() will redraw the number column and cursorline only. + redraw_later(wp, UPD_VALID); + } +} + +/// Redraw when w_virtcol changes and +/// - 'cursorcolumn' is set, or +/// - 'cursorlineopt' contains "screenline", or +/// - "CurSearch" highlight is in use, or +/// - 'concealcursor' is active, or +/// - Visual mode is active. static void redraw_for_cursorcolumn(win_T *wp) FUNC_ATTR_NONNULL_ALL { - if ((wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) { - if (wp->w_p_cuc - || (win_hl_attr(wp, HLF_LC) != win_hl_attr(wp, HLF_L) && using_hlsearch())) { - // When 'cursorcolumn' is set or "CurSearch" is in use - // need to redraw with UPD_SOME_VALID. - redraw_later(wp, UPD_SOME_VALID); - } else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE)) { - // When 'cursorlineopt' contains "screenline" need to redraw with UPD_VALID. - redraw_later(wp, UPD_VALID); - } + if (wp->w_valid & VALID_VIRTCOL) { + return; } + // If the cursor moves horizontally when 'concealcursor' is active, then the - // current line needs to be redrawn in order to calculate the correct - // cursor position. - if ((wp->w_valid & VALID_VIRTCOL) == 0 && wp->w_p_cole > 0 && conceal_cursor_line(wp)) { + // current line needs to be redrawn to calculate the correct cursor position. + if (wp->w_p_cole > 0 && conceal_cursor_line(wp)) { redrawWinline(wp, wp->w_cursor.lnum); } + + if (pum_visible()) { + return; + } + + if (wp->w_p_cuc + || (win_hl_attr(wp, HLF_LC) != win_hl_attr(wp, HLF_L) && using_hlsearch())) { + // When 'cursorcolumn' is set or "CurSearch" is in use + // need to redraw with UPD_SOME_VALID. + redraw_later(wp, UPD_SOME_VALID); + } else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE)) { + // When 'cursorlineopt' contains "screenline" need to redraw with UPD_VALID. + redraw_later(wp, UPD_VALID); + } + + // When current buffer's cursor moves in Visual mode, redraw it with UPD_INVERTED. + if (VIsual_active && wp->w_buffer == curbuf) { + redraw_curbuf_later(UPD_INVERTED); + } } /// Calculates how much the 'listchars' "precedes" or 'smoothscroll' "<<<" @@ -201,14 +227,16 @@ static int skipcol_from_plines(win_T *wp, int plines_off) /// Set wp->w_skipcol to zero and redraw later if needed. static void reset_skipcol(win_T *wp) { - if (wp->w_skipcol != 0) { - wp->w_skipcol = 0; - - // Should use the least expensive way that displays all that changed. - // UPD_NOT_VALID is too expensive, UPD_REDRAW_TOP does not redraw - // enough when the top line gets another screen line. - redraw_later(wp, UPD_SOME_VALID); + if (wp->w_skipcol == 0) { + return; } + + wp->w_skipcol = 0; + + // Should use the least expensive way that displays all that changed. + // UPD_NOT_VALID is too expensive, UPD_REDRAW_TOP does not redraw + // enough when the top line gets another screen line. + redraw_later(wp, UPD_SOME_VALID); } // Update curwin->w_topline to move the cursor onto the screen. @@ -304,7 +332,7 @@ void update_topline(win_T *wp) if (lnum >= wp->w_buffer->b_ml.ml_line_count || n >= halfheight) { break; } - (void)hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); + hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); } } else { n = wp->w_topline + *so_ptr - wp->w_cursor.lnum; @@ -321,7 +349,7 @@ void update_topline(win_T *wp) } } else { // Make sure topline is the first line of a fold. - (void)hasFoldingWin(wp, wp->w_topline, &wp->w_topline, NULL, true, NULL); + hasFoldingWin(wp, wp->w_topline, &wp->w_topline, NULL, true, NULL); check_botline = true; } } @@ -349,7 +377,7 @@ void update_topline(win_T *wp) int n = wp->w_empty_rows; loff.lnum = wp->w_cursor.lnum; // In a fold go to its last line. - (void)hasFoldingWin(wp, loff.lnum, NULL, &loff.lnum, true, NULL); + hasFoldingWin(wp, loff.lnum, NULL, &loff.lnum, true, NULL); loff.fill = 0; n += wp->w_filler_rows; loff.height = 0; @@ -383,7 +411,7 @@ void update_topline(win_T *wp) if (lnum <= 0 || line_count > wp->w_height_inner + 1) { break; } - (void)hasFolding(lnum, &lnum, NULL); + hasFolding(lnum, &lnum, NULL); } } else { line_count = wp->w_cursor.lnum - wp->w_botline + 1 + (int)(*so_ptr); @@ -527,7 +555,7 @@ void set_topline(win_T *wp, linenr_T lnum) linenr_T prev_topline = wp->w_topline; // go to first of folded lines - (void)hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); // Approximate the value of w_botline wp->w_botline += lnum - wp->w_topline; wp->w_topline = lnum; @@ -567,19 +595,6 @@ void changed_line_abv_curs_win(win_T *wp) |VALID_CHEIGHT|VALID_TOPLINE); } -/// Display of line has changed for "buf", invalidate cursor position and -/// w_botline. -void changed_line_display_buf(buf_T *buf) -{ - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf) { - wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL - |VALID_CROW|VALID_CHEIGHT - |VALID_TOPLINE|VALID_BOTLINE|VALID_BOTLINE_AP); - } - } -} - // Make sure the value of curwin->w_botline is valid. void validate_botline(win_T *wp) { @@ -760,8 +775,8 @@ int win_col_off(win_T *wp) { return ((wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc != NUL) ? (number_width(wp) + (*wp->w_p_stc == NUL)) : 0) - + ((cmdwin_type == 0 || wp != curwin) ? 0 : 1) - + win_fdccol_count(wp) + (win_signcol_count(wp) * SIGN_WIDTH); + + ((wp != cmdwin_win) ? 0 : 1) + + win_fdccol_count(wp) + (wp->w_scwidth * SIGN_WIDTH); } int curwin_col_off(void) @@ -1038,7 +1053,9 @@ void curs_columns(win_T *wp, int may_scroll) void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp, int *ecolp, bool local) { - colnr_T scol = 0, ccol = 0, ecol = 0; + colnr_T scol = 0; + colnr_T ccol = 0; + colnr_T ecol = 0; int row = 0; colnr_T coloff = 0; bool visible_row = false; @@ -1126,8 +1143,13 @@ void f_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) semsg(_(e_invalid_line_number_nr), pos.lnum); return; } + if (pos.col < 0) { + pos.col = 0; + } int row = 0; - int scol = 0, ccol = 0, ecol = 0; + int scol = 0; + int ccol = 0; + int ecol = 0; textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false); tv_dict_add_nr(dict, S_LEN("row"), row); @@ -1202,7 +1224,7 @@ bool scrolldown(linenr_T line_count, int byfold) } // Make sure w_topline is at the first of a sequence of folded lines. - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin->w_topline, &curwin->w_topline, NULL); validate_cursor(); // w_wrow needs to be valid for (int todo = line_count; todo > 0; todo--) { if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline) @@ -1327,11 +1349,11 @@ bool scrolldown(linenr_T line_count, int byfold) /// /// @param line_count number of lines to scroll /// @param byfold if true, count a closed fold as one line -bool scrollup(linenr_T line_count, int byfold) +bool scrollup(linenr_T line_count, bool byfold) { linenr_T topline = curwin->w_topline; linenr_T botline = curwin->w_botline; - int do_sms = curwin->w_p_wrap && curwin->w_p_sms; + bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; if (do_sms || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) { int width1 = curwin->w_width_inner - curwin_col_off(); @@ -1354,7 +1376,7 @@ bool scrollup(linenr_T line_count, int byfold) linenr_T lnum = curwin->w_topline; if (byfold) { // for a closed fold: go to the last line in the fold - (void)hasFolding(lnum, NULL, &lnum); + hasFolding(lnum, NULL, &lnum); } if (lnum == curwin->w_topline && do_sms) { // 'smoothscroll': increase "w_skipcol" until it goes over @@ -1410,7 +1432,7 @@ bool scrollup(linenr_T line_count, int byfold) if (hasAnyFolding(curwin)) { // Make sure w_topline is at the first of a sequence of folded lines. - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin->w_topline, &curwin->w_topline, NULL); } curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); @@ -1572,7 +1594,7 @@ static void max_topfill(void) // cursor off the screen. void scrolldown_clamp(void) { - int can_fill = (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)); + bool can_fill = (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)); if (curwin->w_topline <= 1 && !can_fill) { @@ -1604,7 +1626,7 @@ void scrolldown_clamp(void) curwin->w_topline--; curwin->w_topfill = 0; } - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin->w_topline, &curwin->w_topline, NULL); curwin->w_botline--; // approximate w_botline curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); } @@ -1635,7 +1657,7 @@ void scrollup_clamp(void) if (curwin->w_topfill > 0) { curwin->w_topfill--; } else { - (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline); + hasFolding(curwin->w_topline, NULL, &curwin->w_topline); curwin->w_topline++; } curwin->w_botline++; // approximate w_botline @@ -1864,7 +1886,7 @@ void set_empty_rows(win_T *wp, int used) /// When scrolling scroll at least "min_scroll" lines. /// If "set_topbot" is true, set topline and botline first (for "zb"). /// This is messy stuff!!! -void scroll_cursor_bot(int min_scroll, int set_topbot) +void scroll_cursor_bot(int min_scroll, bool set_topbot) { lineoff_T loff; linenr_T old_topline = curwin->w_topline; @@ -1874,7 +1896,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) int old_valid = curwin->w_valid; int old_empty_rows = curwin->w_empty_rows; linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number - int do_sms = curwin->w_p_wrap && curwin->w_p_sms; + bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; if (set_topbot) { bool set_skipcol = false; @@ -2093,7 +2115,7 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) linenr_T old_topline = curwin->w_topline; lineoff_T loff = { .lnum = curwin->w_cursor.lnum }; lineoff_T boff = { .lnum = curwin->w_cursor.lnum }; - (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum); + hasFolding(loff.lnum, &loff.lnum, &boff.lnum); int used = plines_win_nofill(curwin, loff.lnum, true); loff.fill = 0; boff.fill = 0; @@ -2439,7 +2461,7 @@ int onepage(Direction dir, int count) botline_forw(curwin, &loff); botline_topline(&loff); // We're at the wrong end of a fold now. - (void)hasFoldingWin(curwin, loff.lnum, &loff.lnum, NULL, true, NULL); + hasFoldingWin(curwin, loff.lnum, &loff.lnum, NULL, true, NULL); // Always scroll at least one line. Avoid getting stuck on // very long lines. @@ -2486,10 +2508,10 @@ int onepage(Direction dir, int count) if (curwin->w_topline <= old_topline && old_topline < curbuf->b_ml.ml_line_count) { curwin->w_topline = old_topline + 1; - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin->w_topline, &curwin->w_topline, NULL); } } else if (curwin->w_botline > curbuf->b_ml.ml_line_count) { - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin->w_topline, &curwin->w_topline, NULL); } } @@ -2588,7 +2610,7 @@ void halfpage(bool flag, linenr_T Prenum) if (n < 0 && scrolled > 0) { break; } - (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline); + hasFolding(curwin->w_topline, NULL, &curwin->w_topline); curwin->w_topline++; curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); @@ -2612,7 +2634,7 @@ void halfpage(bool flag, linenr_T Prenum) if (i > room) { break; } - (void)hasFolding(curwin->w_botline, NULL, &curwin->w_botline); + hasFolding(curwin->w_botline, NULL, &curwin->w_botline); curwin->w_botline++; room -= i; } while (curwin->w_botline <= curbuf->b_ml.ml_line_count); @@ -2624,8 +2646,8 @@ void halfpage(bool flag, linenr_T Prenum) if (hasAnyFolding(curwin)) { while (--n >= 0 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - (void)hasFolding(curwin->w_cursor.lnum, NULL, - &curwin->w_cursor.lnum); + hasFolding(curwin->w_cursor.lnum, NULL, + &curwin->w_cursor.lnum); curwin->w_cursor.lnum++; } } else { @@ -2647,7 +2669,7 @@ void halfpage(bool flag, linenr_T Prenum) break; } curwin->w_topline--; - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin->w_topline, &curwin->w_topline, NULL); curwin->w_topfill = 0; } curwin->w_valid &= ~(VALID_CROW|VALID_WROW| @@ -2666,8 +2688,8 @@ void halfpage(bool flag, linenr_T Prenum) } else if (hasAnyFolding(curwin)) { while (--n >= 0 && curwin->w_cursor.lnum > 1) { curwin->w_cursor.lnum--; - (void)hasFolding(curwin->w_cursor.lnum, - &curwin->w_cursor.lnum, NULL); + hasFolding(curwin->w_cursor.lnum, + &curwin->w_cursor.lnum, NULL); } } else { curwin->w_cursor.lnum -= n; @@ -2697,7 +2719,7 @@ void do_check_cursorbind(void) colnr_T col = curwin->w_cursor.col; colnr_T coladd = curwin->w_cursor.coladd; colnr_T curswant = curwin->w_curswant; - int set_curswant = curwin->w_set_curswant; + bool set_curswant = curwin->w_set_curswant; win_T *old_curwin = curwin; buf_T *old_curbuf = curbuf; int old_VIsual_select = VIsual_select; diff --git a/src/nvim/move.h b/src/nvim/move.h index ab8fb2b386..afeaeba235 100644 --- a/src/nvim/move.h +++ b/src/nvim/move.h @@ -1,11 +1,8 @@ #pragma once -#include <stdbool.h> - -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep -#include "nvim/vim_defs.h" +#include "nvim/vim_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "move.h.generated.h" diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 0fb1ebf931..a8fde5a652 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -1,7 +1,6 @@ #include <assert.h> #include <inttypes.h> #include <msgpack/object.h> -#include <msgpack/pack.h> #include <msgpack/sbuffer.h> #include <msgpack/unpack.h> #include <stdbool.h> @@ -14,13 +13,13 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/ui.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/event/defs.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/process.h" #include "nvim/event/rstream.h" -#include "nvim/event/stream.h" #include "nvim/event/wstream.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/main.h" @@ -29,10 +28,11 @@ #include "nvim/message.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/channel_defs.h" -#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/packer.h" #include "nvim/msgpack_rpc/unpacker.h" #include "nvim/os/input.h" #include "nvim/rbuffer.h" +#include "nvim/rbuffer_defs.h" #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_client.h" @@ -43,73 +43,31 @@ # define NOT "[notify] " # define ERR "[error] " -// Cannot define array with negative offsets, so this one is needed to be added -// to MSGPACK_UNPACK_\* values. -# define MUR_OFF 2 +# define SEND "->" +# define RECV "<-" -static const char *const msgpack_error_messages[] = { - [MSGPACK_UNPACK_EXTRA_BYTES + MUR_OFF] = "extra bytes found", - [MSGPACK_UNPACK_CONTINUE + MUR_OFF] = "incomplete string", - [MSGPACK_UNPACK_PARSE_ERROR + MUR_OFF] = "parse error", - [MSGPACK_UNPACK_NOMEM_ERROR + MUR_OFF] = "not enough memory", -}; - -static void log_close(FILE *f) +static void log_request(char *dir, uint64_t channel_id, uint32_t req_id, const char *name) { - fputc('\n', f); - fflush(f); - fclose(f); - log_unlock(); + DLOGN("RPC %s %" PRIu64 ": %s id=%u: %s\n", dir, channel_id, REQ, req_id, name); } -static void log_server_msg(uint64_t channel_id, msgpack_sbuffer *packed) +static void log_response(char *dir, uint64_t channel_id, char *kind, uint32_t req_id) { - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - DLOGN("RPC ->ch %" PRIu64 ": ", channel_id); - const msgpack_unpack_return result = - msgpack_unpack_next(&unpacked, packed->data, packed->size, NULL); - switch (result) { - case MSGPACK_UNPACK_SUCCESS: { - uint64_t type = unpacked.data.via.array.ptr[0].via.u64; - log_lock(); - FILE *f = open_log_file(); - fprintf(f, type ? (type == 1 ? RES : NOT) : REQ); - msgpack_object_print(f, unpacked.data); - log_close(f); - msgpack_unpacked_destroy(&unpacked); - break; - } - case MSGPACK_UNPACK_EXTRA_BYTES: - case MSGPACK_UNPACK_CONTINUE: - case MSGPACK_UNPACK_PARSE_ERROR: - case MSGPACK_UNPACK_NOMEM_ERROR: { - log_lock(); - FILE *f = open_log_file(); - fprintf(f, ERR); - fprintf(f, "%s", msgpack_error_messages[result + MUR_OFF]); - log_close(f); - break; - } - } + DLOGN("RPC %s %" PRIu64 ": %s id=%u\n", dir, channel_id, kind, req_id); } -static void log_client_msg(uint64_t channel_id, bool is_request, const char *name) +static void log_notify(char *dir, uint64_t channel_id, const char *name) { - DLOGN("RPC <-ch %" PRIu64 ": ", channel_id); - log_lock(); - FILE *f = open_log_file(); - fprintf(f, "%s: %s", is_request ? REQ : RES, name); - log_close(f); + DLOGN("RPC %s %" PRIu64 ": %s %s\n", dir, channel_id, NOT, name); } #else -# define log_client_msg(...) -# define log_server_msg(...) +# define log_request(...) +# define log_response(...) +# define log_notify(...) #endif static Set(cstr_t) event_strings = SET_INIT; -static msgpack_sbuffer out_buffer; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/channel.c.generated.h" @@ -118,7 +76,6 @@ static msgpack_sbuffer out_buffer; void rpc_init(void) { ch_before_blocking_events = multiqueue_new_child(main_loop.events); - msgpack_sbuffer_init(&out_buffer); } void rpc_start(Channel *channel) @@ -168,8 +125,9 @@ bool rpc_send_event(uint64_t id, const char *name, Array args) return false; } + log_notify(SEND, channel ? channel->id : 0, name); if (channel) { - send_event(channel, name, args); + serialize_request(&channel, 1, 0, name, args); } else { broadcast_event(name, args); } @@ -191,7 +149,6 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem if (!(channel = find_rpc_channel(id))) { api_set_error(err, kErrorTypeException, "Invalid channel: %" PRIu64, id); - api_free_array(args); return NIL; } @@ -199,8 +156,9 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem RpcState *rpc = &channel->rpc; uint32_t request_id = rpc->next_request_id++; // Send the msgpack-rpc request - send_request(channel, request_id, method_name, args); - api_free_array(args); + serialize_request(&channel, 1, request_id, method_name, args); + + log_request(SEND, channel->id, request_id, method_name); // Push the frame ChannelCallFrame frame = { request_id, false, false, NIL, NULL }; @@ -303,8 +261,11 @@ static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data, p->read_ptr = rbuffer_read_ptr(rbuf, &size); p->read_size = size; parse_msgpack(channel); - size_t consumed = size - p->read_size; - rbuffer_consumed_compact(rbuf, consumed); + + if (!unpacker_closed(p)) { + size_t consumed = size - p->read_size; + rbuffer_consumed_compact(rbuf, consumed); + } end: channel_decref(channel); @@ -359,8 +320,13 @@ static void parse_msgpack(Channel *channel) frame->result = p->result; } frame->result_mem = arena_finish(&p->arena); + log_response(RECV, channel->id, frame->errored ? ERR : RES, p->request_id); } else { - log_client_msg(channel->id, p->type == kMessageTypeRequest, p->handler.name); + if (p->type == kMessageTypeNotification) { + log_notify(RECV, channel->id, p->handler.name); + } else { + log_request(RECV, channel->id, p->request_id, p->handler.name); + } Object res = p->result; if (p->result.type != kObjectTypeArray) { @@ -405,7 +371,7 @@ static void handle_request(Channel *channel, Unpacker *p, Array args) if (is_get_mode && !input_blocking()) { // Defer the event to a special queue used by os/input.c. #6247 - multiqueue_put(ch_before_blocking_events, request_event, 1, evdata); + multiqueue_put(ch_before_blocking_events, request_event, evdata); } else { // Invoke immediately. request_event((void **)&evdata); @@ -413,12 +379,11 @@ static void handle_request(Channel *channel, Unpacker *p, Array args) } else { bool is_resize = p->handler.fn == handle_nvim_ui_try_resize; if (is_resize) { - Event ev = event_create_oneshot(event_create(request_event, 1, evdata), - 2); + Event ev = event_create_oneshot(event_create(request_event, evdata), 2); multiqueue_put_event(channel->events, ev); multiqueue_put_event(resize_events, ev); } else { - multiqueue_put(channel->events, request_event, 1, evdata); + multiqueue_put(channel->events, request_event, evdata); DLOG("RPC: scheduled %.*s", (int)p->method_name_len, p->handler.name); } } @@ -441,17 +406,9 @@ static void request_event(void **argv) Object result = handler.fn(channel->id, e->args, &e->used_mem, &error); if (e->type == kMessageTypeRequest || ERROR_SET(&error)) { // Send the response. - msgpack_packer response; - msgpack_packer_init(&response, &out_buffer, msgpack_sbuffer_write); - channel_write(channel, serialize_response(channel->id, - e->handler, - e->type, - e->request_id, - &error, - result, - &out_buffer)); - } - if (!handler.arena_return) { + serialize_response(channel, e->handler, e->type, e->request_id, &error, &result); + } + if (handler.ret_alloc) { api_free_object(result); } @@ -485,7 +442,7 @@ static bool channel_write(Channel *channel, WBuffer *buffer) if (channel->streamtype == kChannelStreamInternal) { channel_incref(channel); - CREATE_EVENT(channel->events, internal_read_event, 2, channel, buffer); + CREATE_EVENT(channel->events, internal_read_event, channel, buffer); success = true; } else { Stream *in = channel_instream(channel); @@ -532,41 +489,14 @@ static void send_error(Channel *chan, MsgpackRpcRequestHandler handler, MessageT { Error e = ERROR_INIT; api_set_error(&e, kErrorTypeException, "%s", err); - channel_write(chan, serialize_response(chan->id, - handler, - type, - id, - &e, - NIL, - &out_buffer)); + serialize_response(chan, handler, type, id, &e, &NIL); api_clear_error(&e); } -static void send_request(Channel *channel, uint32_t id, const char *name, Array args) -{ - const String method = cstr_as_string((char *)name); - channel_write(channel, serialize_request(channel->id, - id, - method, - args, - &out_buffer, - 1)); -} - -static void send_event(Channel *channel, const char *name, Array args) -{ - const String method = cstr_as_string((char *)name); - channel_write(channel, serialize_request(channel->id, - 0, - method, - args, - &out_buffer, - 1)); -} - static void broadcast_event(const char *name, Array args) { - kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; + kvec_withinit_t(Channel *, 4) subscribed = KV_INITIAL_VALUE; + kvi_init(subscribed); Channel *channel; map_foreach_value(&channels, channel, { @@ -576,25 +506,11 @@ static void broadcast_event(const char *name, Array args) } }); - if (!kv_size(subscribed)) { - goto end; - } - - const String method = cstr_as_string((char *)name); - WBuffer *buffer = serialize_request(0, - 0, - method, - args, - &out_buffer, - kv_size(subscribed)); - - for (size_t i = 0; i < kv_size(subscribed); i++) { - Channel *c = kv_A(subscribed, i); - channel_write(c, buffer); + if (kv_size(subscribed)) { + serialize_request(subscribed.items, kv_size(subscribed), 0, name, args); } -end: - kv_destroy(subscribed); + kvi_destroy(subscribed); } static void unsubscribe(Channel *channel, char *event) @@ -652,27 +568,28 @@ static void chan_close_with_error(Channel *channel, char *msg, int loglevel) channel_close(channel->id, kChannelPartRpc, NULL); } -static WBuffer *serialize_request(uint64_t channel_id, uint32_t request_id, const String method, - Array args, msgpack_sbuffer *sbuffer, size_t refcount) +static void serialize_request(Channel **chans, size_t nchans, uint32_t request_id, + const char *method, Array args) { - msgpack_packer pac; - msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); - msgpack_rpc_serialize_request(request_id, method, args, &pac); - log_server_msg(channel_id, sbuffer); - WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), - sbuffer->size, - refcount, - xfree); - msgpack_sbuffer_clear(sbuffer); - return rv; + PackerBuffer packer; + packer_buffer_init_channels(chans, nchans, &packer); + + mpack_array(&packer.ptr, request_id ? 4 : 3); + mpack_w(&packer.ptr, request_id ? 0 : 2); + + if (request_id) { + mpack_uint(&packer.ptr, request_id); + } + + mpack_str(cstr_as_string(method), &packer); + mpack_object_array(args, &packer); + + packer_buffer_finish_channels(&packer); } -static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler handler, - MessageType type, uint32_t response_id, Error *err, Object arg, - msgpack_sbuffer *sbuffer) +void serialize_response(Channel *channel, MsgpackRpcRequestHandler handler, MessageType type, + uint32_t response_id, Error *err, Object *arg) { - msgpack_packer pac; - msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); if (ERROR_SET(err) && type == kMessageTypeNotification) { if (handler.fn == handle_nvim_paste) { // TODO(bfredl): this is pretty much ad-hoc. maybe TUI and UI:s should be @@ -681,23 +598,68 @@ static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler semsg("paste: %s", err->msg); api_clear_error(err); } else { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(err->type)); - ADD(args, CSTR_TO_OBJ(err->msg)); - msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"), - args, &pac); - api_free_array(args); + MAXSIZE_TEMP_ARRAY(args, 2); + ADD_C(args, INTEGER_OBJ(err->type)); + ADD_C(args, CSTR_AS_OBJ(err->msg)); + serialize_request(&channel, 1, 0, "nvim_error_event", args); } + return; + } + + PackerBuffer packer; + packer_buffer_init_channels(&channel, 1, &packer); + + mpack_array(&packer.ptr, 4); + mpack_w(&packer.ptr, 1); + mpack_uint(&packer.ptr, response_id); + + if (ERROR_SET(err)) { + // error represented by a [type, message] array + mpack_array(&packer.ptr, 2); + mpack_integer(&packer.ptr, err->type); + mpack_str(cstr_as_string(err->msg), &packer); + // Nil result + mpack_nil(&packer.ptr); } else { - msgpack_rpc_serialize_response(response_id, err, arg, &pac); + // Nil error + mpack_nil(&packer.ptr); + // Return value + mpack_object(arg, &packer); } - log_server_msg(channel_id, sbuffer); - WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), - sbuffer->size, - 1, // responses only go though 1 channel - xfree); - msgpack_sbuffer_clear(sbuffer); - return rv; + + packer_buffer_finish_channels(&packer); + + log_response(SEND, channel->id, ERROR_SET(err) ? ERR : RES, response_id); +} + +static void packer_buffer_init_channels(Channel **chans, size_t nchans, PackerBuffer *packer) +{ + packer->startptr = alloc_block(); + packer->ptr = packer->startptr; + packer->endptr = packer->startptr + ARENA_BLOCK_SIZE; + packer->packer_flush = channel_flush_callback; + packer->anydata = chans; + packer->anylen = nchans; +} + +static void packer_buffer_finish_channels(PackerBuffer *packer) +{ + size_t len = (size_t)(packer->ptr - packer->startptr); + if (len > 0) { + WBuffer *buf = wstream_new_buffer(packer->startptr, len, packer->anylen, free_block); + Channel **chans = packer->anydata; + for (size_t i = 0; i < packer->anylen; i++) { + channel_write(chans[i], buf); + } + } else { + free_block(packer->startptr); + } +} + +static void channel_flush_callback(PackerBuffer *packer) +{ + packer_buffer_finish_channels(packer); + packer_buffer_init_channels(packer->anydata, packer->anylen, packer); } void rpc_set_client_info(uint64_t id, Dictionary info) @@ -753,6 +715,7 @@ const char *get_client_info(Channel *chan, const char *key) return NULL; } +#ifdef EXITFREE void rpc_free_all_mem(void) { cstr_t key; @@ -760,4 +723,7 @@ void rpc_free_all_mem(void) xfree((void *)key); }); set_destroy(cstr_t, &event_strings); + + multiqueue_free(ch_before_blocking_events); } +#endif diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h index 818bee8318..ff73a2fc53 100644 --- a/src/nvim/msgpack_rpc/channel.h +++ b/src/nvim/msgpack_rpc/channel.h @@ -3,14 +3,10 @@ #include <stdint.h> // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep -#include "nvim/channel.h" -#include "nvim/event/multiqueue.h" -#include "nvim/event/process.h" -#include "nvim/event/socket.h" -#include "nvim/event/wstream.h" +#include "nvim/event/defs.h" #include "nvim/macros_defs.h" #include "nvim/memory_defs.h" // IWYU pragma: keep -#include "nvim/msgpack_rpc/channel_defs.h" // IWYU pragma: export +#include "nvim/msgpack_rpc/channel_defs.h" // IWYU pragma: keep #define METHOD_MAXLEN 512 diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h index 20b8a89afb..56dbb332e0 100644 --- a/src/nvim/msgpack_rpc/channel_defs.h +++ b/src/nvim/msgpack_rpc/channel_defs.h @@ -4,11 +4,7 @@ #include <stdbool.h> #include <uv.h> -#include "klib/kvec.h" -#include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" -#include "nvim/event/process.h" -#include "nvim/event/socket.h" #include "nvim/map_defs.h" typedef struct Channel Channel; diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c deleted file mode 100644 index 1fdfc9e536..0000000000 --- a/src/nvim/msgpack_rpc/helpers.c +++ /dev/null @@ -1,547 +0,0 @@ -#include <msgpack/object.h> -#include <msgpack/sbuffer.h> -#include <msgpack/unpack.h> -#include <msgpack/zone.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -#include "klib/kvec.h" -#include "msgpack/pack.h" -#include "nvim/api/private/helpers.h" -#include "nvim/assert_defs.h" -#include "nvim/func_attr.h" -#include "nvim/memory.h" -#include "nvim/msgpack_rpc/helpers.h" -#include "nvim/types_defs.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "msgpack_rpc/helpers.c.generated.h" -#endif - -static msgpack_zone zone; -static msgpack_sbuffer sbuffer; - -void msgpack_rpc_helpers_init(void) -{ - msgpack_zone_init(&zone, 0xfff); - msgpack_sbuffer_init(&sbuffer); -} - -typedef struct { - const msgpack_object *mobj; - Object *aobj; - bool container; - size_t idx; -} MPToAPIObjectStackItem; - -// uncrustify:off - -/// Convert type used by msgpack parser to Nvim API type. -/// -/// @param[in] obj Msgpack value to convert. -/// @param[out] arg Location where result of conversion will be saved. -/// -/// @return true in case of success, false otherwise. -bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) - FUNC_ATTR_NONNULL_ALL -{ - bool ret = true; - kvec_withinit_t(MPToAPIObjectStackItem, 2) stack = KV_INITIAL_VALUE; - kvi_init(stack); - kvi_push(stack, ((MPToAPIObjectStackItem) { - .mobj = obj, - .aobj = arg, - .container = false, - .idx = 0, - })); - while (ret && kv_size(stack)) { - MPToAPIObjectStackItem cur = kv_last(stack); - if (!cur.container) { - *cur.aobj = NIL; - } - switch (cur.mobj->type) { - case MSGPACK_OBJECT_NIL: - break; - case MSGPACK_OBJECT_BOOLEAN: - *cur.aobj = BOOLEAN_OBJ(cur.mobj->via.boolean); - break; - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - STATIC_ASSERT(sizeof(Integer) == sizeof(cur.mobj->via.i64), - "Msgpack integer size does not match API integer"); - *cur.aobj = INTEGER_OBJ(cur.mobj->via.i64); - break; - case MSGPACK_OBJECT_POSITIVE_INTEGER: - STATIC_ASSERT(sizeof(Integer) == sizeof(cur.mobj->via.u64), - "Msgpack integer size does not match API integer"); - if (cur.mobj->via.u64 > API_INTEGER_MAX) { - ret = false; - } else { - *cur.aobj = INTEGER_OBJ((Integer)cur.mobj->via.u64); - } - break; - case MSGPACK_OBJECT_FLOAT32: - case MSGPACK_OBJECT_FLOAT64: - { - STATIC_ASSERT(sizeof(Float) == sizeof(cur.mobj->via.f64), - "Msgpack floating-point size does not match API integer"); - *cur.aobj = FLOAT_OBJ(cur.mobj->via.f64); - break; - } -#define STR_CASE(type, attr, obj, dest, conv) \ - case type: { \ - dest = conv(((String) { \ - .size = obj->via.attr.size, \ - .data = (obj->via.attr.ptr == NULL || obj->via.attr.size == 0 \ - ? xmemdupz("", 0) \ - : xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), })); \ - break; \ - } - STR_CASE(MSGPACK_OBJECT_STR, str, cur.mobj, *cur.aobj, STRING_OBJ) - STR_CASE(MSGPACK_OBJECT_BIN, bin, cur.mobj, *cur.aobj, STRING_OBJ) - case MSGPACK_OBJECT_ARRAY: { - const size_t size = cur.mobj->via.array.size; - if (cur.container) { - if (cur.idx >= size) { - (void)kv_pop(stack); - } else { - const size_t idx = cur.idx; - cur.idx++; - kv_last(stack) = cur; - kvi_push(stack, ((MPToAPIObjectStackItem) { - .mobj = &cur.mobj->via.array.ptr[idx], - .aobj = &cur.aobj->data.array.items[idx], - .container = false, - })); - } - } else { - *cur.aobj = ARRAY_OBJ(((Array) { - .size = size, - .capacity = size, - .items = (size > 0 - ? xcalloc(size, sizeof(*cur.aobj->data.array.items)) - : NULL), - })); - cur.container = true; - kv_last(stack) = cur; - } - break; - } - case MSGPACK_OBJECT_MAP: { - const size_t size = cur.mobj->via.map.size; - if (cur.container) { - if (cur.idx >= size) { - (void)kv_pop(stack); - } else { - const size_t idx = cur.idx; - cur.idx++; - kv_last(stack) = cur; - const msgpack_object *const key = &cur.mobj->via.map.ptr[idx].key; - switch (key->type) { -#define ID(x) x - STR_CASE(MSGPACK_OBJECT_STR, str, key, - cur.aobj->data.dictionary.items[idx].key, ID) - STR_CASE(MSGPACK_OBJECT_BIN, bin, key, - cur.aobj->data.dictionary.items[idx].key, ID) -#undef ID - case MSGPACK_OBJECT_NIL: - case MSGPACK_OBJECT_BOOLEAN: - case MSGPACK_OBJECT_POSITIVE_INTEGER: - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - case MSGPACK_OBJECT_FLOAT32: - case MSGPACK_OBJECT_FLOAT64: - case MSGPACK_OBJECT_EXT: - case MSGPACK_OBJECT_MAP: - case MSGPACK_OBJECT_ARRAY: - ret = false; - break; - } - if (ret) { - kvi_push(stack, ((MPToAPIObjectStackItem) { - .mobj = &cur.mobj->via.map.ptr[idx].val, - .aobj = &cur.aobj->data.dictionary.items[idx].value, - .container = false, - })); - } - } - } else { - *cur.aobj = DICTIONARY_OBJ(((Dictionary) { - .size = size, - .capacity = size, - .items = (size > 0 - ? xcalloc(size, sizeof(*cur.aobj->data.dictionary.items)) - : NULL), - })); - cur.container = true; - kv_last(stack) = cur; - } - break; - } - case MSGPACK_OBJECT_EXT: - if (0 <= cur.mobj->via.ext.type && cur.mobj->via.ext.type <= EXT_OBJECT_TYPE_MAX) { - cur.aobj->type = (ObjectType)(cur.mobj->via.ext.type + EXT_OBJECT_TYPE_SHIFT); - msgpack_object data; - msgpack_unpack_return status = msgpack_unpack(cur.mobj->via.ext.ptr, cur.mobj->via.ext.size, - NULL, &zone, &data); - - if (status != MSGPACK_UNPACK_SUCCESS || data.type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - ret = false; - break; - } - cur.aobj->data.integer = (handle_T)data.via.i64; - ret = true; - } - break; -#undef STR_CASE - } - if (!cur.container) { - (void)kv_pop(stack); - } - } - kvi_destroy(stack); - return ret; -} - -static bool msgpack_rpc_to_string(const msgpack_object *const obj, String *const arg) - FUNC_ATTR_NONNULL_ALL -{ - if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) { - arg->data = obj->via.bin.ptr != NULL - ? xmemdupz(obj->via.bin.ptr, obj->via.bin.size) - : NULL; - arg->size = obj->via.bin.size; - return true; - } - return false; -} - -bool msgpack_rpc_to_array(const msgpack_object *const obj, Array *const arg) - FUNC_ATTR_NONNULL_ALL -{ - if (obj->type != MSGPACK_OBJECT_ARRAY) { - return false; - } - - arg->size = obj->via.array.size; - arg->items = xcalloc(obj->via.array.size, sizeof(Object)); - - for (uint32_t i = 0; i < obj->via.array.size; i++) { - if (!msgpack_rpc_to_object(obj->via.array.ptr + i, &arg->items[i])) { - return false; - } - } - - return true; -} - -bool msgpack_rpc_to_dictionary(const msgpack_object *const obj, Dictionary *const arg) - FUNC_ATTR_NONNULL_ALL -{ - if (obj->type != MSGPACK_OBJECT_MAP) { - return false; - } - - arg->size = obj->via.array.size; - arg->items = xcalloc(obj->via.map.size, sizeof(KeyValuePair)); - - for (uint32_t i = 0; i < obj->via.map.size; i++) { - if (!msgpack_rpc_to_string(&obj->via.map.ptr[i].key, - &arg->items[i].key)) { - return false; - } - - if (!msgpack_rpc_to_object(&obj->via.map.ptr[i].val, - &arg->items[i].value)) { - return false; - } - } - - return true; -} - -void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - if (result) { - msgpack_pack_true(res); - } else { - msgpack_pack_false(res); - } -} - -void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_int64(res, result); -} - -void msgpack_rpc_from_float(Float result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_double(res, result); -} - -void msgpack_rpc_from_string(const String result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_str(res, result.size); - if (result.size > 0) { - msgpack_pack_str_body(res, result.data, result.size); - } -} - -static void msgpack_rpc_from_handle(ObjectType type, Integer o, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(3) -{ - msgpack_packer pac; - msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); - msgpack_pack_int64(&pac, (handle_T)o); - msgpack_pack_ext(res, sbuffer.size, (int8_t)(type - EXT_OBJECT_TYPE_SHIFT)); - msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); - msgpack_sbuffer_clear(&sbuffer); -} - -typedef struct { - const Object *aobj; - bool container; - size_t idx; -} APIToMPObjectStackItem; - -/// Convert type used by Nvim API to msgpack type. -/// -/// @param[in] result Object to convert. -/// @param[out] res Structure that defines where conversion results are saved. -/// -/// @return true in case of success, false otherwise. -void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) - FUNC_ATTR_NONNULL_ARG(2) -{ - kvec_withinit_t(APIToMPObjectStackItem, 2) stack = KV_INITIAL_VALUE; - kvi_init(stack); - kvi_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 })); - while (kv_size(stack)) { - APIToMPObjectStackItem cur = kv_last(stack); - STATIC_ASSERT(kObjectTypeWindow == kObjectTypeBuffer + 1 - && kObjectTypeTabpage == kObjectTypeWindow + 1, - "Buffer, window and tabpage enum items are in order"); - switch (cur.aobj->type) { - case kObjectTypeNil: - case kObjectTypeLuaRef: - // TODO(bfredl): could also be an error. Though kObjectTypeLuaRef - // should only appear when the caller has opted in to handle references, - // see nlua_pop_Object. - msgpack_pack_nil(res); - break; - case kObjectTypeBoolean: - msgpack_rpc_from_boolean(cur.aobj->data.boolean, res); - break; - case kObjectTypeInteger: - msgpack_rpc_from_integer(cur.aobj->data.integer, res); - break; - case kObjectTypeFloat: - msgpack_rpc_from_float(cur.aobj->data.floating, res); - break; - case kObjectTypeString: - msgpack_rpc_from_string(cur.aobj->data.string, res); - break; - case kObjectTypeBuffer: - case kObjectTypeWindow: - case kObjectTypeTabpage: - msgpack_rpc_from_handle(cur.aobj->type, cur.aobj->data.integer, res); - break; - case kObjectTypeArray: { - const size_t size = cur.aobj->data.array.size; - if (cur.container) { - if (cur.idx >= size) { - (void)kv_pop(stack); - } else { - const size_t idx = cur.idx; - cur.idx++; - kv_last(stack) = cur; - kvi_push(stack, ((APIToMPObjectStackItem) { - .aobj = &cur.aobj->data.array.items[idx], - .container = false, - })); - } - } else { - msgpack_pack_array(res, size); - cur.container = true; - kv_last(stack) = cur; - } - break; - } - case kObjectTypeDictionary: { - const size_t size = cur.aobj->data.dictionary.size; - if (cur.container) { - if (cur.idx >= size) { - (void)kv_pop(stack); - } else { - const size_t idx = cur.idx; - cur.idx++; - kv_last(stack) = cur; - msgpack_rpc_from_string(cur.aobj->data.dictionary.items[idx].key, res); - kvi_push(stack, ((APIToMPObjectStackItem) { - .aobj = &cur.aobj->data.dictionary.items[idx].value, - .container = false, - })); - } - } else { - msgpack_pack_map(res, size); - cur.container = true; - kv_last(stack) = cur; - } - break; - } - } - if (!cur.container) { - (void)kv_pop(stack); - } - } - kvi_destroy(stack); -} - -// uncrustify:on - -void msgpack_rpc_from_array(Array result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_array(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_object(result.items[i], res); - } -} - -void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_map(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_string(result.items[i].key, res); - msgpack_rpc_from_object(result.items[i].value, res); - } -} - -/// Serializes a msgpack-rpc request or notification(id == 0) -void msgpack_rpc_serialize_request(uint32_t request_id, const String method, Array args, - msgpack_packer *pac) - FUNC_ATTR_NONNULL_ARG(4) -{ - msgpack_pack_array(pac, request_id ? 4 : 3); - msgpack_pack_int(pac, request_id ? 0 : 2); - - if (request_id) { - msgpack_pack_uint32(pac, request_id); - } - - msgpack_rpc_from_string(method, pac); - msgpack_rpc_from_array(args, pac); -} - -/// Serializes a msgpack-rpc response -void msgpack_rpc_serialize_response(uint32_t response_id, Error *err, Object arg, - msgpack_packer *pac) - FUNC_ATTR_NONNULL_ARG(2, 4) -{ - msgpack_pack_array(pac, 4); - msgpack_pack_int(pac, 1); - msgpack_pack_uint32(pac, response_id); - - if (ERROR_SET(err)) { - // error represented by a [type, message] array - msgpack_pack_array(pac, 2); - msgpack_rpc_from_integer(err->type, pac); - msgpack_rpc_from_string(cstr_as_string(err->msg), pac); - // Nil result - msgpack_pack_nil(pac); - } else { - // Nil error - msgpack_pack_nil(pac); - // Return value - msgpack_rpc_from_object(arg, pac); - } -} - -static bool msgpack_rpc_is_notification(msgpack_object *req) -{ - return req->via.array.ptr[0].via.u64 == 2; -} - -msgpack_object *msgpack_rpc_method(msgpack_object *req) -{ - msgpack_object *obj = req->via.array.ptr - + (msgpack_rpc_is_notification(req) ? 1 : 2); - return obj->type == MSGPACK_OBJECT_STR || obj->type == MSGPACK_OBJECT_BIN - ? obj : NULL; -} - -msgpack_object *msgpack_rpc_args(msgpack_object *req) -{ - msgpack_object *obj = req->via.array.ptr - + (msgpack_rpc_is_notification(req) ? 2 : 3); - return obj->type == MSGPACK_OBJECT_ARRAY ? obj : NULL; -} - -static msgpack_object *msgpack_rpc_msg_id(msgpack_object *req) -{ - if (msgpack_rpc_is_notification(req)) { - return NULL; - } - msgpack_object *obj = &req->via.array.ptr[1]; - return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER ? obj : NULL; -} - -MessageType msgpack_rpc_validate(uint32_t *response_id, msgpack_object *req, Error *err) -{ - *response_id = 0; - // Validate the basic structure of the msgpack-rpc payload - if (req->type != MSGPACK_OBJECT_ARRAY) { - api_set_error(err, kErrorTypeValidation, "Message is not an array"); - return kMessageTypeUnknown; - } - - if (req->via.array.size == 0) { - api_set_error(err, kErrorTypeValidation, "Message is empty"); - return kMessageTypeUnknown; - } - - if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - api_set_error(err, kErrorTypeValidation, "Message type must be an integer"); - return kMessageTypeUnknown; - } - - MessageType type = (MessageType)req->via.array.ptr[0].via.u64; - if (type != kMessageTypeRequest && type != kMessageTypeNotification) { - api_set_error(err, kErrorTypeValidation, "Unknown message type"); - return kMessageTypeUnknown; - } - - if ((type == kMessageTypeRequest && req->via.array.size != 4) - || (type == kMessageTypeNotification && req->via.array.size != 3)) { - api_set_error(err, kErrorTypeValidation, - "Request array size must be 4 (request) or 3 (notification)"); - return type; - } - - if (type == kMessageTypeRequest) { - msgpack_object *id_obj = msgpack_rpc_msg_id(req); - if (!id_obj) { - api_set_error(err, kErrorTypeValidation, "ID must be a positive integer"); - return type; - } - *response_id = (uint32_t)id_obj->via.u64; - } - - if (!msgpack_rpc_method(req)) { - api_set_error(err, kErrorTypeValidation, "Method must be a string"); - return type; - } - - if (!msgpack_rpc_args(req)) { - api_set_error(err, kErrorTypeValidation, "Parameters must be an array"); - return type; - } - - return type; -} diff --git a/src/nvim/msgpack_rpc/helpers.h b/src/nvim/msgpack_rpc/helpers.h deleted file mode 100644 index dd2096f305..0000000000 --- a/src/nvim/msgpack_rpc/helpers.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include <msgpack.h> -#include <stdbool.h> -#include <stdint.h> - -#include "nvim/api/private/defs.h" -#include "nvim/event/wstream.h" - -/// Value by which objects represented as EXT type are shifted -/// -/// Subtracted when packing, added when unpacking. Used to allow moving -/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be -/// split or reordered. -#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer -#define EXT_OBJECT_TYPE_MAX (kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT) - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "msgpack_rpc/helpers.h.generated.h" -#endif diff --git a/src/nvim/msgpack_rpc/packer.c b/src/nvim/msgpack_rpc/packer.c new file mode 100644 index 0000000000..cac68f76f0 --- /dev/null +++ b/src/nvim/msgpack_rpc/packer.c @@ -0,0 +1,235 @@ +#include <assert.h> + +#include "nvim/api/private/defs.h" +#include "nvim/lua/executor.h" +#include "nvim/msgpack_rpc/packer.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "msgpack_rpc/packer.c.generated.h" +#endif + +static void check_buffer(PackerBuffer *packer) +{ + ptrdiff_t remaining = packer->endptr - packer->ptr; + if (remaining < MPACK_ITEM_SIZE) { + packer->packer_flush(packer); + } +} + +static void mpack_w8(char **b, const char *data) +{ +#ifdef ORDER_BIG_ENDIAN + memcpy(*b, data, 8); + *b += 8; +#else + for (int i = 7; i >= 0; i--) { + *(*b)++ = data[i]; + } +#endif +} + +void mpack_integer(char **ptr, Integer i) +{ + if (i >= 0) { + if (i > 0xfffffff) { + mpack_w(ptr, 0xcf); + mpack_w8(ptr, (char *)&i); + } else { + mpack_uint(ptr, (uint32_t)i); + } + } else { + if (i < -0x80000000LL) { + mpack_w(ptr, 0xd3); + mpack_w8(ptr, (char *)&i); + } else if (i < -0x8000) { + mpack_w(ptr, 0xd2); + mpack_w4(ptr, (uint32_t)i); + } else if (i < -0x80) { + mpack_w(ptr, 0xd1); + mpack_w2(ptr, (uint32_t)i); + } else if (i < -0x20) { + mpack_w(ptr, 0xd0); + mpack_w(ptr, (char)i); + } else { + mpack_w(ptr, (char)i); + } + } +} + +void mpack_float8(char **ptr, double i) +{ + mpack_w(ptr, 0xcb); + mpack_w8(ptr, (char *)&i); +} + +void mpack_str(String str, PackerBuffer *packer) +{ + const size_t len = str.size; + if (len < 20) { + mpack_w(&packer->ptr, 0xa0 | len); + } else if (len < 0xff) { + mpack_w(&packer->ptr, 0xd9); + mpack_w(&packer->ptr, len); + } else if (len < 0xffff) { + mpack_w(&packer->ptr, 0xda); + mpack_w2(&packer->ptr, (uint32_t)len); + } else if (len < 0xffffffff) { + mpack_w(&packer->ptr, 0xdb); + mpack_w4(&packer->ptr, (uint32_t)len); + } else { + abort(); + } + + size_t pos = 0; + while (pos < len) { + ptrdiff_t remaining = packer->endptr - packer->ptr; + size_t to_copy = MIN(len - pos, (size_t)remaining); + memcpy(packer->ptr, str.data + pos, to_copy); + packer->ptr += to_copy; + pos += to_copy; + + if (pos < len) { + packer->packer_flush(packer); + } + } +} + +void mpack_handle(ObjectType type, handle_T handle, PackerBuffer *packer) +{ + char exttype = (char)(type - EXT_OBJECT_TYPE_SHIFT); + if (-0x1f <= handle && handle <= 0x7f) { + mpack_w(&packer->ptr, 0xd4); + mpack_w(&packer->ptr, exttype); + mpack_w(&packer->ptr, (char)handle); + } else { + // we want to encode some small negative sentinel like -1. This is handled above + assert(handle >= 0); + // FAIL: we cannot use fixext 4/8 due to a design error + // (in theory fixext 2 for handle<=0xff but we don't gain much from it) + char buf[MPACK_ITEM_SIZE]; + char *pos = buf; + mpack_uint(&pos, (uint32_t)handle); + ptrdiff_t packsize = pos - buf; + mpack_w(&packer->ptr, 0xc7); + mpack_w(&packer->ptr, packsize); + mpack_w(&packer->ptr, exttype); + // check_buffer(packer); + memcpy(packer->ptr, buf, (size_t)packsize); + packer->ptr += packsize; + } +} + +void mpack_object(Object *obj, PackerBuffer *packer) +{ + mpack_object_inner(obj, NULL, 0, packer); +} + +void mpack_object_array(Array arr, PackerBuffer *packer) +{ + mpack_array(&packer->ptr, (uint32_t)arr.size); + if (arr.size > 0) { + Object container = ARRAY_OBJ(arr); + mpack_object_inner(&arr.items[0], arr.size > 1 ? &container : NULL, 1, packer); + } +} + +typedef struct { + Object *container; + size_t idx; +} ContainerStackItem; + +void mpack_object_inner(Object *current, Object *container, size_t container_idx, + PackerBuffer *packer) + FUNC_ATTR_NONNULL_ARG(1, 4) +{ + // The inner loop of this function packs "current" and then fetches the next + // value from "container". "stack" is only used for nested containers. + kvec_withinit_t(ContainerStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + + while (true) { + check_buffer(packer); + switch (current->type) { + case kObjectTypeLuaRef: + // TODO(bfredl): could also be an error. Though kObjectTypeLuaRef + // should only appear when the caller has opted in to handle references, + // see nlua_pop_Object. + api_free_luaref(current->data.luaref); + current->data.luaref = LUA_NOREF; + FALLTHROUGH; + case kObjectTypeNil: + mpack_nil(&packer->ptr); + break; + case kObjectTypeBoolean: + mpack_bool(&packer->ptr, current->data.boolean); + break; + case kObjectTypeInteger: + mpack_integer(&packer->ptr, current->data.integer); + break; + case kObjectTypeFloat: + mpack_float8(&packer->ptr, current->data.floating); + break; + case kObjectTypeString: + mpack_str(current->data.string, packer); + break; + case kObjectTypeBuffer: + case kObjectTypeWindow: + case kObjectTypeTabpage: + mpack_handle(current->type, (handle_T)current->data.integer, packer); + break; + case kObjectTypeDictionary: + case kObjectTypeArray: {} + size_t current_size; + if (current->type == kObjectTypeArray) { + current_size = current->data.array.size; + mpack_array(&packer->ptr, (uint32_t)current_size); + } else { + current_size = current->data.dictionary.size; + mpack_map(&packer->ptr, (uint32_t)current_size); + } + if (current_size > 0) { + if (current->type == kObjectTypeArray && current_size == 1) { + current = ¤t->data.array.items[0]; + continue; + } + if (container) { + kvi_push(stack, ((ContainerStackItem) { + .container = container, + .idx = container_idx, + })); + } + container = current; + container_idx = 0; + } + break; + } + + if (!container) { + if (kv_size(stack)) { + ContainerStackItem it = kv_pop(stack); + container = it.container; + container_idx = it.idx; + } else { + break; + } + } + + if (container->type == kObjectTypeArray) { + Array arr = container->data.array; + current = &arr.items[container_idx++]; + if (container_idx >= arr.size) { + container = NULL; + } + } else { + Dictionary dict = container->data.dictionary; + KeyValuePair *it = &dict.items[container_idx++]; + check_buffer(packer); + mpack_str(it->key, packer); + current = &it->value; + if (container_idx >= dict.size) { + container = NULL; + } + } + } + kvi_destroy(stack); +} diff --git a/src/nvim/msgpack_rpc/packer.h b/src/nvim/msgpack_rpc/packer.h new file mode 100644 index 0000000000..8117bd09bd --- /dev/null +++ b/src/nvim/msgpack_rpc/packer.h @@ -0,0 +1,76 @@ +#pragma once + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "nvim/api/private/defs.h" +#include "nvim/msgpack_rpc/packer_defs.h" + +#define mpack_w(b, byte) *(*(b))++ = (char)(byte); +static inline void mpack_w2(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static inline void mpack_w4(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 24) & 0xff); + *(*b)++ = (char)((v >> 16) & 0xff); + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static inline void mpack_uint(char **buf, uint32_t val) +{ + if (val > 0xffff) { + mpack_w(buf, 0xce); + mpack_w4(buf, val); + } else if (val > 0xff) { + mpack_w(buf, 0xcd); + mpack_w2(buf, val); + } else if (val > 0x7f) { + mpack_w(buf, 0xcc); + mpack_w(buf, val); + } else { + mpack_w(buf, val); + } +} + +#define mpack_nil(buf) mpack_w(buf, 0xc0) +static inline void mpack_bool(char **buf, bool val) +{ + mpack_w(buf, 0xc2 | (val ? 1 : 0)); +} + +static inline void mpack_array(char **buf, uint32_t len) +{ + if (len < 0x10) { + mpack_w(buf, 0x90 | len); + } else if (len < 0x10000) { + mpack_w(buf, 0xdc); + mpack_w2(buf, len); + } else { + mpack_w(buf, 0xdd); + mpack_w4(buf, len); + } +} + +static inline void mpack_map(char **buf, uint32_t len) +{ + if (len < 0x10) { + mpack_w(buf, 0x80 | len); + } else if (len < 0x10000) { + mpack_w(buf, 0xde); + mpack_w2(buf, len); + } else { + mpack_w(buf, 0xdf); + mpack_w4(buf, len); + } +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "msgpack_rpc/packer.h.generated.h" +#endif diff --git a/src/nvim/msgpack_rpc/packer_defs.h b/src/nvim/msgpack_rpc/packer_defs.h new file mode 100644 index 0000000000..420f3dc424 --- /dev/null +++ b/src/nvim/msgpack_rpc/packer_defs.h @@ -0,0 +1,24 @@ +#pragma once + +#include <stddef.h> +#include <stdint.h> + +// Max possible length: bytecode + 8 int/float bytes +// Ext objects are maximum 8=3+5 (nested uint32 payload) +#define MPACK_ITEM_SIZE 9 + +typedef struct packer_buffer_t PackerBuffer; + +// Must ensure at least MPACK_ITEM_SIZE of space. +typedef void (*PackerBufferFlush)(PackerBuffer *self); + +struct packer_buffer_t { + char *startptr; + char *ptr; + char *endptr; + + // these are free to be used by packer_flush for any purpose, if want + void *anydata; + size_t anylen; + PackerBufferFlush packer_flush; +}; diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index f3627eaa61..56b03d67d0 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -6,9 +6,10 @@ #include "nvim/channel.h" #include "nvim/eval.h" +#include "nvim/event/defs.h" #include "nvim/event/socket.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/log.h" #include "nvim/main.h" #include "nvim/memory.h" diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 38263381bf..dbb30b0c9a 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -10,7 +10,6 @@ #include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/msgpack_rpc/channel_defs.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/unpacker.h" #include "nvim/ui_client.h" @@ -18,15 +17,18 @@ # include "msgpack_rpc/unpacker.c.generated.h" #endif -Object unpack(const char *data, size_t size, Error *err) +Object unpack(const char *data, size_t size, Arena *arena, Error *err) { Unpacker unpacker; mpack_parser_init(&unpacker.parser, 0); unpacker.parser.data.p = &unpacker; + unpacker.arena = *arena; int result = mpack_parse(&unpacker.parser, &data, &size, api_parse_enter, api_parse_exit); + *arena = unpacker.arena; + if (result == MPACK_NOMEM) { api_set_error(err, kErrorTypeException, "object was too deep to unpack"); } else if (result == MPACK_EOF) { diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h index 53af29761e..022d778013 100644 --- a/src/nvim/msgpack_rpc/unpacker.h +++ b/src/nvim/msgpack_rpc/unpacker.h @@ -1,19 +1,17 @@ #pragma once #include <inttypes.h> -#include <stdbool.h> #include <string.h> #include "mpack/mpack_core.h" #include "mpack/object.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" -#include "nvim/api/private/helpers.h" #include "nvim/grid_defs.h" #include "nvim/memory_defs.h" -#include "nvim/msgpack_rpc/channel_defs.h" +#include "nvim/msgpack_rpc/channel_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" -#include "nvim/ui_client.h" +#include "nvim/ui_defs.h" struct Unpacker { mpack_parser_t parser; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 1f789dc153..8ff47097fa 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -17,6 +17,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" @@ -34,19 +35,21 @@ #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/help.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" @@ -62,21 +65,25 @@ #include "nvim/quickfix.h" #include "nvim/search.h" #include "nvim/spell.h" +#include "nvim/spell_defs.h" #include "nvim/spellfile.h" #include "nvim/spellsuggest.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" #include "nvim/textformat.h" #include "nvim/textobject.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" -typedef struct normal_state { +typedef struct { VimState state; bool command_finished; bool ctrl_w; @@ -368,7 +375,7 @@ static int nv_compare(const void *s1, const void *s2) if (c2 < 0) { c2 = -c2; } - return c1 - c2; + return c1 == c2 ? 0 : c1 > c2 ? 1 : -1; } /// Initialize the nv_cmd_idx[] table. @@ -703,7 +710,7 @@ static void normal_get_additional_char(NormalState *s) int *cp; bool repl = false; // get character for replace mode bool lit = false; // get extra character literally - int lang; // getting a text character + bool lang; // getting a text character no_mapping++; allow_keys++; // no mapping for nchar, but allow key codes @@ -838,10 +845,10 @@ static void normal_get_additional_char(NormalState *s) no_mapping++; // Vim may be in a different mode when the user types the next key, // but when replaying a recording the next key is already in the - // typeahead buffer, so record a <Nop> before that to prevent the - // vpeekc() above from applying wrong mappings when replaying. + // typeahead buffer, so record an <Ignore> before that to prevent + // the vpeekc() above from applying wrong mappings when replaying. no_u_sync++; - gotchars_nop(); + gotchars_ignore(); no_u_sync--; } } @@ -1028,7 +1035,7 @@ normal_end: restart_VIsual_select = 0; } if (restart_edit != 0 && !VIsual_active && s->old_mapped_len == 0) { - (void)edit(restart_edit, false, 1); + edit(restart_edit, false, 1); } } @@ -1257,7 +1264,7 @@ static void normal_check_interrupt(NormalState *s) } else if (!global_busy || !exmode_active) { if (!quit_more) { // flush all buffers - (void)vgetc(); + vgetc(); } got_int = false; } @@ -1340,10 +1347,6 @@ static void normal_redraw(NormalState *s) show_cursor_info_later(false); - if (VIsual_active) { - redraw_curbuf_later(UPD_INVERTED); // update inverted part - } - if (must_redraw) { update_screen(); } else { @@ -1448,9 +1451,7 @@ static int normal_check(VimState *state) // has been done, close any file for startup messages. if (time_fd != NULL) { TIME_MSG("first screen update"); - TIME_MSG("--- NVIM STARTED ---"); - fclose(time_fd); - time_fd = NULL; + time_finish(); } // After the first screen update may start triggering WinScrolled // autocmd events. Store all the scroll positions and sizes now. @@ -1848,7 +1849,7 @@ void clear_showcmd(void) } if (VIsual_active && !char_avail()) { - int cursor_bot = lt(VIsual, curwin->w_cursor); + bool cursor_bot = lt(VIsual, curwin->w_cursor); int lines; colnr_T leftcol, rightcol; linenr_T top, bot; @@ -1862,8 +1863,8 @@ void clear_showcmd(void) bot = VIsual.lnum; } // Include closed folds as a whole. - (void)hasFolding(top, &top, NULL); - (void)hasFolding(bot, NULL, &bot); + hasFolding(top, &top, NULL); + hasFolding(bot, NULL, &bot); lines = bot - top + 1; if (VIsual_mode == Ctrl_V) { @@ -2186,7 +2187,7 @@ void check_scrollbind(linenr_T topline_diff, int leftcol_diff) // do the horizontal scroll if (want_hor) { - (void)set_leftcol(tgt_leftcol); + set_leftcol(tgt_leftcol); } } @@ -2257,7 +2258,7 @@ static void nv_page(cmdarg_T *cap) goto_tabpage(cap->count0); } } else { - (void)onepage(cap->arg, cap->count1); + onepage(cap->arg, cap->count1); } } @@ -2607,7 +2608,7 @@ void nv_scroll_line(cmdarg_T *cap) } /// Scroll "count" lines up or down, and redraw. -void scroll_redraw(int up, linenr_T count) +void scroll_redraw(bool up, linenr_T count) { linenr_T prev_topline = curwin->w_topline; int prev_skipcol = curwin->w_skipcol; @@ -2678,7 +2679,7 @@ static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg) LANGMAP_ADJUST(nchar, true); no_mapping--; allow_keys--; - (void)add_to_showcmd(nchar); + add_to_showcmd(nchar); if (nchar == K_DEL || nchar == K_KDEL) { n /= 10; @@ -2723,7 +2724,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar) LANGMAP_ADJUST(nchar, true); no_mapping--; allow_keys--; - (void)add_to_showcmd(nchar); + add_to_showcmd(nchar); if (vim_strchr("gGwW", nchar) == NULL) { clearopbeep(cap->oap); @@ -2873,8 +2874,8 @@ static void nv_zet(cmdarg_T *cap) case 'h': case K_LEFT: if (!curwin->w_p_wrap) { - (void)set_leftcol((colnr_T)cap->count1 > curwin->w_leftcol - ? 0 : curwin->w_leftcol - (colnr_T)cap->count1); + set_leftcol((colnr_T)cap->count1 > curwin->w_leftcol + ? 0 : curwin->w_leftcol - (colnr_T)cap->count1); } break; @@ -2887,7 +2888,7 @@ static void nv_zet(cmdarg_T *cap) case 'l': case K_RIGHT: if (!curwin->w_p_wrap) { - (void)set_leftcol(curwin->w_leftcol + (colnr_T)cap->count1); + set_leftcol(curwin->w_leftcol + (colnr_T)cap->count1); } break; @@ -3289,8 +3290,8 @@ static void nv_ctrlo(cmdarg_T *cap) static void nv_hat(cmdarg_T *cap) { if (!checkclearopq(cap->oap)) { - (void)buflist_getfile(cap->count0, 0, - GETF_SETMARK|GETF_ALT, false); + buflist_getfile(cap->count0, 0, + GETF_SETMARK|GETF_ALT, false); } } @@ -3555,7 +3556,7 @@ static void nv_ident(cmdarg_T *cap) init_history(); add_to_history(HIST_SEARCH, buf, true, NUL); - (void)normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0, NULL); + normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0, NULL); } else { g_tag_at_cursor = true; do_cmdline_cmd(buf); @@ -3639,8 +3640,8 @@ static void nv_scroll(cmdarg_T *cap) // Count a fold for one screen line. for (n = cap->count1 - 1; n > 0 && curwin->w_cursor.lnum > curwin->w_topline; n--) { - (void)hasFolding(curwin->w_cursor.lnum, - &curwin->w_cursor.lnum, NULL); + hasFolding(curwin->w_cursor.lnum, + &curwin->w_cursor.lnum, NULL); if (curwin->w_cursor.lnum > curwin->w_topline) { curwin->w_cursor.lnum--; } @@ -3681,7 +3682,7 @@ static void nv_scroll(cmdarg_T *cap) // Count a fold for one screen line. lnum = curwin->w_topline; while (n-- > 0 && lnum < curwin->w_botline - 1) { - (void)hasFolding(lnum, NULL, &lnum); + hasFolding(lnum, NULL, &lnum); lnum++; } n = lnum - curwin->w_topline; @@ -3901,7 +3902,7 @@ static void nv_gotofile(cmdarg_T *cap) if (ptr != NULL) { // do autowrite if necessary if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !buf_hide(curbuf)) { - (void)autowrite(curbuf, false); + autowrite(curbuf, false); } setpcmark(); if (do_ecmd(0, ptr, NULL, NULL, ECMD_LAST, @@ -3972,9 +3973,9 @@ static void nv_search(cmdarg_T *cap) return; } - (void)normal_search(cap, cap->cmdchar, cap->searchbuf, - (cap->arg || !equalpos(save_cursor, curwin->w_cursor)) - ? 0 : SEARCH_MARK, NULL); + normal_search(cap, cap->cmdchar, cap->searchbuf, + (cap->arg || !equalpos(save_cursor, curwin->w_cursor)) + ? 0 : SEARCH_MARK, NULL); } /// Handle "N" and "n" commands. @@ -3990,7 +3991,7 @@ static void nv_next(cmdarg_T *cap) // an offset is given and the cursor is on the last char in the buffer: // Repeat with count + 1. cap->count1 += 1; - (void)normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, NULL); + normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, NULL); cap->count1 -= 1; } } @@ -4124,7 +4125,7 @@ static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos) if (cap->nchar == 'm' || cap->nchar == 'M') { int c; // norm is true for "]M" and "[m" - int norm = ((findc == '{') == (cap->nchar == 'm')); + bool norm = ((findc == '{') == (cap->nchar == 'm')); n = cap->count1; // found a match: we were inside a method @@ -4290,9 +4291,9 @@ static void nv_brackets(cmdarg_T *cap) } else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE) { // [ or ] followed by a middle mouse click: put selected text with // indent adjustment. Any other button just does as usual. - (void)do_mouse(cap->oap, cap->nchar, - (cap->cmdchar == ']') ? FORWARD : BACKWARD, - cap->count1, PUT_FIXINDENT); + do_mouse(cap->oap, cap->nchar, + (cap->cmdchar == ']') ? FORWARD : BACKWARD, + cap->count1, PUT_FIXINDENT); } else if (cap->nchar == 'z') { // "[z" and "]z": move to start or end of open fold. if (foldMoveTo(false, cap->cmdchar == ']' ? FORWARD : BACKWARD, @@ -4557,7 +4558,7 @@ static void nv_replace(cmdarg_T *cap) // Insert the newline with an insert command, takes care of // autoindent. The insert command depends on being on the last // character of a line or not. - (void)del_chars(cap->count1, false); // delete the characters + del_chars(cap->count1, false); // delete the characters stuffcharReadbuff('\r'); stuffcharReadbuff(ESC); @@ -4722,7 +4723,7 @@ static void nv_vreplace(cmdarg_T *cap) /// Swap case for "~" command, when it does not work like an operator. static void n_swapchar(cmdarg_T *cap) { - int did_change = 0; + bool did_change = false; if (checkclearopq(cap->oap)) { return; @@ -5122,6 +5123,7 @@ static void n_start_visual_mode(int c) curwin->w_old_cursor_lnum = curwin->w_cursor.lnum; curwin->w_old_visual_lnum = curwin->w_cursor.lnum; } + redraw_curbuf_later(UPD_VALID); } /// CTRL-W: Window commands @@ -5313,7 +5315,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) } else { if (cap->count1 > 1) { // if it fails, let the cursor still move to the last char - (void)cursor_down(cap->count1 - 1, false); + cursor_down(cap->count1 - 1, false); } i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1; coladvance((colnr_T)i); @@ -5627,7 +5629,7 @@ static void nv_g_cmd(cmdarg_T *cap) case K_X2DRAG: case K_X2RELEASE: mod_mask = MOD_MASK_CTRL; - (void)do_mouse(oap, cap->nchar, BACKWARD, cap->count1, 0); + do_mouse(oap, cap->nchar, BACKWARD, cap->count1, 0); break; case K_IGNORE: @@ -5700,12 +5702,12 @@ static void n_opencmd(cmdarg_T *cap) if (cap->cmdchar == 'O') { // Open above the first line of a folded sequence of lines - (void)hasFolding(curwin->w_cursor.lnum, - &curwin->w_cursor.lnum, NULL); + hasFolding(curwin->w_cursor.lnum, + &curwin->w_cursor.lnum, NULL); } else { // Open below the last line of a folded sequence of lines - (void)hasFolding(curwin->w_cursor.lnum, - NULL, &curwin->w_cursor.lnum); + hasFolding(curwin->w_cursor.lnum, + NULL, &curwin->w_cursor.lnum); } // trigger TextChangedI for the 'o/O' command curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); @@ -6109,10 +6111,10 @@ static void nv_normal(cmdarg_T *cap) /// Don't even beep if we are canceling a command. static void nv_esc(cmdarg_T *cap) { - int no_reason = (cap->oap->op_type == OP_NOP - && cap->opcount == 0 - && cap->count0 == 0 - && cap->oap->regname == 0); + bool no_reason = (cap->oap->op_type == OP_NOP + && cap->opcount == 0 + && cap->count0 == 0 + && cap->oap->regname == 0); if (cap->arg) { // true for CTRL-C if (restart_edit == 0 && cmdwin_type == 0 && !VIsual_active && no_reason) { @@ -6444,6 +6446,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) bool was_visual = false; int dir; int flags = 0; + const int save_fen = curwin->w_p_fen; if (cap->oap->op_type != OP_NOP) { // "dp" is ":diffput" @@ -6494,6 +6497,10 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) savereg = copy_register(regname); } + // Temporarily disable folding, as deleting a fold marker may cause + // the cursor to be included in a fold. + curwin->w_p_fen = false; + // To place the cursor correctly after a blockwise put, and to leave the // text in the correct position when putting over a selection with // 'virtualedit' and past the end of the line, we use the 'c' operator in @@ -6544,9 +6551,12 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) xfree(savereg); } - // What to reselect with "gv"? Selecting the just put text seems to - // be the most useful, since the original text was removed. if (was_visual) { + if (save_fen) { + curwin->w_p_fen = true; + } + // What to reselect with "gv"? Selecting the just put text seems to + // be the most useful, since the original text was removed. curbuf->b_visual.vi_start = curbuf->b_op_start; curbuf->b_visual.vi_end = curbuf->b_op_end; // need to adjust cursor position @@ -6619,6 +6629,6 @@ void normal_cmd(oparg_T *oap, bool toplevel) s.toplevel = toplevel; s.oa = *oap; normal_prepare(&s); - (void)normal_execute(&s.state, safe_vgetc()); + normal_execute(&s.state, safe_vgetc()); *oap = s.oa; } diff --git a/src/nvim/normal.h b/src/nvim/normal.h index dbe74712fc..57a3418d92 100644 --- a/src/nvim/normal.h +++ b/src/nvim/normal.h @@ -1,9 +1,11 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include <stddef.h> // IWYU pragma: keep + #include "nvim/macros_defs.h" -#include "nvim/normal_defs.h" // IWYU pragma: export +#include "nvim/normal_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep /// Values for find_ident_under_cursor() enum { diff --git a/src/nvim/normal_defs.h b/src/nvim/normal_defs.h index 060c1057f9..0309f6bc80 100644 --- a/src/nvim/normal_defs.h +++ b/src/nvim/normal_defs.h @@ -16,7 +16,7 @@ typedef enum { } MotionType; /// Arguments for operators. -typedef struct oparg_S { +typedef struct { int op_type; ///< current pending operator type int regname; ///< register to use for the operator MotionType motion_type; ///< type of the current cursor motion @@ -42,7 +42,7 @@ typedef struct oparg_S { } oparg_T; /// Arguments for Normal mode commands. -typedef struct cmdarg_S { +typedef struct { oparg_T *oap; ///< Operator arguments int prechar; ///< prefix character (optional, always 'g') int cmdchar; ///< command character @@ -70,6 +70,5 @@ enum { REPLACE_NL_NCHAR = -2, }; -/// columns needed by shown command -enum { SHOWCMD_COLS = 10, }; +enum { SHOWCMD_COLS = 10, }; ///< columns needed by shown command enum { SHOWCMD_BUFLEN = SHOWCMD_COLS + 1 + 30, }; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3a4e87edf7..9b969a2337 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -9,13 +9,16 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/api/private/defs.h" #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" +#include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -23,23 +26,30 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_getln.h" #include "nvim/extmark.h" #include "nvim/fold.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" @@ -54,16 +64,34 @@ #include "nvim/plines.h" #include "nvim/search.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/terminal.h" #include "nvim/textformat.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/yankmap.h" -static yankreg_T y_regs[NUM_REGISTERS] = { 0 }; +struct yank_registers { + yankmap_T inner; +}; + +yank_registers_T y_regs; + +static yankreg_T *get_reg(yank_registers_T *regs, int idx) +{ + return yankmap_get(®s->inner, idx); + +} + +static yankreg_T *get_global_reg(int idx) +{ + return get_reg(&y_regs, idx); +} static yankreg_T *y_previous = NULL; // ptr to last written yankreg @@ -73,25 +101,6 @@ static bool clipboard_delay_update = false; // delay clipboard update static bool clipboard_needs_update = false; // clipboard was updated static bool clipboard_didwarn = false; -// structure used by block_prep, op_delete and op_yank for blockwise operators -// also op_change, op_shift, op_insert, op_replace - AKelly -struct block_def { - int startspaces; // 'extra' cols before first char - int endspaces; // 'extra' cols after last char - int textlen; // chars in block - char *textstart; // pointer to 1st char (partially) in block - colnr_T textcol; // index of chars (partially) in block - colnr_T start_vcol; // start col of 1st char wholly inside block - colnr_T end_vcol; // start col of 1st char wholly after block - int is_short; // true if line is too short to fit in block - int is_MAX; // true if curswant==MAXCOL when starting - int is_oneChar; // true if block within one character - int pre_whitesp; // screen cols of ws before block - int pre_whitesp_c; // chars of ws before block - colnr_T end_char_vcols; // number of vcols of post-block char - colnr_T start_char_vcols; // number of vcols of pre-block char -}; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ops.c.generated.h" #endif @@ -213,9 +222,8 @@ int get_extra_op_char(int optype) } /// handle a shift operation -void op_shift(oparg_T *oap, int curs_top, int amount) +void op_shift(oparg_T *oap, bool curs_top, int amount) { - int i; int block_col = 0; if (u_save((linenr_T)(oap->start.lnum - 1), @@ -227,7 +235,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) block_col = curwin->w_cursor.col; } - for (i = oap->line_count - 1; i >= 0; i--) { + for (int i = oap->line_count - 1; i >= 0; i--) { int first_char = (uint8_t)(*get_cursor_line_ptr()); if (first_char == NUL) { // empty line curwin->w_cursor.col = 0; @@ -288,7 +296,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) /// leaves cursor on first blank in the line. /// /// @param call_changed_bytes call changed_bytes() -void shift_line(int left, int round, int amount, int call_changed_bytes) +void shift_line(bool left, bool round, int amount, int call_changed_bytes) { const int sw_val = get_sw_value_indent(curbuf); @@ -324,7 +332,7 @@ void shift_line(int left, int round, int amount, int call_changed_bytes) if (State & VREPLACE_FLAG) { change_indent(INDENT_SET, count, false, NUL, call_changed_bytes); } else { - (void)set_indent(count, call_changed_bytes ? SIN_CHANGED : 0); + set_indent(count, call_changed_bytes ? SIN_CHANGED : 0); } } @@ -378,19 +386,21 @@ static void shift_block(oparg_T *oap, int amount) } // TODO(vim): is passing bd.textstart for start of the line OK? - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, - bd.start_vcol, bd.textstart, bd.textstart); - while (ascii_iswhite(*cts.cts_ptr)) { - incr = lbr_chartabsize_adv(&cts); + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, bd.textstart); + StrCharInfo ci = utf_ptr2StrCharInfo(bd.textstart); + int vcol = bd.start_vcol; + while (ascii_iswhite(ci.chr.value)) { + incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + ci = utfc_next(ci); total += incr; - cts.cts_vcol += incr; + vcol += incr; } - bd.textstart = cts.cts_ptr; - bd.start_vcol = cts.cts_vcol; - clear_chartabsize_arg(&cts); + bd.textstart = ci.ptr; + bd.start_vcol = vcol; - int tabs = 0, spaces = 0; + int tabs = 0; + int spaces = 0; // OK, now total=all the VWS reqd, and textstart points at the 1st // non-ws char in the block. if (!curbuf->b_p_et) { @@ -438,16 +448,13 @@ static void shift_block(oparg_T *oap, int amount) // The character's column is in "bd.start_vcol". colnr_T non_white_col = bd.start_vcol; - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, - non_white_col, bd.textstart, non_white); - while (ascii_iswhite(*cts.cts_ptr)) { - incr = lbr_chartabsize_adv(&cts); - cts.cts_vcol += incr; + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, bd.textstart); + while (ascii_iswhite(*non_white)) { + incr = win_charsize(cstype, non_white_col, non_white, (uint8_t)(*non_white), &csarg).width; + non_white_col += incr; + non_white++; } - non_white_col = cts.cts_vcol; - non_white = cts.cts_ptr; - clear_chartabsize_arg(&cts); const colnr_T block_space_width = non_white_col - oap->start_vcol; // We will shift by "total" or "block_space_width", whichever is less. @@ -468,19 +475,17 @@ static void shift_block(oparg_T *oap, int amount) if (bd.startspaces) { verbatim_copy_width -= bd.start_char_vcols; } - init_chartabsize_arg(&cts, curwin, 0, verbatim_copy_width, - bd.textstart, verbatim_copy_end); - while (cts.cts_vcol < destination_col) { - incr = lbr_chartabsize(&cts); - if (cts.cts_vcol + incr > destination_col) { + cstype = init_charsize_arg(&csarg, curwin, 0, bd.textstart); + StrCharInfo ci = utf_ptr2StrCharInfo(verbatim_copy_end); + while (verbatim_copy_width < destination_col) { + incr = win_charsize(cstype, verbatim_copy_width, ci.ptr, ci.chr.value, &csarg).width; + if (verbatim_copy_width + incr > destination_col) { break; } - cts.cts_vcol += incr; - MB_PTR_ADV(cts.cts_ptr); + verbatim_copy_width += incr; + ci = utfc_next(ci); } - verbatim_copy_width = cts.cts_vcol; - verbatim_copy_end = cts.cts_ptr; - clear_chartabsize_arg(&cts); + verbatim_copy_end = ci.ptr; // If "destination_col" is different from the width of the initial // part of the line that will be copied, it means we encountered a tab @@ -520,7 +525,7 @@ static void shift_block(oparg_T *oap, int amount) /// Insert string "s" (b_insert ? before : after) block :AKelly /// Caller must prepare for undo. -static void block_insert(oparg_T *oap, char *s, int b_insert, struct block_def *bdp) +static void block_insert(oparg_T *oap, char *s, bool b_insert, struct block_def *bdp) { int ts_val; int count = 0; // extra spaces to replace a cut TAB @@ -771,6 +776,24 @@ char *get_expr_line_src(void) return xstrdup(expr_line); } + +int get_userreg(int regname) +{ + if ((regname >= 'a' && regname <= 'z') + || (regname >= 'A' && regname <= 'Z') + || (regname >= '0' && regname <= '9') + || (regname <= 127 && strchr("\"-:.%#=*+_/", regname)) + || regname == Ctrl_F + || regname == Ctrl_P + || regname == Ctrl_W + || regname == Ctrl_A + || (regname + USER_REGISTERS_START) < regname) { + return -1; + } + + return regname + USER_REGISTERS_START; +} + /// @return whether `regname` is a valid name of a yank register. /// /// @note: There is no check for 0 (default register), caller should do this. @@ -787,12 +810,156 @@ bool valid_yank_reg(int regname, bool writing) || regname == '-' || regname == '_' || regname == '*' - || regname == '+') { + || regname == '+' + || get_userreg(regname) != -1) { return true; } return false; } +static int call_userreg_put(const char* urf, int regname, typval_T* out) +{ + char regname_str[5]; + int len; + + len = utf_char2len(regname); + regname_str[len] = 0; + utf_char2bytes(regname, regname_str); + + typval_T args[3]; + args[0].v_type = VAR_STRING; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_NUMBER; + + args[0].vval.v_string = "put"; + args[1].vval.v_string = regname_str; + args[2].vval.v_number = 0; + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + + return call_func( + urf, + -1, + out, + /* argcount_in = */ 3, + args, + &funcexe); +} + +// Converts a typval returned from the userregfunction to a register. +static void typval_to_yankreg(yankreg_T* yankreg, typval_T* val) +{ + if (!yankreg || !val) { + return; + } + + char* type; + dict_T* dict; + typval_T tv; + size_t i; + size_t sz; + + free_register(yankreg); + memset(yankreg, 0, sizeof(*yankreg)); + + switch (val->v_type) { + + case VAR_DICT: + dict = val->vval.v_dict; + type = tv_dict_get_string(dict, "type", false); + + if (!strcmp(type, "block")) { + yankreg->y_width = (int) tv_dict_get_number(dict, "width"); + yankreg->y_type = kMTBlockWise; + } else if (!strcmp(type, "line")) { + yankreg->y_type = kMTLineWise; + } else { + yankreg->y_type = kMTCharWise; + } + + if (tv_dict_get_tv(dict, "lines", &tv) == OK) { + if (tv.v_type == VAR_STRING) { + yankreg->y_array = (char**) xcalloc(sizeof(char*), 1); + yankreg->y_array[0] = strdup(tv.vval.v_string); + } else if (tv.v_type == VAR_LIST) { + yankreg->y_array = + (char**) xcalloc(sizeof(char*), (size_t) tv_list_len(tv.vval.v_list)); + + i = 0; + TV_LIST_ITER_CONST(tv.vval.v_list, li, { + if (li->li_tv.v_type == VAR_STRING) { + yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv)); + } else { + yankreg->y_array[i] = NULL; + } + ++ i; + }); + + yankreg->y_size = i; + } + } else { + yankreg->y_array = NULL; + } + + if (tv_dict_get_tv(dict, "additional_data", &tv) == OK) { + if (tv.v_type == VAR_DICT) { + yankreg->additional_data = tv.vval.v_dict; + } + } + break; + + case VAR_LIST: + yankreg->y_type = kMTLineWise; + sz = (size_t) tv_list_len(val->vval.v_list); + yankreg->y_array = (char**) xcalloc(sizeof(char*), sz); + yankreg->y_size = sz; + i = 0; + TV_LIST_ITER_CONST(val->vval.v_list, li, { + yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv)); + i ++; + }); + break; + + default: + yankreg->y_type = kMTCharWise; + yankreg->y_size = 1; + + if (val->vval.v_string) { + yankreg->y_array = (char**) xcalloc(sizeof(char*), 1); + yankreg->y_array[0] = strdup(tv_get_string(val)); + } else { + yankreg->y_array = NULL; + } + + break; + + } + + yankreg->timestamp = os_time(); +} + +static void copy_userreg(yankreg_T* into, int regname) +{ + if (!into) { + return; + } + + if (!curbuf->b_p_urf || strlen(curbuf->b_p_urf) == 0) { + return; + } + + typval_T* ret = xmalloc(sizeof(typval_T)); + + if (call_userreg_put(curbuf->b_p_urf, regname, ret) == FAIL) { + return; + } + + typval_to_yankreg(into, ret); + + tv_free(ret); +} + /// @return yankreg_T to use, according to the value of `regname`. /// Cannot handle the '_' (black hole) register. /// Must only be called with a valid register name! @@ -815,9 +982,15 @@ yankreg_T *get_yank_register(int regname, int mode) { yankreg_T *reg; - if (mode == YREG_PASTE && get_clipboard(regname, ®, false)) { + if ((mode == YREG_PASTE || mode == YREG_PUT) + && get_clipboard(regname, ®, false)) { // reg is set to clipboard contents. return reg; + } else if (mode == YREG_PUT && (regname == '*' || regname == '+')) { + // in case clipboard not available and we aren't actually pasting, + // return an empty register + static yankreg_T empty_reg = { .y_array = NULL }; + return &empty_reg; } else if (mode != YREG_YANK && (regname == 0 || regname == '"' || regname == '*' || regname == '+') && y_previous != NULL) { @@ -830,7 +1003,11 @@ yankreg_T *get_yank_register(int regname, int mode) if (i == -1) { i = 0; } - reg = &y_regs[i]; + reg = get_global_reg(i); + if (get_userreg(regname) != -1 && mode != YREG_YANK) { + // If the mode is not yank, copy the userreg data to the reg. + copy_userreg(reg, regname); + } if (mode == YREG_YANK) { // remember the written register for unnamed paste @@ -856,7 +1033,7 @@ yankreg_T *copy_register(int name) if (copy->y_size == 0) { copy->y_array = NULL; } else { - copy->y_array = xcalloc(copy->y_size, sizeof(char *)); + copy->y_array = (char**) xcalloc(copy->y_size, sizeof(char *)); for (size_t i = 0; i < copy->y_size; i++) { copy->y_array[i] = xstrdup(reg->y_array[i]); } @@ -887,8 +1064,7 @@ int do_record(int c) if (reg_recording == 0) { // start recording - // registers 0-9, a-z and " are allowed - if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) { + if (c < 0) { retval = FAIL; } else { reg_recording = c; @@ -909,14 +1085,15 @@ int do_record(int c) if (p != NULL) { // Remove escaping for K_SPECIAL in multi-byte chars. vim_unescape_ks(p); - (void)tv_dict_add_str(dict, S_LEN("regcontents"), p); + tv_dict_add_str(dict, S_LEN("regcontents"), p); } // Name of requested register, or empty string for unnamed operation. - char buf[NUMBUFLEN + 2]; - buf[0] = (char)regname; - buf[1] = NUL; - (void)tv_dict_add_str(dict, S_LEN("regname"), buf); + char buf[NUMBUFLEN + 5]; + int len = utf_char2len(regname); + utf_char2bytes(regname, buf); + buf[len] = NUL; + tv_dict_add_str(dict, S_LEN("regname"), buf); tv_dict_set_keys_readonly(dict); // Get the recorded key hits. K_SPECIAL will be escaped, this @@ -992,6 +1169,9 @@ static int stuff_yank(int regname, char *p) reg->y_type = kMTCharWise; } reg->timestamp = os_time(); + if (get_userreg(regname) != -1) { + return eval_yank_userreg(curbuf->b_p_urf, regname, reg); + } return OK; } @@ -1159,6 +1339,7 @@ int do_execreg(int regname, int colon, int addcr, int silent) } } reg_executing = regname == 0 ? '"' : regname; // disable the 'q' command + pending_end_reg_executing = false; } return retval; } @@ -1266,9 +1447,24 @@ int insert_reg(int regname, bool literally_arg) } else { for (size_t i = 0; i < reg->y_size; i++) { if (regname == '-') { + Direction dir = BACKWARD; + if ((State & REPLACE_FLAG) != 0) { + pos_T curpos; + if (u_save_cursor() == FAIL) { + return FAIL; + } + del_chars(mb_charlen(reg->y_array[0]), true); + curpos = curwin->w_cursor; + if (oneright() == FAIL) { + // hit end of line, need to put forward (after the current position) + dir = FORWARD; + } + curwin->w_cursor = curpos; + } + AppendCharToRedobuff(Ctrl_R); AppendCharToRedobuff(regname); - do_put(regname, NULL, BACKWARD, 1, PUT_CURSEND); + do_put(regname, NULL, dir, 1, PUT_CURSEND); } else { stuffescaped(reg->y_array[i], literally); } @@ -1284,6 +1480,90 @@ int insert_reg(int regname, bool literally_arg) return retval; } +/// Converts a yankreg to a dict which can be used as an argument to the +// userregfunc. +static dict_T* yankreg_to_dict(yankreg_T* yankreg) { + dict_T *const dict = tv_dict_alloc(); + dict->dv_refcount = 1; + tv_dict_add_nr(dict, S_LEN("width"), yankreg->y_width); + + const char* type; + + switch(yankreg->y_type) { + case kMTLineWise: + type = "line"; + break; + case kMTCharWise: + type = "char"; + break; + case kMTBlockWise: + type = "block"; + break; + default: + type = "unknown"; + } + + tv_dict_add_str(dict, S_LEN("type"), type); + if (yankreg->additional_data) { + tv_dict_add_dict(dict, S_LEN("additional_data"), yankreg->additional_data); + } + + list_T *const lines = tv_list_alloc((long)yankreg->y_size); + + size_t i; + for (i = 0; i < yankreg->y_size; ++ i) { + tv_list_append_string( + lines, yankreg->y_array[i], (long)strlen(yankreg->y_array[i])); + } + + tv_dict_add_list(dict, S_LEN("lines"), lines); + + return dict; +} + +/* + * Executes the yank() function on a user-defined register to set the contents + * of that register. + */ +static int eval_yank_userreg(const char *ufn, int regname, yankreg_T *reg) +{ + if (!reg) + return -1; + + int ret, len; + char regname_str[5]; + + len = (*utf_char2len)(regname); + regname_str[len] = 0; + utf_char2bytes(regname, regname_str); + + typval_T args[4]; + args[0].v_type = VAR_STRING; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_DICT; + args[3].v_type = VAR_UNKNOWN; + + args[0].vval.v_string = "yank"; + args[1].vval.v_string = regname_str; + args[2].vval.v_dict = yankreg_to_dict(reg); + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + + typval_T* out = xmalloc(sizeof(typval_T)); + return call_func( + ufn, + -1, + out, + /* argcount_in = */ 3, + args, + &funcexe + ); + + tv_free(out); + return ret; +} + /// If "regname" is a special register, return true and store a pointer to its /// value in "argp". /// @@ -1367,6 +1647,9 @@ bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg) case '_': // black hole: always empty *argp = ""; return true; + + default: + break; } return false; @@ -1413,14 +1696,14 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr) /// Shift the delete registers: "9 is cleared, "8 becomes "9, etc. static void shift_delete_registers(bool y_append) { - free_register(&y_regs[9]); // free register "9 + free_register(get_global_reg(9)); // free register "9 for (int n = 9; n > 1; n--) { - y_regs[n] = y_regs[n - 1]; + *get_global_reg(n) = *get_global_reg(n - 1); } if (!y_append) { - y_previous = &y_regs[1]; + y_previous = get_global_reg(1); } - y_regs[1].y_array = NULL; // set register "1 to empty + get_global_reg(1)->y_array = NULL; // set register "1 to empty } /// Handle a delete operation. @@ -1495,7 +1778,7 @@ int op_delete(oparg_T *oap) // register. For the black hole register '_' don't yank anything. if (oap->regname != '_') { yankreg_T *reg = NULL; - int did_yank = false; + bool did_yank = false; if (oap->regname != 0) { // check for read-only register if (!valid_yank_reg(oap->regname, true)) { @@ -1513,7 +1796,7 @@ int op_delete(oparg_T *oap) if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) { shift_delete_registers(is_append_register(oap->regname)); - reg = &y_regs[1]; + reg = get_global_reg(1); op_yank_reg(oap, false, reg, false); did_yank = true; } @@ -1695,8 +1978,8 @@ int op_delete(oparg_T *oap) } } - (void)del_bytes((colnr_T)n, !virtual_op, - oap->op_type == OP_DELETE && !oap->is_VIsual); + del_bytes((colnr_T)n, !virtual_op, + oap->op_type == OP_DELETE && !oap->is_VIsual); } else { // delete characters between lines pos_T curpos; @@ -1722,10 +2005,10 @@ int op_delete(oparg_T *oap) // delete from start of line until op_end int n = (oap->end.col + 1 - !oap->inclusive); curwin->w_cursor.col = 0; - (void)del_bytes((colnr_T)n, !virtual_op, - oap->op_type == OP_DELETE && !oap->is_VIsual); + del_bytes((colnr_T)n, !virtual_op, + oap->op_type == OP_DELETE && !oap->is_VIsual); curwin->w_cursor = curpos; // restore curwin->w_cursor - (void)do_join(2, false, false, false, false); + do_join(2, false, false, false, false); curbuf_splice_pending--; extmark_splice(curbuf, (int)startpos.lnum - 1, startpos.col, (int)oap->line_count - 1, n, deleted_bytes, @@ -1760,8 +2043,13 @@ static void mb_adjust_opend(oparg_T *oap) return; } - char *p = ml_get(oap->end.lnum); - oap->end.col += utf_cp_tail_off(p, p + oap->end.col); + const char *line = ml_get(oap->end.lnum); + const char *ptr = line + oap->end.col; + if (*ptr != NUL) { + ptr -= utf_head_off(line, ptr); + ptr += utfc_ptr2len(ptr) - 1; + oap->end.col = (colnr_T)(ptr - line); + } } /// Put character 'c' at position 'lp' @@ -1793,7 +2081,7 @@ static int op_replace(oparg_T *oap, int c) int n; struct block_def bd; char *after_p = NULL; - int had_ctrl_v_cr = false; + bool had_ctrl_v_cr = false; if ((curbuf->b_ml.ml_flags & ML_EMPTY) || oap->empty) { return OK; // nothing to do @@ -1815,11 +2103,6 @@ static int op_replace(oparg_T *oap, int c) // block mode replace if (oap->motion_type == kMTBlockWise) { - int numc; - int num_chars; - char *newp; - char *oldp; - colnr_T oldlen; bd.is_MAX = (curwin->w_curswant == MAXCOL); for (; curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) { curwin->w_cursor.col = 0; // make sure cursor position is valid @@ -1850,7 +2133,7 @@ static int op_replace(oparg_T *oap, int c) && !bd.is_oneChar && bd.end_char_vcols > 0) ? bd.end_char_vcols - 1 : 0; // Figure out how many characters to replace. - numc = oap->end_vcol - oap->start_vcol + 1; + int numc = oap->end_vcol - oap->start_vcol + 1; if (bd.is_short && (!virtual_op || bd.is_MAX)) { numc -= (oap->end_vcol - bd.end_vcol) + 1; } @@ -1866,11 +2149,11 @@ static int op_replace(oparg_T *oap, int c) } // Compute bytes needed, move character count to num_chars. - num_chars = numc; + int num_chars = numc; numc *= utf_char2len(c); - oldp = get_cursor_line_ptr(); - oldlen = (int)strlen(oldp); + char *oldp = get_cursor_line_ptr(); + colnr_T oldlen = (int)strlen(oldp); size_t newp_size = (size_t)bd.textcol + (size_t)bd.startspaces; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { @@ -1880,7 +2163,7 @@ static int op_replace(oparg_T *oap, int c) - bd.textcol - bd.textlen); } } - newp = xmallocz(newp_size); + char *newp = xmallocz(newp_size); // copy up to deleted part memmove(newp, oldp, (size_t)bd.textcol); oldp += bd.textcol + bd.textlen; @@ -1891,7 +2174,8 @@ static int op_replace(oparg_T *oap, int c) size_t after_p_len = 0; int col = oldlen - bd.textcol - bd.textlen + 1; assert(col >= 0); - int newrows = 0, newcols = 0; + int newrows = 0; + int newcols = 0; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { // strlen(newp) at this point int newp_len = bd.textcol + bd.startspaces; @@ -2030,7 +2314,7 @@ static int op_replace(oparg_T *oap, int c) void op_tilde(oparg_T *oap) { struct block_def bd; - int did_change = false; + bool did_change = false; if (u_save((linenr_T)(oap->start.lnum - 1), (linenr_T)(oap->end.lnum + 1)) == FAIL) { @@ -2040,11 +2324,9 @@ void op_tilde(oparg_T *oap) pos_T pos = oap->start; if (oap->motion_type == kMTBlockWise) { // Visual block mode for (; pos.lnum <= oap->end.lnum; pos.lnum++) { - int one_change; - block_prep(oap, &bd, pos.lnum, false); pos.col = bd.textcol; - one_change = swapchars(oap->op_type, &pos, bd.textlen); + bool one_change = swapchars(oap->op_type, &pos, bd.textlen); did_change |= one_change; } if (did_change) { @@ -2142,16 +2424,16 @@ bool swapchar(int op_type, pos_T *pos) return false; } - if (op_type == OP_UPPER && c == 0xdf) { + // ~ is OP_NOP, g~ is OP_TILDE, gU is OP_UPPER + if ((op_type == OP_UPPER || op_type == OP_NOP || op_type == OP_TILDE) && c == 0xdf) { pos_T sp = curwin->w_cursor; - // Special handling of German sharp s: change to "SS". + // Special handling for lowercase German sharp s (ß): convert to uppercase (ẞ). curwin->w_cursor = *pos; del_char(false); - ins_char('S'); - ins_char('S'); + ins_char(0x1E9E); curwin->w_cursor = sp; - inc(pos); + return true; } int nc = c; @@ -2270,7 +2552,7 @@ void op_insert(oparg_T *oap, int count1) pos_T t1 = oap->start; const pos_T start_insert = curwin->w_cursor; - (void)edit(NUL, false, (linenr_T)count1); + edit(NUL, false, (linenr_T)count1); // When a tab was inserted, and the characters in front of the tab // have been converted to a tab as well, the column of the cursor @@ -2472,9 +2754,6 @@ int op_change(oparg_T *oap) ins_len = (int)strlen(firstline) - pre_textlen; if (ins_len > 0) { - int offset; - char *newp; - char *oldp; // Subsequent calls to ml_get() flush the firstline data - take a // copy of the inserted text. char *ins_text = xmalloc((size_t)ins_len + 1); @@ -2489,16 +2768,16 @@ int op_change(oparg_T *oap) // initial coladd offset as part of "startspaces" if (bd.is_short) { vpos.lnum = linenr; - (void)getvpos(&vpos, oap->start_vcol); + getvpos(&vpos, oap->start_vcol); } else { vpos.coladd = 0; } - oldp = ml_get(linenr); - newp = xmalloc(strlen(oldp) + (size_t)vpos.coladd - + (size_t)ins_len + 1); + char *oldp = ml_get(linenr); + char *newp = xmalloc(strlen(oldp) + (size_t)vpos.coladd + + (size_t)ins_len + 1); // copy up to block start memmove(newp, oldp, (size_t)bd.textcol); - offset = bd.textcol; + int offset = bd.textcol; memset(newp + offset, ' ', (size_t)vpos.coladd); offset += vpos.coladd; memmove(newp + offset, ins_text, (size_t)ins_len); @@ -2520,11 +2799,20 @@ int op_change(oparg_T *oap) return retval; } + +/* + * set all the yank registers to empty (called from main()) + */ +void init_yank(void) +{ + init_yankmap(&y_regs.inner); +} + #if defined(EXITFREE) void clear_registers(void) { for (int i = 0; i < NUM_REGISTERS; i++) { - free_register(&y_regs[i]); + free_register(get_global_reg(i)); } } @@ -2570,6 +2858,14 @@ bool op_yank(oparg_T *oap, bool message) yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); op_yank_reg(oap, message, reg, is_append_register(oap->regname)); + + if (get_userreg(oap->regname) != -1) { + if (eval_yank_userreg(curbuf->b_p_urf, oap->regname, reg) == -1) { + beep_flush(); + return false; + } + } + set_clipboard(oap->regname, reg); do_autocmd_textyankpost(oap, reg); @@ -2635,66 +2931,11 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) reg->y_array[y_idx] = xstrdup(ml_get(lnum)); break; - case kMTCharWise: { - colnr_T startcol = 0, endcol = MAXCOL; - int is_oneChar = false; - colnr_T cs, ce; - char *p = ml_get(lnum); - bd.startspaces = 0; - bd.endspaces = 0; - - if (lnum == oap->start.lnum) { - startcol = oap->start.col; - if (virtual_op) { - getvcol(curwin, &oap->start, &cs, NULL, &ce); - if (ce != cs && oap->start.coladd > 0) { - // Part of a tab selected -- but don't double-count it. - bd.startspaces = (ce - cs + 1) - oap->start.coladd; - if (bd.startspaces < 0) { - bd.startspaces = 0; - } - startcol++; - } - } - } - - if (lnum == oap->end.lnum) { - endcol = oap->end.col; - if (virtual_op) { - getvcol(curwin, &oap->end, &cs, NULL, &ce); - if (p[endcol] == NUL || (cs + oap->end.coladd < ce - // Don't add space for double-wide - // char; endcol will be on last byte - // of multi-byte char. - && utf_head_off(p, p + endcol) == 0)) { - if (oap->start.lnum == oap->end.lnum - && oap->start.col == oap->end.col) { - // Special case: inside a single char - is_oneChar = true; - bd.startspaces = oap->end.coladd - - oap->start.coladd + oap->inclusive; - endcol = startcol; - } else { - bd.endspaces = oap->end.coladd - + oap->inclusive; - endcol -= oap->inclusive; - } - } - } - } - if (endcol == MAXCOL) { - endcol = (colnr_T)strlen(p); - } - if (startcol > endcol - || is_oneChar) { - bd.textlen = 0; - } else { - bd.textlen = endcol - startcol + oap->inclusive; - } - bd.textstart = p + startcol; + case kMTCharWise: + charwise_block_prep(oap->start, oap->end, &bd, lnum, oap->inclusive); yank_copy_line(reg, &bd, y_idx, false); break; - } + // NOTREACHED case kMTUnknown: abort(); @@ -2748,7 +2989,11 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) if (oap->regname == NUL) { *namebuf = NUL; } else { - vim_snprintf(namebuf, sizeof(namebuf), _(" into \"%c"), oap->regname); + char buf[5]; + int len = (*utf_char2len) (oap->regname); + utf_char2bytes(oap->regname, buf); + buf[len] = 0; + vim_snprintf(namebuf, sizeof(namebuf), _(" into \"%s"), buf); } // redisplay now, so message is not deleted @@ -2818,6 +3063,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) FUNC_ATTR_NONNULL_ALL { static bool recursive = false; + int len; if (recursive || !has_event(EVENT_TEXTYANKPOST)) { // No autocommand was defined, or we yanked from this autocommand. @@ -2836,17 +3082,18 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) tv_list_append_string(list, reg->y_array[i], -1); } tv_list_set_lock(list, VAR_FIXED); - (void)tv_dict_add_list(dict, S_LEN("regcontents"), list); + tv_dict_add_list(dict, S_LEN("regcontents"), list); // Register type. - char buf[NUMBUFLEN + 2]; + char buf[NUMBUFLEN + 6]; format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); - (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); + tv_dict_add_str(dict, S_LEN("regtype"), buf); // Name of requested register, or empty string for unnamed operation. - buf[0] = (char)oap->regname; - buf[1] = NUL; - (void)tv_dict_add_str(dict, S_LEN("regname"), buf); + len = utf_char2len(oap->regname); + buf[len] = 0; + utf_char2bytes(oap->regname, buf); + tv_dict_add_str(dict, S_LEN("regname"), buf); // Motion type: inclusive or exclusive. tv_dict_add_bool(dict, S_LEN("inclusive"), @@ -2855,11 +3102,11 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) // Kind of operation: yank, delete, change). buf[0] = (char)get_op_char(oap->op_type); buf[1] = NUL; - (void)tv_dict_add_str(dict, S_LEN("operator"), buf); + tv_dict_add_str(dict, S_LEN("operator"), buf); // Selection type: visual or not. - (void)tv_dict_add_bool(dict, S_LEN("visual"), - oap->is_VIsual ? kBoolVarTrue : kBoolVarFalse); + tv_dict_add_bool(dict, S_LEN("visual"), + oap->is_VIsual ? kBoolVarTrue : kBoolVarFalse); tv_dict_set_keys_readonly(dict); textlock++; @@ -2870,6 +3117,56 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) recursive = false; } +/// Execute autocommands for TextPutPost. +/// +/// @param oap Operator arguments. +/// @param reg The yank register used. +static void do_autocmd_textputpost(int regname, yankreg_T *reg) + FUNC_ATTR_NONNULL_ALL +{ + static bool recursive = false; + int len; + + if (recursive || !has_event(EVENT_TEXTPUTPOST)) { + // No autocommand was defined, or we yanked from this autocommand. + return; + } + + recursive = true; + + save_v_event_T save_v_event; + // Set the v:event dictionary with information about the yank. + dict_T *dict = get_v_event(&save_v_event); + + // The yanked text contents. + list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); + for (size_t i = 0; i < reg->y_size; i++) { + tv_list_append_string(list, (const char *)reg->y_array[i], -1); + } + tv_list_set_lock(list, VAR_FIXED); + (void)tv_dict_add_list(dict, S_LEN("regcontents"), list); + + // Register type. + char buf[NUMBUFLEN + 6]; + format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); + (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); + + // Name of requested register, or empty string for unnamed operation. + len = (*utf_char2len)(regname); + buf[len] = 0; + utf_char2bytes(regname, buf); + recursive = true; + (void)tv_dict_add_str(dict, S_LEN("regname"), buf); + + tv_dict_set_keys_readonly(dict); + textlock++; + apply_autocmds(EVENT_TEXTPUTPOST, NULL, NULL, false, curbuf); + textlock--; + restore_v_event(dict, &save_v_event); + + recursive = false; +} + /// Put contents of register "regname" into the text. /// Caller must check "regname" to be valid! /// @@ -2928,7 +3225,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) if (flags & PUT_LINE) { stuffcharReadbuff(command_start_char); for (; count > 0; count--) { - (void)stuff_inserted(NUL, 1, count != 1); + stuff_inserted(NUL, 1, count != 1); if (count != 1) { // To avoid 'autoindent' affecting the text, use Ctrl_U to remove any // whitespace. Can't just insert Ctrl_U into readbuf1, this would go @@ -2940,7 +3237,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } } } else { - (void)stuff_inserted(command_start_char, count, false); + stuff_inserted(command_start_char, count, false); } // Putting the text is done later, so can't move the cursor to the next @@ -3050,6 +3347,10 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) reg = get_yank_register(regname, YREG_PASTE); } + if (get_userreg(regname) != -1) { + copy_userreg(reg, regname); + } + y_type = reg->y_type; y_width = reg->y_width; y_size = reg->y_size; @@ -3061,6 +3362,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) return; } + colnr_T split_pos = 0; if (y_type == kMTLineWise) { if (flags & PUT_LINE_SPLIT) { // "p" or "P" in Visual mode: split the lines to put the text in @@ -3068,23 +3370,24 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) if (u_save_cursor() == FAIL) { goto end; } - char *p = get_cursor_pos_ptr(); + char *curline = get_cursor_line_ptr(); + char *p = curline + curwin->w_cursor.col; if (dir == FORWARD && *p != NUL) { MB_PTR_ADV(p); } + // we need this later for the correct extmark_splice() event + split_pos = (colnr_T)(p - curline); + char *ptr = xstrdup(p); ml_append(curwin->w_cursor.lnum, ptr, 0, false); xfree(ptr); - char *oldp = get_cursor_line_ptr(); - p = oldp + curwin->w_cursor.col; - if (dir == FORWARD && *p != NUL) { - MB_PTR_ADV(p); - } - ptr = xmemdupz(oldp, (size_t)(p - oldp)); + ptr = xmemdupz(get_cursor_line_ptr(), (size_t)split_pos); ml_replace(curwin->w_cursor.lnum, ptr, false); nr_lines++; dir = FORWARD; + + buf_updates_send_changes(curbuf, curwin->w_cursor.lnum, 1, 1); } if (flags & PUT_LINE_FORWARD) { // Must be "p" for a Visual block, put lines below the block. @@ -3118,9 +3421,9 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) // Correct line number for closed fold. Don't move the cursor yet, // u_save() uses it. if (dir == BACKWARD) { - (void)hasFolding(lnum, &lnum, NULL); + hasFolding(lnum, &lnum, NULL); } else { - (void)hasFolding(lnum, NULL, &lnum); + hasFolding(lnum, NULL, &lnum); } if (dir == FORWARD) { lnum++; @@ -3225,19 +3528,19 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } // get the old line and advance to the position to insert at char *oldp = get_cursor_line_ptr(); - size_t oldlen = strlen(oldp); - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0, oldp, oldp); - while (cts.cts_vcol < col && *cts.cts_ptr != NUL) { - // Count a tab for what it's worth (if list mode not on) - incr = lbr_chartabsize_adv(&cts); - cts.cts_vcol += incr; + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, oldp); + StrCharInfo ci = utf_ptr2StrCharInfo(oldp); + vcol = 0; + while (vcol < col && *ci.ptr != NUL) { + incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + vcol += incr; + ci = utfc_next(ci); } - vcol = cts.cts_vcol; - char *ptr = cts.cts_ptr; + size_t oldlen = (size_t)(ci.ptr - oldp) + strlen(ci.ptr); + char *ptr = ci.ptr; bd.textcol = (colnr_T)(ptr - oldp); - clear_chartabsize_arg(&cts); shortline = (vcol < col) || (vcol == col && !*ptr); @@ -3261,16 +3564,15 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) yanklen = (int)strlen(y_array[i]); if ((flags & PUT_BLOCK_INNER) == 0) { - // calculate number of spaces required to fill right side of - // block + // calculate number of spaces required to fill right side of block spaces = y_width + 1; - init_chartabsize_arg(&cts, curwin, 0, 0, y_array[i], y_array[i]); - for (int j = 0; j < yanklen; j++) { - spaces -= lbr_chartabsize(&cts); - cts.cts_ptr++; - cts.cts_vcol = 0; + + cstype = init_charsize_arg(&csarg, curwin, 0, y_array[i]); + ci = utf_ptr2StrCharInfo(y_array[i]); + while (*ci.ptr != NUL) { + spaces -= win_charsize(cstype, 0, ci.ptr, ci.chr.value, &csarg).width; + ci = utfc_next(ci); } - clear_chartabsize_arg(&cts); if (spaces < 0) { spaces = 0; } @@ -3344,13 +3646,11 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } curbuf->b_op_end.coladd = 0; if (flags & PUT_CURSEND) { - colnr_T len; - curwin->w_cursor = curbuf->b_op_end; curwin->w_cursor.col++; // in Insert mode we might be after the NUL, correct for that - len = (colnr_T)strlen(get_cursor_line_ptr()); + colnr_T len = (colnr_T)strlen(get_cursor_line_ptr()); if (curwin->w_cursor.col > len) { curwin->w_cursor.col = len; } @@ -3533,7 +3833,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } else if ((indent = get_indent() + indent_diff) < 0) { indent = 0; } - (void)set_indent(indent, SIN_NOMARK); + set_indent(indent, SIN_NOMARK); curwin->w_cursor = old_pos; // remember how many chars were removed if (cnt == count && i == y_size - 1) { @@ -3545,7 +3845,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) bcount_t totsize = 0; int lastsize = 0; if (y_type == kMTCharWise - || (y_type == kMTLineWise && flags & PUT_LINE_SPLIT)) { + || (y_type == kMTLineWise && (flags & PUT_LINE_SPLIT))) { for (i = 0; i < y_size - 1; i++) { totsize += (bcount_t)strlen(y_array[i]) + 1; } @@ -3556,9 +3856,9 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) extmark_splice(curbuf, (int)new_cursor.lnum - 1, col, 0, 0, 0, (int)y_size - 1, lastsize, totsize, kExtmarkUndo); - } else if (y_type == kMTLineWise && flags & PUT_LINE_SPLIT) { + } else if (y_type == kMTLineWise && (flags & PUT_LINE_SPLIT)) { // Account for last pasted NL + last NL - extmark_splice(curbuf, (int)new_cursor.lnum - 1, col + 1, 0, 0, 0, + extmark_splice(curbuf, (int)new_cursor.lnum - 1, split_pos, 0, 0, 0, (int)y_size + 1, 0, totsize + 2, kExtmarkUndo); } @@ -3652,6 +3952,10 @@ error: } end: + if (reg) { + do_autocmd_textputpost(regname, reg); + } + if (cmdmod.cmod_flags & CMOD_LOCKMARKS) { curbuf->b_op_start = orig_start; curbuf->b_op_end = orig_end; @@ -3696,7 +4000,7 @@ void adjust_cursor_eol(void) } /// @return true if lines starting with '#' should be left aligned. -int preprocs_left(void) +bool preprocs_left(void) { return ((curbuf->b_p_si && !curbuf->b_p_cin) || (curbuf->b_p_cin && in_cinkeys('#', ' ', true) @@ -3724,7 +4028,7 @@ int get_register_name(int num) /// @return the index of the register "" points to. int get_unname_register(void) { - return y_previous == NULL ? -1 : (int)(y_previous - &y_regs[0]); + return yankmap_find(&y_regs.inner, y_previous); } /// ":dis" and ":registers": Display the contents of the yank registers. @@ -3761,10 +4065,10 @@ void ex_display(exarg_T *eap) if (y_previous != NULL) { yb = y_previous; } else { - yb = &(y_regs[0]); + yb = get_global_reg(0); } } else { - yb = &(y_regs[i]); + yb = get_global_reg(i); } get_clipboard(name, &yb, true); @@ -3956,7 +4260,7 @@ char *skip_comment(char *line, bool process, bool include_space, bool *is_commen /// to set those marks. /// /// @return FAIL for failure, OK otherwise -int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions, bool setmark) +int do_join(size_t count, bool insert_space, bool save_undo, bool use_formatoptions, bool setmark) { char *curr = NULL; char *curr_start = NULL; @@ -3967,8 +4271,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions int sumsize = 0; // size of the long new line int ret = OK; int *comments = NULL; - int remove_comments = (use_formatoptions == true) - && has_format_option(FO_REMOVE_COMS); + bool remove_comments = use_formatoptions && has_format_option(FO_REMOVE_COMS); bool prev_was_comment = false; assert(count >= 1); @@ -4185,7 +4488,7 @@ static void restore_lbr(bool lbr_saved) /// - textlen includes the first/last char to be wholly yanked /// - start/endspaces is the number of columns of the first/last yanked char /// that are to be yanked. -static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del) +void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del) { int incr = 0; // Avoid a problem with unwanted linebreaks in block mode. @@ -4206,25 +4509,25 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool char *line = ml_get(lnum); char *prev_pstart = line; - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, lnum, bdp->start_vcol, line, line); - while (cts.cts_vcol < oap->start_vcol && *cts.cts_ptr != NUL) { - // Count a tab for what it's worth (if list mode not on) - incr = lbr_chartabsize(&cts); - cts.cts_vcol += incr; - if (ascii_iswhite(*cts.cts_ptr)) { + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, lnum, line); + StrCharInfo ci = utf_ptr2StrCharInfo(line); + int vcol = bdp->start_vcol; + while (vcol < oap->start_vcol && *ci.ptr != NUL) { + incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + vcol += incr; + if (ascii_iswhite(ci.chr.value)) { bdp->pre_whitesp += incr; bdp->pre_whitesp_c++; } else { bdp->pre_whitesp = 0; bdp->pre_whitesp_c = 0; } - prev_pstart = cts.cts_ptr; - MB_PTR_ADV(cts.cts_ptr); + prev_pstart = ci.ptr; + ci = utfc_next(ci); } - bdp->start_vcol = cts.cts_vcol; - char *pstart = cts.cts_ptr; - clear_chartabsize_arg(&cts); + bdp->start_vcol = vcol; + char *pstart = ci.ptr; bdp->start_char_vcols = incr; if (bdp->start_vcol < oap->start_vcol) { // line too short @@ -4261,17 +4564,18 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool } } } else { - init_chartabsize_arg(&cts, curwin, lnum, bdp->end_vcol, line, pend); + cstype = init_charsize_arg(&csarg, curwin, lnum, line); + ci = utf_ptr2StrCharInfo(pend); + vcol = bdp->end_vcol; char *prev_pend = pend; - while (cts.cts_vcol <= oap->end_vcol && *cts.cts_ptr != NUL) { - // Count a tab for what it's worth (if list mode not on) - prev_pend = cts.cts_ptr; - incr = lbr_chartabsize_adv(&cts); - cts.cts_vcol += incr; + while (vcol <= oap->end_vcol && *ci.ptr != NUL) { + prev_pend = ci.ptr; + incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + vcol += incr; + ci = utfc_next(ci); } - bdp->end_vcol = cts.cts_vcol; - pend = cts.cts_ptr; - clear_chartabsize_arg(&cts); + bdp->end_vcol = vcol; + pend = ci.ptr; if (bdp->end_vcol <= oap->end_vcol && (!is_del @@ -4307,6 +4611,65 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool restore_lbr(lbr_saved); } +/// Get block text from "start" to "end" +void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T lnum, + bool inclusive) +{ + colnr_T startcol = 0; + colnr_T endcol = MAXCOL; + bool is_oneChar = false; + colnr_T cs, ce; + char *p = ml_get(lnum); + bdp->startspaces = 0; + bdp->endspaces = 0; + + if (lnum == start.lnum) { + startcol = start.col; + if (virtual_op) { + getvcol(curwin, &start, &cs, NULL, &ce); + if (ce != cs && start.coladd > 0) { + // Part of a tab selected -- but don't double-count it. + bdp->startspaces = (ce - cs + 1) - start.coladd; + if (bdp->startspaces < 0) { + bdp->startspaces = 0; + } + startcol++; + } + } + } + + if (lnum == end.lnum) { + endcol = end.col; + if (virtual_op) { + getvcol(curwin, &end, &cs, NULL, &ce); + if (p[endcol] == NUL || (cs + end.coladd < ce + // Don't add space for double-wide + // char; endcol will be on last byte + // of multi-byte char. + && utf_head_off(p, p + endcol) == 0)) { + if (start.lnum == end.lnum && start.col == end.col) { + // Special case: inside a single char + is_oneChar = true; + bdp->startspaces = end.coladd - start.coladd + inclusive; + endcol = startcol; + } else { + bdp->endspaces = end.coladd + inclusive; + endcol -= inclusive; + } + } + } + } + if (endcol == MAXCOL) { + endcol = (colnr_T)strlen(p); + } + if (startcol > endcol || is_oneChar) { + bdp->textlen = 0; + } else { + bdp->textlen = endcol - startcol + inclusive; + } + bdp->textstart = p + startcol; +} + /// Handle the add/subtract operator. /// /// @param[in] oap Arguments of operator. @@ -4374,7 +4737,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) length = oap->end.col - pos.col + 1; } } - int one_change = do_addsub(oap->op_type, &pos, length, amount); + bool one_change = do_addsub(oap->op_type, &pos, length, amount); if (one_change) { // Remember the start position of the first change. if (change_cnt == 0) { @@ -4419,7 +4782,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) /// @param Prenum1 Amount of addition or subtraction. /// /// @return true if some character was changed. -int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) +bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) { char *buf1 = NULL; char buf2[NUMBUFLEN]; @@ -4575,7 +4938,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) curwin->w_cursor.col = col; startpos = curwin->w_cursor; did_change = true; - (void)del_char(false); + del_char(false); ins_char(firstdigit); endpos = curwin->w_cursor; curwin->w_cursor.col = col; @@ -4630,13 +4993,13 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) if (!pre) { if (subtract) { if (n > oldn) { - n = 1 + (n ^ (uvarnumber_T) - 1); + n = 1 + (n ^ (uvarnumber_T)(-1)); negative ^= true; } } else { // add if (n < oldn) { - n = (n ^ (uvarnumber_T) - 1); + n = (n ^ (uvarnumber_T)(-1)); negative ^= true; } } @@ -4683,7 +5046,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) } } // del_char() will mark line needing displaying - (void)del_char(false); + del_char(false); c = gchar_cursor(); } @@ -4894,7 +5257,7 @@ void *get_reg_contents(int regname, int flags) return get_reg_wrap_one_line(xstrdup(retval), flags); } - yankreg_T *reg = get_yank_register(regname, YREG_PASTE); + yankreg_T *reg = get_yank_register(regname, YREG_PUT); if (reg->y_array == NULL) { return NULL; } @@ -4957,6 +5320,10 @@ static yankreg_T *init_write_reg(int name, yankreg_T **old_y_previous, bool must static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous) { + if (get_userreg(name) != -1) { + eval_yank_userreg(curbuf->b_p_urf, name, reg); + } + // Send text of clipboard register to the clipboard. set_clipboard(name, reg); @@ -5224,16 +5591,16 @@ static varnumber_T line_count_info(char *line, varnumber_T *wc, varnumber_T *cc, varnumber_T i; varnumber_T words = 0; varnumber_T chars = 0; - int is_word = 0; + bool is_word = false; for (i = 0; i < limit && line[i] != NUL;) { if (is_word) { if (ascii_isspace(line[i])) { words++; - is_word = 0; + is_word = false; } } else if (!ascii_isspace(line[i])) { - is_word = 1; + is_word = true; } chars++; i += utfc_ptr2len(line + i); @@ -5521,7 +5888,7 @@ static void op_colon(oparg_T *oap) // When using !! on a closed fold the range ".!" works best to operate // on, it will be made the whole closed fold later. linenr_T endOfStartFold = oap->start.lnum; - (void)hasFolding(oap->start.lnum, NULL, &endOfStartFold); + hasFolding(oap->start.lnum, NULL, &endOfStartFold); if (oap->end.lnum != oap->start.lnum && oap->end.lnum != endOfStartFold) { // Make it a range with the end line. stuffcharReadbuff(','); @@ -5740,7 +6107,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; // Avoid a problem with unwanted linebreaks in block mode - (void)reset_lbr(); + reset_lbr(); oap->is_VIsual = VIsual_active; if (oap->motion_force == 'V') { oap->motion_type = kMTLineWise; @@ -6126,7 +6493,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) vim_beep(BO_OPER); CancelRedo(); } else { - (void)op_delete(oap); + op_delete(oap); // save cursor line for undo if it wasn't saved yet if (oap->motion_type == kMTLineWise && has_format_option(FO_AUTO) @@ -6145,7 +6512,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { restore_lbr(lbr_saved); oap->excl_tr_ws = cap->cmdchar == 'z'; - (void)op_yank(oap, !gui_yank); + op_yank(oap, !gui_yank); } check_cursor_col(); break; @@ -6276,7 +6643,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) op_insert(oap, cap->count1); // Reset linebreak, so that formatting works correctly. - (void)reset_lbr(); + reset_lbr(); // TODO(brammool): when inserting in several lines, should format all // the lines. @@ -6350,7 +6717,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT || oap->op_type == OP_DELETE)) { - (void)reset_lbr(); + reset_lbr(); coladvance(curwin->w_curswant = old_col); } } else { @@ -6409,7 +6776,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) } if (explicit_cb_reg) { - target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER]; + target = get_global_reg(*name == '*' ? STAR_REGISTER : PLUS_REGISTER); if (writing && (cb_flags & (*name == '*' ? CB_UNNAMED : CB_UNNAMEDPLUS))) { clipboard_needs_update = false; } @@ -6426,10 +6793,10 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) if (cb_flags & CB_UNNAMEDPLUS) { *name = (cb_flags & CB_UNNAMED && writing) ? '"' : '+'; - target = &y_regs[PLUS_REGISTER]; + target = get_global_reg(PLUS_REGISTER); } else { *name = '*'; - target = &y_regs[STAR_REGISTER]; + target = get_global_reg(STAR_REGISTER); } goto end; } @@ -6480,8 +6847,6 @@ bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines) } } - reg->y_array = xcalloc(lines, sizeof(uint8_t *)); - reg->y_size = lines; reg->additional_data = NULL; reg->timestamp = 0; return true; @@ -6494,7 +6859,6 @@ void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust) // but otherwise there is no line after the final newline if (reg->y_type != kMTCharWise) { if (reg->y_type == kMTUnknown || clipboard_adjust) { - xfree(reg->y_array[reg->y_size - 1]); reg->y_size--; } if (reg->y_type == kMTUnknown) { @@ -6680,7 +7044,7 @@ static void set_clipboard(int name, yankreg_T *reg) tv_list_append_string(args, ®type, 1); tv_list_append_string(args, ((char[]) { (char)name }), 1); - (void)eval_call_provider("clipboard", "set", args, true); + eval_call_provider("clipboard", "set", args, true); } /// Avoid slow things (clipboard) during batch operations (while/for-loops). @@ -6745,11 +7109,11 @@ static inline bool reg_empty(const yankreg_T *const reg) /// Iterate over global registers. /// /// @see op_register_iter -const void *op_global_reg_iter(const void *const iter, char *const name, yankreg_T *const reg, - bool *is_unnamed) +iter_register_T op_global_reg_iter(iter_register_T iter, char *const name, + yankreg_T *const reg, bool *is_unnamed) FUNC_ATTR_NONNULL_ARG(2, 3, 4) FUNC_ATTR_WARN_UNUSED_RESULT { - return op_reg_iter(iter, y_regs, name, reg, is_unnamed); + return op_reg_iter(iter, &y_regs, name, reg, is_unnamed); } /// Iterate over registers `regs`. @@ -6761,30 +7125,33 @@ const void *op_global_reg_iter(const void *const iter, char *const name, yankreg /// /// @return Pointer that must be passed to next `op_register_iter` call or /// NULL if iteration is over. -const void *op_reg_iter(const void *const iter, const yankreg_T *const regs, char *const name, - yankreg_T *const reg, bool *is_unnamed) +iter_register_T op_reg_iter(iter_register_T iter, yank_registers_T *regs, + char *const name, yankreg_T *const reg, + bool *is_unnamed) FUNC_ATTR_NONNULL_ARG(3, 4, 5) FUNC_ATTR_WARN_UNUSED_RESULT { *name = NUL; - const yankreg_T *iter_reg = (iter == NULL - ? &(regs[0]) - : (const yankreg_T *const)iter); - while (iter_reg - &(regs[0]) < NUM_SAVED_REGISTERS && reg_empty(iter_reg)) { - iter_reg++; + int iter_idx = (int)(iter == ITER_REGISTER_NULL ? 0 : iter - 1); + + while (iter_idx < NUM_SAVED_REGISTERS && reg_empty(get_reg(regs, iter_idx))) { + ++iter_idx; } - if (iter_reg - &(regs[0]) == NUM_SAVED_REGISTERS || reg_empty(iter_reg)) { - return NULL; + + if (iter_idx >= NUM_SAVED_REGISTERS || reg_empty(get_reg(regs, iter_idx))) { + return ITER_REGISTER_NULL; } - int iter_off = (int)(iter_reg - &(regs[0])); - *name = (char)get_register_name(iter_off); - *reg = *iter_reg; - *is_unnamed = (iter_reg == y_previous); - while (++iter_reg - &(regs[0]) < NUM_SAVED_REGISTERS) { - if (!reg_empty(iter_reg)) { - return (void *)iter_reg; + + *reg = *get_reg(regs, iter_idx); + *name = (char) get_register_name(iter_idx); + *is_unnamed = (get_reg(regs, iter_idx) == y_previous); + + while (++iter_idx < NUM_SAVED_REGISTERS) { + if (!reg_empty(get_reg(regs, iter_idx))) { + return iter_idx + 1; } } - return NULL; + + return ITER_REGISTER_NULL; } /// Get a number of non-empty registers @@ -6792,8 +7159,8 @@ size_t op_reg_amount(void) FUNC_ATTR_WARN_UNUSED_RESULT { size_t ret = 0; - for (size_t i = 0; i < NUM_SAVED_REGISTERS; i++) { - if (!reg_empty(y_regs + i)) { + for (int i = 0; i < NUM_SAVED_REGISTERS; i++) { + if (!reg_empty(get_global_reg(i))) { ret++; } } @@ -6813,11 +7180,11 @@ bool op_reg_set(const char name, const yankreg_T reg, bool is_unnamed) if (i == -1) { return false; } - free_register(&y_regs[i]); - y_regs[i] = reg; + free_register(get_global_reg(i)); + *get_global_reg(i) = reg; if (is_unnamed) { - y_previous = &y_regs[i]; + y_previous = get_global_reg(i); } return true; } @@ -6833,7 +7200,7 @@ const yankreg_T *op_reg_get(const char name) if (i == -1) { return NULL; } - return &y_regs[i]; + return get_global_reg(i); } /// Set the previous yank register @@ -6848,7 +7215,7 @@ bool op_reg_set_previous(const char name) return false; } - y_previous = &y_regs[i]; + y_previous = get_global_reg(i); return true; } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 67a613cbca..643d2a2deb 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -15,10 +15,30 @@ #include "nvim/pos_defs.h" #include "nvim/types_defs.h" +/// structure used by block_prep, op_delete and op_yank for blockwise operators +/// also op_change, op_shift, op_insert, op_replace - AKelly +struct block_def { + int startspaces; ///< 'extra' cols before first char + int endspaces; ///< 'extra' cols after last char + int textlen; ///< chars in block + char *textstart; ///< pointer to 1st char (partially) in block + colnr_T textcol; ///< index of chars (partially) in block + colnr_T start_vcol; ///< start col of 1st char wholly inside block + colnr_T end_vcol; ///< start col of 1st char wholly after block + int is_short; ///< true if line is too short to fit in block + int is_MAX; ///< true if curswant==MAXCOL when starting + int is_oneChar; ///< true if block within one character + int pre_whitesp; ///< screen cols of ws before block + int pre_whitesp_c; ///< chars of ws before block + colnr_T end_char_vcols; ///< number of vcols of post-block char + colnr_T start_char_vcols; ///< number of vcols of pre-block char +}; + typedef int (*Indenter)(void); /// flags for do_put() enum { + ITER_REGISTER_NULL = 0, PUT_FIXINDENT = 1, ///< make indent look nice PUT_CURSEND = 2, ///< leave cursor after end of new text PUT_CURSLINE = 4, ///< leave cursor on last line of new text @@ -42,6 +62,7 @@ enum { STAR_REGISTER = 37, PLUS_REGISTER = 38, NUM_REGISTERS = 39, + USER_REGISTERS_START = 39 }; /// Operator IDs; The order must correspond to opchars[] in ops.c! @@ -86,7 +107,7 @@ enum GRegFlags { }; /// Definition of one register -typedef struct yankreg { +typedef struct { char **y_array; ///< Pointer to an array of line pointers. size_t y_size; ///< Number of lines in y_array. MotionType y_type; ///< Register type @@ -101,6 +122,11 @@ typedef enum { YREG_YANK, YREG_PUT, } yreg_mode_t; +/// Returns a reference to a user-defined register. +int get_userreg(int regname); + +static inline int op_reg_index(int regname) + REAL_FATTR_CONST; /// Convert register name into register index /// @@ -108,7 +134,6 @@ typedef enum { /// /// @return Index in y_regs array or -1 if register name was not recognized. static inline int op_reg_index(const int regname) - FUNC_ATTR_CONST { if (ascii_isdigit(regname)) { return regname - '0'; @@ -123,15 +148,21 @@ static inline int op_reg_index(const int regname) } else if (regname == '+') { return PLUS_REGISTER; } else { - return -1; + return get_userreg(regname); } } +struct yank_registers; +typedef struct yank_registers yank_registers_T; + +typedef size_t iter_register_T; + +static inline bool is_literal_register(int regname) + REAL_FATTR_CONST; /// @see get_yank_register /// @return true when register should be inserted literally /// (selection or clipboard) static inline bool is_literal_register(const int regname) - FUNC_ATTR_CONST { return regname == '*' || regname == '+'; } diff --git a/src/nvim/option.c b/src/nvim/option.c index 96d6d8e01e..d5df8385f8 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1,8 +1,7 @@ // User-settable options. Checklist for adding a new option: // - Put it in options.lua -// - For a global option: Add a variable for it in option_defs.h. +// - For a global option: Add a variable for it in option_vars.h. // - For a buffer or window local option: -// - Add a BV_XX or WV_XX entry to option_defs.h // - Add a variable to the window or buffer struct in buffer_defs.h. // - For a window option, add some code to copy_winopt(). // - For a window string option, add code to check_winopt() @@ -12,7 +11,7 @@ // - For a buffer string option, add code to check_buf_options(). // - If it's a numeric option, add any necessary bounds checks to check_num_option_bounds(). // - If it's a list of flags, add some code in do_set(), search for WW_ALL. -// - Add documentation! doc/options.txt, and any other related places. +// - Add documentation! "desc" in options.lua, and any other related places. // - Add an entry in runtime/optwin.vim. #define IN_OPTION_C @@ -31,29 +30,35 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/ascii_defs.h" +#include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cmdexpand_defs.h" #include "nvim/cursor_shape.h" +#include "nvim/decoration_defs.h" #include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/vars.h" +#include "nvim/eval/window.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/ex_session.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" @@ -63,6 +68,7 @@ #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" #include "nvim/mapping.h" +#include "nvim/math.h" #include "nvim/mbyte.h" #include "nvim/memfile.h" #include "nvim/memline.h" @@ -79,13 +85,14 @@ #include "nvim/os/input.h" #include "nvim/os/lang.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/search.h" -#include "nvim/sign_defs.h" #include "nvim/spell.h" #include "nvim/spellfile.h" #include "nvim/spellsuggest.h" @@ -95,7 +102,9 @@ #include "nvim/terminal.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" +#include "nvim/undo_defs.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -109,8 +118,6 @@ static const char e_not_allowed_in_modeline[] = N_("E520: Not allowed in a modeline"); static const char e_not_allowed_in_modeline_when_modelineexpr_is_off[] = N_("E992: Not allowed in a modeline when 'modelineexpr' is off"); -static const char e_key_code_not_set[] - = N_("E846: Key code not set"); static const char e_number_required_after_equal[] = N_("E521: Number required after ="); static const char e_preview_window_already_exists[] @@ -146,30 +153,28 @@ typedef enum { # include "option.c.generated.h" #endif -// options[] is initialized here. -// The order of the options MUST be alphabetic for ":set all" and findoption(). -// All option names MUST start with a lowercase letter (for findoption()). -// Exception: "t_" options are at the end. +// options[] is initialized in options.generated.h. // The options with a NULL variable are 'hidden': a set command for them is // ignored and they are not printed. #ifdef INCLUDE_GENERATED_DECLARATIONS # include "options.generated.h" +# include "options_map.generated.h" #endif -static char *(p_bin_dep_opts[]) = { - "textwidth", "wrapmargin", "modeline", "expandtab", NULL +static int p_bin_dep_opts[] = { + kOptTextwidth, kOptWrapmargin, kOptModeline, kOptExpandtab, kOptInvalid }; -static char *(p_paste_dep_opts[]) = { - "autoindent", "expandtab", "ruler", "showmatch", "smarttab", - "softtabstop", "textwidth", "wrapmargin", "revins", "varsofttabstop", NULL + +static int p_paste_dep_opts[] = { + kOptAutoindent, kOptExpandtab, kOptRuler, kOptShowmatch, kOptSmarttab, kOptSofttabstop, + kOptTextwidth, kOptWrapmargin, kOptRevins, kOptVarsofttabstop, kOptInvalid }; void set_init_tablocal(void) { // susy baka: cmdheight calls itself OPT_GLOBAL but is really tablocal! - int ch_idx = findoption("cmdheight"); - p_ch = (OptInt)(intptr_t)options[ch_idx].def_val; + p_ch = options[kOptCmdheight].def_val.number; } /// Initialize the 'shell' option to a default value. @@ -183,9 +188,9 @@ static void set_init_default_shell(void) const size_t len = strlen(shell) + 3; // two quotes and a trailing NUL char *const cmd = xmalloc(len); snprintf(cmd, len, "\"%s\"", shell); - set_string_default("sh", cmd, true); + set_string_default(kOptShell, cmd, true); } else { - set_string_default("sh", (char *)shell, false); + set_string_default(kOptShell, (char *)shell, false); } } } @@ -200,7 +205,7 @@ static void set_init_default_backupskip(void) static char *(names[3]) = { "TMPDIR", "TEMP", "TMP" }; #endif garray_T ga; - int opt_idx = findoption("backupskip"); + OptIndex opt_idx = kOptBackupskip; ga_init(&ga, 1, 100); for (size_t n = 0; n < ARRAY_SIZE(names); n++) { @@ -214,7 +219,7 @@ static void set_init_default_backupskip(void) p = "/tmp"; # endif mustfree = false; - } else // NOLINT(readability/braces) + } else #endif { p = vim_getenv(names[n]); @@ -244,7 +249,7 @@ static void set_init_default_backupskip(void) } } if (ga.ga_data != NULL) { - set_string_default("bsk", ga.ga_data, true); + set_string_default(kOptBackupskip, ga.ga_data, true); } } @@ -270,13 +275,8 @@ static void set_init_default_cdpath(void) } } buf[j] = NUL; - int opt_idx = findoption("cdpath"); - if (opt_idx >= 0) { - options[opt_idx].def_val = buf; - options[opt_idx].flags |= P_DEF_ALLOCED; - } else { - xfree(buf); // cannot happen - } + options[kOptCdpath].def_val.string = buf; + options[kOptCdpath].flags |= P_DEF_ALLOCED; xfree(cdpath); } @@ -289,7 +289,7 @@ static void set_init_default_cdpath(void) /// default. static void set_init_expand_env(void) { - for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { vimoption_T *opt = &options[opt_idx]; if (opt->flags & P_NO_DEF_EXP) { continue; @@ -304,9 +304,9 @@ static void set_init_expand_env(void) p = xstrdup(p); *(char **)opt->var = p; if (opt->flags & P_DEF_ALLOCED) { - xfree(opt->def_val); + xfree(opt->def_val.string); } - opt->def_val = p; + opt->def_val.string = p; opt->flags |= P_DEF_ALLOCED; } } @@ -347,20 +347,20 @@ void set_init_1(bool clean_arg) backupdir = xrealloc(backupdir, backupdir_len + 3); memmove(backupdir + 2, backupdir, backupdir_len + 1); memmove(backupdir, ".,", 2); - set_string_default("backupdir", backupdir, true); - set_string_default("viewdir", stdpaths_user_state_subpath("view", 2, true), + set_string_default(kOptBackupdir, backupdir, true); + set_string_default(kOptViewdir, stdpaths_user_state_subpath("view", 2, true), true); - set_string_default("directory", stdpaths_user_state_subpath("swap", 2, true), + set_string_default(kOptDirectory, stdpaths_user_state_subpath("swap", 2, true), true); - set_string_default("undodir", stdpaths_user_state_subpath("undo", 2, true), + set_string_default(kOptUndodir, stdpaths_user_state_subpath("undo", 2, true), true); // Set default for &runtimepath. All necessary expansions are performed in // this function. char *rtp = runtimepath_default(clean_arg); if (rtp) { - set_string_default("runtimepath", rtp, true); + set_string_default(kOptRuntimepath, rtp, true); // Make a copy of 'rtp' for 'packpath' - set_string_default("packpath", rtp, false); + set_string_default(kOptPackpath, rtp, false); rtp = NULL; // ownership taken } @@ -375,9 +375,6 @@ void set_init_1(bool clean_arg) check_win_options(curwin); check_options(); - // Set all options to their default value - set_options_default(OPT_FREE); - // set 'laststatus' last_status(false); @@ -399,7 +396,7 @@ void set_init_1(bool clean_arg) // NOTE: mlterm's author is being asked to 'set' a variable // instead of an environment variable due to inheritance. if (os_env_exists("MLTERM")) { - set_option_value_give_err("tbidi", BOOLEAN_OPTVAL(true), 0); + set_option_value_give_err(kOptTermbidi, BOOLEAN_OPTVAL(true), 0); } didset_options2(); @@ -420,33 +417,35 @@ void set_init_1(bool clean_arg) /// Set an option to its default value. /// This does not take care of side effects! /// -/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL -static void set_option_default(const int opt_idx, int opt_flags) +/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL. +/// +/// TODO(famiu): Refactor this when def_val uses OptVal. +static void set_option_default(const OptIndex opt_idx, int opt_flags) { - int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; + bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; // pointer to variable for current option vimoption_T *opt = &options[opt_idx]; void *varp = get_varp_scope(opt, both ? OPT_LOCAL : opt_flags); uint32_t flags = opt->flags; if (varp != NULL) { // skip hidden option, nothing to do for it - if (flags & P_STRING) { - // Use set_string_option_direct() for local options to handle - // freeing and allocating the value. + if (option_has_type(opt_idx, kOptValTypeString)) { + // Use set_string_option_direct() for local options to handle freeing and allocating the + // value. if (opt->indir != PV_NONE) { - set_string_option_direct(NULL, opt_idx, opt->def_val, opt_flags, 0); + set_string_option_direct(opt_idx, opt->def_val.string, opt_flags, 0); } else { - if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) { + if (flags & P_ALLOCED) { free_string_option(*(char **)(varp)); } - *(char **)varp = opt->def_val; + *(char **)varp = opt->def_val.string; opt->flags &= ~P_ALLOCED; } - } else if (flags & P_NUM) { + } else if (option_has_type(opt_idx, kOptValTypeNumber)) { if (opt->indir == PV_SCROLL) { win_comp_scroll(curwin); } else { - OptInt def_val = (OptInt)(intptr_t)opt->def_val; + OptInt def_val = opt->def_val.number; if ((OptInt *)varp == &curwin->w_p_so || (OptInt *)varp == &curwin->w_p_siso) { // 'scrolloff' and 'sidescrolloff' local values have a @@ -460,8 +459,8 @@ static void set_option_default(const int opt_idx, int opt_flags) *(OptInt *)get_varp_scope(opt, OPT_GLOBAL) = def_val; } } - } else { // P_BOOL - *(int *)varp = (int)(intptr_t)opt->def_val; + } else { // boolean + *(int *)varp = opt->def_val.boolean; #ifdef UNIX // 'modeline' defaults to off for root if (opt->indir == PV_ML && getuid() == ROOT_UID) { @@ -480,17 +479,17 @@ static void set_option_default(const int opt_idx, int opt_flags) *flagsp = *flagsp & ~P_INSECURE; } - set_option_sctx_idx(opt_idx, opt_flags, current_sctx); + set_option_sctx(opt_idx, opt_flags, current_sctx); } /// Set all options (except terminal options) to their default value. /// -/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL +/// @param opt_flags Option flags. static void set_options_default(int opt_flags) { - for (int i = 0; options[i].fullname; i++) { - if (!(options[i].flags & P_NODEFAULT)) { - set_option_default(i, opt_flags); + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + if (!(options[opt_idx].flags & P_NODEFAULT)) { + set_option_default(opt_idx, opt_flags); } } @@ -505,22 +504,23 @@ static void set_options_default(int opt_flags) /// Set the Vi-default value of a string option. /// Used for 'sh', 'backupskip' and 'term'. /// -/// @param name The name of the option -/// @param val The value of the option -/// @param allocated If true, do not copy default as it was already allocated. -static void set_string_default(const char *name, char *val, bool allocated) +/// @param opt_idx Option index in options[] table. +/// @param val The value of the option. +/// @param allocated If true, do not copy default as it was already allocated. +static void set_string_default(OptIndex opt_idx, char *val, bool allocated) FUNC_ATTR_NONNULL_ALL { - int opt_idx = findoption(name); - if (opt_idx >= 0) { - vimoption_T *opt = &options[opt_idx]; - if (opt->flags & P_DEF_ALLOCED) { - xfree(opt->def_val); - } + if (opt_idx == kOptInvalid) { + return; + } - opt->def_val = allocated ? val : xstrdup(val); - opt->flags |= P_DEF_ALLOCED; + vimoption_T *opt = &options[opt_idx]; + if (opt->flags & P_DEF_ALLOCED) { + xfree(opt->def_val.string); } + + opt->def_val.string = allocated ? val : xstrdup(val); + opt->flags |= P_DEF_ALLOCED; } /// For an option value that contains comma separated items, find "newval" in @@ -556,11 +556,10 @@ static char *find_dup_item(char *origval, const char *newval, uint32_t flags) /// Set the Vi-default value of a number option. /// Used for 'lines' and 'columns'. -void set_number_default(char *name, OptInt val) +void set_number_default(OptIndex opt_idx, OptInt val) { - int opt_idx = findoption(name); - if (opt_idx >= 0) { - options[opt_idx].def_val = (void *)(intptr_t)val; + if (opt_idx != kOptInvalid) { + options[opt_idx].def_val.number = val; } } @@ -568,22 +567,25 @@ void set_number_default(char *name, OptInt val) /// Free all options. void free_all_options(void) { - for (int i = 0; options[i].fullname; i++) { - if (options[i].indir == PV_NONE) { + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + if (options[opt_idx].indir == PV_NONE) { // global option: free value and default value. - if ((options[i].flags & P_ALLOCED) && options[i].var != NULL) { - optval_free(optval_from_varp(i, options[i].var)); + if ((options[opt_idx].flags & P_ALLOCED) && options[opt_idx].var != NULL) { + optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); } - if (options[i].flags & P_DEF_ALLOCED) { - optval_free(optval_from_varp(i, &options[i].def_val)); + if (options[opt_idx].flags & P_DEF_ALLOCED) { + optval_free(optval_from_varp(opt_idx, &options[opt_idx].def_val)); } - } else if (options[i].var != VAR_WIN) { + } else if (options[opt_idx].var != VAR_WIN) { // buffer-local option: free global value - optval_free(optval_from_varp(i, options[i].var)); + optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); } } free_operatorfunc_option(); free_tagfunc_option(); + XFREE_CLEAR(fenc_default); + XFREE_CLEAR(p_term); + XFREE_CLEAR(p_ttytype); } #endif @@ -595,18 +597,17 @@ void set_init_2(bool headless) // 'scroll' defaults to half the window height. The stored default is zero, // which results in the actual value computed from the window height. - int idx = findoption("scroll"); - if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { - set_option_default(idx, OPT_LOCAL); + if (!(options[kOptScroll].flags & P_WAS_SET)) { + set_option_default(kOptScroll, OPT_LOCAL); } comp_col(); // 'window' is only for backwards compatibility with Vi. // Default is Rows - 1. - if (!option_was_set("window")) { + if (!option_was_set(kOptWindow)) { p_window = Rows - 1; } - set_number_default("window", Rows - 1); + set_number_default(kOptWindow, Rows - 1); } /// Initialize the options, part three: After reading the .vimrc @@ -617,14 +618,8 @@ void set_init_3(void) // Set 'shellpipe' and 'shellredir', depending on the 'shell' option. // This is done after other initializations, where 'shell' might have been // set, but only if they have not been set before. - int idx_srr = findoption("srr"); - int do_srr = (idx_srr < 0) - ? false - : !(options[idx_srr].flags & P_WAS_SET); - int idx_sp = findoption("sp"); - int do_sp = (idx_sp < 0) - ? false - : !(options[idx_sp].flags & P_WAS_SET); + bool do_srr = !(options[kOptShellredir].flags & P_WAS_SET); + bool do_sp = !(options[kOptShellpipe].flags & P_WAS_SET); size_t len = 0; char *p = (char *)invocation_path_tail(p_sh, &len); @@ -639,11 +634,11 @@ void set_init_3(void) || path_fnamecmp(p, "tcsh") == 0) { if (do_sp) { p_sp = "|& tee"; - options[idx_sp].def_val = p_sp; + options[kOptShellpipe].def_val.string = p_sp; } if (do_srr) { p_srr = ">&"; - options[idx_srr].def_val = p_srr; + options[kOptShellredir].def_val.string = p_srr; } } else if (path_fnamecmp(p, "sh") == 0 || path_fnamecmp(p, "ksh") == 0 @@ -658,18 +653,18 @@ void set_init_3(void) // Always use POSIX shell style redirection if we reach this if (do_sp) { p_sp = "2>&1| tee"; - options[idx_sp].def_val = p_sp; + options[kOptShellpipe].def_val.string = p_sp; } if (do_srr) { p_srr = ">%s 2>&1"; - options[idx_srr].def_val = p_srr; + options[kOptShellredir].def_val.string = p_srr; } } xfree(p); } if (buf_is_empty(curbuf)) { - int idx_ffs = findoption_len(S_LEN("ffs")); + int idx_ffs = find_option("ffs"); // Apply the first entry of 'fileformats' to the initial buffer. if (idx_ffs >= 0 && (options[idx_ffs].flags & P_WAS_SET)) { @@ -692,12 +687,11 @@ void set_helplang_default(const char *lang) if (lang_len < 2) { // safety check return; } - int idx = findoption("hlg"); - if (idx < 0 || (options[idx].flags & P_WAS_SET)) { + if (options[kOptHelplang].flags & P_WAS_SET) { return; } - if (options[idx].flags & P_ALLOCED) { + if (options[kOptHelplang].flags & P_ALLOCED) { free_string_option(p_hlg); } p_hlg = xmemdupz(lang, lang_len); @@ -711,7 +705,7 @@ void set_helplang_default(const char *lang) p_hlg[1] = 'n'; } p_hlg[2] = NUL; - options[idx].flags |= P_ALLOCED; + options[kOptHelplang].flags |= P_ALLOCED; } /// 'title' and 'icon' only default to true if they have not been set or reset @@ -724,15 +718,13 @@ void set_title_defaults(void) // If GUI is (going to be) used, we can always set the window title and // icon name. Saves a bit of time, because the X11 display server does // not need to be contacted. - int idx1 = findoption("title"); - if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { - options[idx1].def_val = 0; - p_title = 0; + if (!(options[kOptTitle].flags & P_WAS_SET)) { + options[kOptTitle].def_val.boolean = false; + p_title = false; } - idx1 = findoption("icon"); - if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { - options[idx1].def_val = 0; - p_icon = 0; + if (!(options[kOptIcon].flags & P_WAS_SET)) { + options[kOptIcon].def_val.boolean = false; + p_icon = false; } } @@ -748,13 +740,13 @@ void ex_set(exarg_T *eap) if (eap->forceit) { flags |= OPT_ONECOLUMN; } - (void)do_set(eap->arg, flags); + do_set(eap->arg, flags); } /// Get the default value for a string option. -static char *stropt_get_default_val(int opt_idx, uint64_t flags) +static char *stropt_get_default_val(OptIndex opt_idx, uint64_t flags) { - char *newval = options[opt_idx].def_val; + char *newval = options[opt_idx].def_val.string; // expand environment variables and ~ since the default value was // already expanded, only required when an environment variable was set // later @@ -773,8 +765,7 @@ static char *stropt_get_default_val(int opt_idx, uint64_t flags) } /// Copy the new string value into allocated memory for the option. -/// Can't use set_string_option_direct(), because we need to remove the -/// backslashes. +/// Can't use set_string_option_direct(), because we need to remove the backslashes. static char *stropt_copy_value(char *origval, char **argp, set_op_T op, uint32_t flags FUNC_ATTR_UNUSED) { @@ -822,7 +813,7 @@ static char *stropt_copy_value(char *origval, char **argp, set_op_T op, } /// Expand environment variables and ~ in string option value 'newval'. -static char *stropt_expand_envvar(int opt_idx, char *origval, char *newval, set_op_T op) +static char *stropt_expand_envvar(OptIndex opt_idx, char *origval, char *newval, set_op_T op) { char *s = option_expand(opt_idx, newval); if (s == NULL) { @@ -922,8 +913,8 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags) /// set {opt}< /// set {opt}={val} /// set {opt}:{val} -static char *stropt_get_newval(int nextchar, int opt_idx, char **argp, void *varp, char *origval, - set_op_T *op_arg, uint32_t flags) +static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void *varp, + char *origval, set_op_T *op_arg, uint32_t flags) { char *arg = *argp; set_op_T op = *op_arg; @@ -1024,81 +1015,23 @@ static set_prefix_T get_option_prefix(char **argp) return PREFIX_NONE; } -/// @param[in] arg Pointer to start option name -/// @param[out] opt_idxp Option index in options[] table. -/// @param[out] keyp -/// @param[out] len Length of option name -/// @return FAIL if an error is detected, OK otherwise -static int parse_option_name(char *arg, int *keyp, int *lenp, int *opt_idxp) -{ - // find end of name - int key = 0; - int len; - int opt_idx; - - if (*arg == '<') { - opt_idx = -1; - // look out for <t_>;> - if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4]) { - len = 5; - } else { - len = 1; - while (arg[len] != NUL && arg[len] != '>') { - len++; - } - } - if (arg[len] != '>') { - return FAIL; - } - if (arg[1] == 't' && arg[2] == '_') { // could be term code - opt_idx = findoption_len(arg + 1, (size_t)(len - 1)); - } - len++; - if (opt_idx == -1) { - key = find_key_option(arg + 1, true); - } - } else { - // The two characters after "t_" may not be alphanumeric. - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) { - len = 4; - } else { - len = 0; - while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') { - len++; - } - } - opt_idx = findoption_len(arg, (size_t)len); - if (opt_idx == -1) { - key = find_key_option(arg, false); - } - } - - *keyp = key; - *lenp = len; - *opt_idxp = opt_idx; - - return OK; -} - -static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t flags, +static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_t flags, set_prefix_T prefix, const char **errmsg) { // Only bools can have a prefix of 'inv' or 'no' - if (!(flags & P_BOOL) && prefix != PREFIX_NONE) { + if (!option_has_type(opt_idx, kOptValTypeBoolean) && prefix != PREFIX_NONE) { *errmsg = e_invarg; return FAIL; } // Skip all options that are not window-local (used when showing // an already loaded buffer in a window). - if ((opt_flags & OPT_WINONLY) - && (opt_idx < 0 || options[opt_idx].var != VAR_WIN)) { + if ((opt_flags & OPT_WINONLY) && (opt_idx == kOptInvalid || options[opt_idx].var != VAR_WIN)) { return FAIL; } // Skip all options that are window-local (used for :vimgrep). - if ((opt_flags & OPT_NOWIN) && opt_idx >= 0 - && options[opt_idx].var == VAR_WIN) { + if ((opt_flags & OPT_NOWIN) && opt_idx != kOptInvalid && options[opt_idx].var == VAR_WIN) { return FAIL; } @@ -1116,7 +1049,7 @@ static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t fla // 'foldmethod' becomes "marker" instead of "diff" and that // "wrap" gets set. if (win->w_p_diff - && opt_idx >= 0 // shut up coverity warning + && opt_idx != kOptInvalid // shut up coverity warning && (options[opt_idx].indir == PV_FDM || options[opt_idx].indir == PV_WRAP)) { return FAIL; @@ -1132,8 +1065,79 @@ static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t fla return OK; } +/// Skip over the name of a TTY option or keycode option. +/// +/// @param[in] arg Start of TTY or keycode option name. +/// +/// @return NULL when option isn't a TTY or keycode option. Otherwise pointer to the char after the +/// option name. +static const char *find_tty_option_end(const char *arg) +{ + if (strequal(arg, "term")) { + return arg + sizeof("term") - 1; + } else if (strequal(arg, "ttytype")) { + return arg + sizeof("ttytype") - 1; + } + + const char *p = arg; + bool delimit = false; // whether to delimit < + + if (arg[0] == '<') { + // look out for <t_>;> + delimit = true; + p++; + } + if (p[0] == 't' && p[1] == '_' && p[2] && p[3]) { + p += 4; + } else if (delimit) { + // Search for delimiting >. + while (*p != NUL && *p != '>') { + p++; + } + } + // Return NULL when delimiting > is not found. + if (delimit) { + if (*p != '>') { + return NULL; + } + p++; + } + + return arg == p ? NULL : p; +} + +/// Skip over the name of an option. +/// +/// @param[in] arg Start of option name. +/// @param[out] opt_idxp Set to option index in options[] table. +/// +/// @return NULL when no option name found. Otherwise pointer to the char after the option name. +const char *find_option_end(const char *arg, OptIndex *opt_idxp) +{ + const char *p; + + // Handle TTY and keycode options separately. + if ((p = find_tty_option_end(arg)) != NULL) { + *opt_idxp = kOptInvalid; + return p; + } else { + p = arg; + } + + if (!ASCII_ISALPHA(*p)) { + *opt_idxp = kOptInvalid; + return NULL; + } + while (ASCII_ISALPHA(*p)) { + p++; + } + + *opt_idxp = find_option_len(arg, (size_t)(p - arg)); + return p; +} + /// Get new option value from argp. Allocated OptVal must be freed by caller. -static OptVal get_option_newval(int opt_idx, int opt_flags, set_prefix_T prefix, char **argp, +static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, char **argp, int nextchar, set_op_T op, uint32_t flags, void *varp, char *errbuf, const size_t errbuflen, const char **errmsg) FUNC_ATTR_WARN_UNUSED_RESULT @@ -1169,7 +1173,7 @@ static OptVal get_option_newval(int opt_idx, int opt_flags, set_prefix_T prefix, break; } } else if (nextchar == '&') { - newval_bool = TRISTATE_FROM_INT((int)(intptr_t)options[opt_idx].def_val); + newval_bool = TRISTATE_FROM_INT(options[opt_idx].def_val.boolean); } else if (nextchar == '<') { // For 'autoread', kNone means to use global value. if ((int *)varp == &curbuf->b_p_ar && opt_flags == OPT_LOCAL) { @@ -1203,7 +1207,7 @@ static OptVal get_option_newval(int opt_idx, int opt_flags, set_prefix_T prefix, // other error arg++; if (nextchar == '&') { - newval_num = (OptInt)(intptr_t)options[opt_idx].def_val; + newval_num = options[opt_idx].def_val.number; } else if (nextchar == '<') { if ((OptInt *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) { // for 'undolevels' NO_LOCAL_UNDOLEVEL means using the global newval_num @@ -1271,61 +1275,56 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * char *arg = *argp; // find end of name - int key = 0; - int len; - int opt_idx; - if (parse_option_name(arg, &key, &len, &opt_idx) == FAIL) { - *errmsg = e_invarg; + OptIndex opt_idx; + const char *const option_end = find_option_end(arg, &opt_idx); + + if (opt_idx != kOptInvalid) { + assert(option_end >= arg); + } else if (is_tty_option(arg)) { // Silently ignore TTY options. + return; + } else { // Invalid option name, skip. + *errmsg = e_unknown_option; return; } - // remember character after option name - int afterchar = (uint8_t)arg[len]; + // Remember character after option name. + uint8_t afterchar = (uint8_t)(*option_end); + char *p = (char *)option_end; - // skip white space, allow ":set ai ?" - while (ascii_iswhite(arg[len])) { - len++; + // Skip white space, allow ":set ai ?". + while (ascii_iswhite(*p)) { + p++; } - set_op_T op = get_op(arg + len); + set_op_T op = get_op(p); if (op != OP_NONE) { - len++; - } - - uint8_t nextchar = (uint8_t)arg[len]; // next non-white char after option name - - if (opt_idx == -1 && key == 0) { // found a mismatch: skip - *errmsg = e_unknown_option; - return; + p++; } - uint32_t flags; // flags for current option + uint8_t nextchar = (uint8_t)(*p); // next non-white char after option name + uint32_t flags = 0; // flags for current option void *varp = NULL; // pointer to variable for current option - if (opt_idx >= 0) { - if (options[opt_idx].var == NULL) { // hidden option: skip - // Only give an error message when requesting the value of - // a hidden option, ignore setting it. - if (vim_strchr("=:!&<", nextchar) == NULL - && (!(options[opt_idx].flags & P_BOOL) - || nextchar == '?')) { - *errmsg = e_unsupportedoption; - } - return; + if (options[opt_idx].var == NULL) { // hidden option: skip + // Only give an error message when requesting the value of + // a hidden option, ignore setting it. + if (vim_strchr("=:!&<", nextchar) == NULL + && (!option_has_type(opt_idx, kOptValTypeBoolean) || nextchar == '?')) { + *errmsg = e_unsupportedoption; } - - flags = options[opt_idx].flags; - varp = get_varp_scope(&(options[opt_idx]), opt_flags); - } else { - flags = P_STRING; + return; } + flags = options[opt_idx].flags; + varp = get_varp_scope(&(options[opt_idx]), opt_flags); + if (validate_opt_idx(curwin, opt_idx, opt_flags, flags, prefix, errmsg) == FAIL) { return; } if (vim_strchr("?=:!&<", nextchar) != NULL) { - *argp += len; + *argp = p; + if (nextchar == '&' && (*argp)[1] == 'v' && (*argp)[2] == 'i') { if ((*argp)[3] == 'm') { // "opt&vim": set to Vim default *argp += 3; @@ -1340,14 +1339,10 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * } } - // - // allow '=' and ':' as MS-DOS command.com allows only one - // '=' character per "set" command line. grrr. (jw) - // + // Allow '=' and ':' as MS-DOS command.com allows only one '=' character per "set" command line. if (nextchar == '?' - || (prefix == PREFIX_NONE - && vim_strchr("=:&<", nextchar) == NULL - && !(flags & P_BOOL))) { + || (prefix == PREFIX_NONE && vim_strchr("=:&<", nextchar) == NULL + && !option_has_type(opt_idx, kOptValTypeBoolean))) { // print value if (*did_show) { msg_putchar('\n'); // cursor below last one @@ -1355,29 +1350,26 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * gotocmdline(true); // cursor at status line *did_show = true; // remember that we did a line } - if (opt_idx >= 0) { - showoneopt(&options[opt_idx], opt_flags); - if (p_verbose > 0) { - // Mention where the option was last set. - if (varp == options[opt_idx].var) { - option_last_set_msg(options[opt_idx].last_set); - } else if ((int)options[opt_idx].indir & PV_WIN) { - option_last_set_msg(curwin->w_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); - } else if ((int)options[opt_idx].indir & PV_BUF) { - option_last_set_msg(curbuf->b_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); - } + showoneopt(&options[opt_idx], opt_flags); + + if (p_verbose > 0) { + // Mention where the option was last set. + if (varp == options[opt_idx].var) { + option_last_set_msg(options[opt_idx].last_set); + } else if ((int)options[opt_idx].indir & PV_WIN) { + option_last_set_msg(curwin->w_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); + } else if ((int)options[opt_idx].indir & PV_BUF) { + option_last_set_msg(curbuf->b_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); } - } else { - *errmsg = e_key_code_not_set; - return; } + if (nextchar != '?' && nextchar != NUL && !ascii_iswhite(afterchar)) { *errmsg = e_trailing; } return; } - if (flags & P_BOOL) { + if (option_has_type(opt_idx, kOptValTypeBoolean)) { if (vim_strchr("=:", nextchar) != NULL) { *errmsg = e_invarg; return; @@ -1441,7 +1433,7 @@ int do_set(char *arg, int opt_flags) if (*arg == '&') { arg++; // Only for :set command set global value of local options. - set_options_default(OPT_FREE | opt_flags); + set_options_default(opt_flags); didset_options(); didset_options2(); ui_refresh_options(); @@ -1453,7 +1445,7 @@ int do_set(char *arg, int opt_flags) } else { char *startarg = arg; // remember for error message const char *errmsg = NULL; - char errbuf[80]; + char errbuf[ERR_BUFLEN]; do_one_set_option(opt_flags, &arg, &did_show, errbuf, sizeof(errbuf), &errmsg); @@ -1497,21 +1489,45 @@ int do_set(char *arg, int opt_flags) if (silent_mode && did_show) { // After displaying option values in silent mode. silent_mode = false; - info_message = true; // use os_msg(), not os_errmsg() + info_message = true; // use stdout, not stderr msg_putchar('\n'); silent_mode = true; - info_message = false; // use os_msg(), not os_errmsg() + info_message = false; // use stdout, not stderr } return OK; } +// Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number. +// When "has_lt" is true there is a '<' before "*arg_arg". +// Returns 0 when the key is not recognized. +static int find_key_len(const char *arg_arg, size_t len, bool has_lt) +{ + int key = 0; + const char *arg = arg_arg; + + // Don't use get_special_key_code() for t_xx, we don't want it to call + // add_termcap_entry(). + if (len >= 4 && arg[0] == 't' && arg[1] == '_') { + key = TERMCAP2KEY((uint8_t)arg[2], (uint8_t)arg[3]); + } else if (has_lt) { + arg--; // put arg at the '<' + int modifiers = 0; + key = find_special_key(&arg, len + 1, &modifiers, FSK_KEYCODE | FSK_KEEP_X_KEY | FSK_SIMPLIFY, + NULL); + if (modifiers) { // can't handle modifiers here + key = 0; + } + } + return key; +} + /// Convert a key name or string into a key value. /// Used for 'wildchar' and 'cedit' options. int string_to_key(char *arg) { if (*arg == '<') { - return find_key_option(arg + 1, true); + return find_key_len(arg + 1, strlen(arg), true); } if (*arg == '^') { return CTRL_CHR((uint8_t)arg[1]); @@ -1623,7 +1639,7 @@ char *find_shada_parameter(int type) /// These string options cannot be indirect! /// If "val" is NULL expand the current value of the option. /// Return pointer to NameBuff, or NULL when not expanded. -static char *option_expand(int opt_idx, char *val) +static char *option_expand(OptIndex opt_idx, char *val) { // if option doesn't need expansion nothing to do if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL) { @@ -1660,18 +1676,18 @@ static char *option_expand(int opt_idx, char *val) static void didset_options(void) { // initialize the table for 'iskeyword' et.al. - (void)init_chartab(); + init_chartab(); didset_string_options(); - (void)spell_check_msm(); - (void)spell_check_sps(); - (void)compile_cap_prog(curwin->w_s); - (void)did_set_spell_option(true); + spell_check_msm(); + spell_check_sps(); + compile_cap_prog(curwin->w_s); + did_set_spell_option(true); // set cedit_key - (void)did_set_cedit(NULL); + did_set_cedit(NULL); // initialize the table for 'breakat'. - (void)did_set_breakat(NULL); + did_set_breakat(NULL); didset_window_options(curwin, true); } @@ -1682,49 +1698,49 @@ static void didset_options2(void) highlight_changed(); // Parse default for 'fillchars'. - (void)set_fillchars_option(curwin, curwin->w_p_fcs, true); + set_chars_option(curwin, curwin->w_p_fcs, kFillchars, true, NULL, 0); // Parse default for 'listchars'. - (void)set_listchars_option(curwin, curwin->w_p_lcs, true); + set_chars_option(curwin, curwin->w_p_lcs, kListchars, true, NULL, 0); // Parse default for 'wildmode'. check_opt_wim(); xfree(curbuf->b_p_vsts_array); - (void)tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array); + tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array); xfree(curbuf->b_p_vts_array); - (void)tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array); + tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array); } /// Check for string options that are NULL (normally only termcap options). void check_options(void) { - for (int opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++) { - if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL) { + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + if ((option_has_type(opt_idx, kOptValTypeString)) && options[opt_idx].var != NULL) { check_string_option((char **)get_varp(&(options[opt_idx]))); } } } -/// Return true when option "opt" was set from a modeline or in secure mode. -/// Return false when it wasn't. -/// Return -1 for an unknown option. -int was_set_insecurely(win_T *const wp, char *opt, int opt_flags) +/// Check if option was set insecurely. +/// +/// @param wp Window. +/// @param opt_idx Option index in options[] table. +/// @param opt_flags Option flags. +/// +/// @return True if option was set from a modeline or in secure mode, false if it wasn't. +int was_set_insecurely(win_T *const wp, OptIndex opt_idx, int opt_flags) { - int idx = findoption(opt); + assert(opt_idx != kOptInvalid); - if (idx >= 0) { - uint32_t *flagp = insecure_flag(wp, idx, opt_flags); - return (*flagp & P_INSECURE) != 0; - } - internal_error("was_set_insecurely()"); - return -1; + uint32_t *flagp = insecure_flag(wp, opt_idx, opt_flags); + return (*flagp & P_INSECURE) != 0; } /// Get a pointer to the flags used for the P_INSECURE flag of option /// "opt_idx". For some local options a local flags field is used. /// NOTE: Caller must make sure that "wp" is set to the window from which /// the option is used. -uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags) +uint32_t *insecure_flag(win_T *const wp, OptIndex opt_idx, int opt_flags) { if (opt_flags & OPT_LOCAL) { assert(wp != NULL); @@ -1774,7 +1790,7 @@ bool valid_name(const char *val, const char *allowed) void check_blending(win_T *wp) { wp->w_grid_alloc.blending = - wp->w_p_winbl > 0 || (wp->w_floating && wp->w_float_config.shadow); + wp->w_p_winbl > 0 || (wp->w_floating && wp->w_config.shadow); } /// Handle setting `winhighlight' in window "wp" @@ -1827,23 +1843,18 @@ bool parse_winhl_opt(win_T *wp) return true; } -/// Get the script context of global option "name". -sctx_T *get_option_sctx(const char *const name) +/// Get the script context of global option at index opt_idx. +sctx_T *get_option_sctx(OptIndex opt_idx) { - int idx = findoption(name); - - if (idx >= 0) { - return &options[idx].last_set.script_ctx; - } - siemsg("no such option: %s", name); - return NULL; + assert(opt_idx != kOptInvalid); + return &options[opt_idx].last_set.script_ctx; } /// Set the script_ctx for an option, taking care of setting the buffer- or /// window-local value. -void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx) +void set_option_sctx(OptIndex opt_idx, int opt_flags, sctx_T script_ctx) { - int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; + bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int indir = (int)options[opt_idx].indir; nlua_set_sctx(&script_ctx); LastSet last_set = { @@ -1875,7 +1886,7 @@ void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx) } /// Apply the OptionSet autocommand. -static void apply_optionset_autocmd(int opt_idx, int opt_flags, OptVal oldval, OptVal oldval_g, +static void apply_optionset_autocmd(OptIndex opt_idx, int opt_flags, OptVal oldval, OptVal oldval_g, OptVal oldval_l, OptVal newval, const char *errmsg) { // Don't do this while starting up, failure or recursively. @@ -1884,10 +1895,10 @@ static void apply_optionset_autocmd(int opt_idx, int opt_flags, OptVal oldval, O } char buf_type[7]; - typval_T oldval_tv = optval_as_tv(oldval); - typval_T oldval_g_tv = optval_as_tv(oldval_g); - typval_T oldval_l_tv = optval_as_tv(oldval_l); - typval_T newval_tv = optval_as_tv(newval); + typval_T oldval_tv = optval_as_tv(oldval, false); + typval_T oldval_g_tv = optval_as_tv(oldval_g, false); + typval_T oldval_l_tv = optval_as_tv(oldval_l, false); + typval_T newval_tv = optval_as_tv(newval, false); vim_snprintf(buf_type, sizeof(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_tv(VV_OPTION_NEW, &newval_tv); @@ -1950,7 +1961,7 @@ static const char *did_set_arabic(optset_T *args) p_deco = true; // Force-set the necessary keymap for arabic. - errmsg = set_option_value("keymap", STATIC_CSTR_AS_OPTVAL("arabic"), OPT_LOCAL); + errmsg = set_option_value(kOptKeymap, STATIC_CSTR_AS_OPTVAL("arabic"), OPT_LOCAL); } else { // 'arabic' is reset, handle various sub-settings. if (!p_tbidi) { @@ -1995,22 +2006,6 @@ static const char *did_set_binary(optset_T *args) return NULL; } -/// Called when the 'breakat' option changes value. -static const char *did_set_breakat(optset_T *args FUNC_ATTR_UNUSED) -{ - for (int i = 0; i < 256; i++) { - breakat_flags[i] = false; - } - - if (p_breakat != NULL) { - for (char *p = p_breakat; *p; p++) { - breakat_flags[(uint8_t)(*p)] = true; - } - } - - return NULL; -} - /// Process the updated 'buflisted' option value. static const char *did_set_buflisted(optset_T *args) { @@ -2174,26 +2169,64 @@ static const char *did_set_laststatus(optset_T *args) // Also clear the cmdline to remove the ruler if there is one if (value == 3 && old_value != 3) { frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false); - (void)win_comp_pos(); + win_comp_pos(); clear_cmdline = true; } // When switching from global statusline, increase height of topframe by STATUS_HEIGHT // in order to to re-add the space that was previously taken by the global statusline if (old_value == 3 && value != 3) { frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false); - (void)win_comp_pos(); + win_comp_pos(); } last_status(false); // (re)set last window status line. return NULL; } +/// Process the updated 'lines' or 'columns' option value. +static const char *did_set_lines_or_columns(optset_T *args) +{ + // If the screen (shell) height has been changed, assume it is the + // physical screenheight. + if (p_lines != Rows || p_columns != Columns) { + // Changing the screen size is not allowed while updating the screen. + if (updating_screen) { + OptVal oldval = (OptVal){ .type = kOptValTypeNumber, .data = args->os_oldval }; + set_option_varp(args->os_idx, args->os_varp, oldval, false); + } else if (full_screen) { + screen_resize((int)p_columns, (int)p_lines); + } else { + // TODO(bfredl): is this branch ever needed? + // Postpone the resizing; check the size and cmdline position for + // messages. + Rows = (int)p_lines; + Columns = (int)p_columns; + check_screensize(); + int new_row = (int)(Rows - MAX(p_ch, 1)); + if (cmdline_row > new_row && Rows > p_ch) { + assert(p_ch >= 0 && new_row <= INT_MAX); + cmdline_row = new_row; + } + } + if (p_window >= Rows || !option_was_set(kOptWindow)) { + p_window = Rows - 1; + } + } + + // Adjust 'scrolljump' if needed. + if (p_sj >= Rows && full_screen) { + p_sj = Rows / 2; + } + + return NULL; +} + /// Process the updated 'lisp' option value. static const char *did_set_lisp(optset_T *args) { buf_T *buf = (buf_T *)args->os_buf; // When 'lisp' option changes include/exclude '-' in keyword characters. - (void)buf_init_chartab(buf, false); // ignore errors + buf_init_chartab(buf, false); // ignore errors return NULL; } @@ -2334,7 +2367,7 @@ static const char *did_set_paste(optset_T *args FUNC_ATTR_UNUSED) buf->b_p_vsts = buf->b_p_vsts_nopaste ? xstrdup(buf->b_p_vsts_nopaste) : empty_string_option; xfree(buf->b_p_vsts_array); if (buf->b_p_vsts && buf->b_p_vsts != empty_string_option) { - (void)tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array); + tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array); } else { buf->b_p_vsts_array = NULL; } @@ -2391,7 +2424,6 @@ static const char *did_set_previewwindow(optset_T *args) /// Process the new 'pumblend' option value. static const char *did_set_pumblend(optset_T *args FUNC_ATTR_UNUSED) { - p_pb = MAX(MIN(p_pb, 100), 0); hl_invalidate_blends(); pum_grid.blending = (p_pb > 0); if (pum_drawn()) { @@ -2762,231 +2794,224 @@ static void do_spelllang_source(win_T *win) } /// Check the bounds of numeric options. -static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, char *errbuf, - size_t errbuflen, const char *errmsg) +/// +/// @param opt_idx Index in options[] table. Must not be kOptInvalid. +/// @param[in] varp Pointer to option variable. +/// @param[in,out] newval Pointer to new option value. Will be set to bound checked value. +/// @param[out] errbuf Buffer for error message. Cannot be NULL. +/// @param errbuflen Length of error buffer. +/// +/// @return Error message, if any. +static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt *newval, + char *errbuf, size_t errbuflen) + FUNC_ATTR_NONNULL_ARG(4) { - int old_Rows = Rows; // remember old Rows - // Check the (new) bounds for Rows and Columns here. - if (p_lines < min_rows() && full_screen) { - if (errbuf != NULL) { + const char *errmsg = NULL; + + switch (opt_idx) { + case kOptLines: + if (*newval < min_rows() && full_screen) { vim_snprintf(errbuf, errbuflen, _("E593: Need at least %d lines"), min_rows()); errmsg = errbuf; + *newval = min_rows(); } - p_lines = min_rows(); - } - if (p_columns < MIN_COLUMNS && full_screen) { - if (errbuf != NULL) { + // True max size is defined by check_screensize(). + *newval = MIN(*newval, INT_MAX); + break; + case kOptColumns: + if (*newval < MIN_COLUMNS && full_screen) { vim_snprintf(errbuf, errbuflen, _("E594: Need at least %d columns"), MIN_COLUMNS); errmsg = errbuf; + *newval = MIN_COLUMNS; } - p_columns = MIN_COLUMNS; - } - - // True max size is defined by check_screensize() - p_lines = MIN(p_lines, INT_MAX); - p_columns = MIN(p_columns, INT_MAX); - - // If the screen (shell) height has been changed, assume it is the - // physical screenheight. - if (p_lines != Rows || p_columns != Columns) { - // Changing the screen size is not allowed while updating the screen. - if (updating_screen) { - *pp = old_value; - } else if (full_screen) { - screen_resize((int)p_columns, (int)p_lines); - } else { - // TODO(bfredl): is this branch ever needed? - // Postpone the resizing; check the size and cmdline position for - // messages. - Rows = (int)p_lines; - Columns = (int)p_columns; - check_screensize(); - int new_row = (int)(Rows - MAX(p_ch, 1)); - if (cmdline_row > new_row && Rows > p_ch) { - assert(p_ch >= 0 && new_row <= INT_MAX); - cmdline_row = new_row; - } - } - if (p_window >= Rows || !option_was_set("window")) { - p_window = Rows - 1; + // True max size is defined by check_screensize(). + *newval = MIN(*newval, INT_MAX); + break; + case kOptPumblend: + *newval = MAX(MIN(*newval, 100), 0); + break; + case kOptScrolljump: + if ((*newval < -100 || *newval >= Rows) && full_screen) { + errmsg = e_scroll; + *newval = 1; } - } - - if ((curwin->w_p_scr <= 0 || (curwin->w_p_scr > curwin->w_height && curwin->w_height > 0)) - && full_screen) { - if (pp == &(curwin->w_p_scr)) { - if (curwin->w_p_scr != 0) { + break; + case kOptScroll: + if (varp == &(curwin->w_p_scr) + && (*newval <= 0 + || (*newval > curwin->w_height_inner && curwin->w_height_inner > 0)) + && full_screen) { + if (*newval != 0) { errmsg = e_scroll; } - win_comp_scroll(curwin); - } else if (curwin->w_p_scr <= 0) { - // If 'scroll' became invalid because of a side effect silently adjust it. - curwin->w_p_scr = 1; - } else { // curwin->w_p_scr > curwin->w_height - curwin->w_p_scr = curwin->w_height; - } - } - if ((p_sj < -100 || p_sj >= Rows) && full_screen) { - if (Rows != old_Rows) { // Rows changed, just adjust p_sj - p_sj = Rows / 2; - } else { - errmsg = e_scroll; - p_sj = 1; + *newval = win_default_scroll(curwin); } + break; + default: + break; } return errmsg; } -/// Options that need some validation. -static const char *validate_num_option(const OptInt *pp, OptInt *valuep) +/// Validate and bound check option value. +/// +/// @param opt_idx Index in options[] table. Must not be kOptInvalid. +/// @param[in] varp Pointer to option variable. +/// @param[in,out] newval Pointer to new option value. Will be set to bound checked value. +/// @param[out] errbuf Buffer for error message. Cannot be NULL. +/// @param errbuflen Length of error buffer. +/// +/// @return Error message, if any. +static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *newval, char *errbuf, + size_t errbuflen) { - OptInt value = *valuep; + OptInt value = *newval; // Many number options assume their value is in the signed int range. if (value < INT_MIN || value > INT_MAX) { return e_invarg; } - if (pp == &p_wh) { + if (varp == &p_wh) { if (value < 1) { return e_positive; } else if (p_wmh > value) { return e_winheight; } - } else if (pp == &p_hh) { + } else if (varp == &p_hh) { if (value < 0) { return e_positive; } - } else if (pp == &p_wmh) { + } else if (varp == &p_wmh) { if (value < 0) { return e_positive; } else if (value > p_wh) { return e_winheight; } - } else if (pp == &p_wiw) { + } else if (varp == &p_wiw) { if (value < 1) { return e_positive; } else if (p_wmw > value) { return e_winwidth; } - } else if (pp == &p_wmw) { + } else if (varp == &p_wmw) { if (value < 0) { return e_positive; } else if (value > p_wiw) { return e_winwidth; } - } else if (pp == &p_mco) { - *valuep = MAX_MCO; - } else if (pp == &p_titlelen) { + } else if (varp == &p_mco) { + *newval = MAX_MCO; + } else if (varp == &p_titlelen) { if (value < 0) { return e_positive; } - } else if (pp == &p_uc) { + } else if (varp == &p_uc) { if (value < 0) { return e_positive; } - } else if (pp == &p_ch) { + } else if (varp == &p_ch) { if (value < 0) { return e_positive; } else { p_ch_was_zero = value == 0; } - } else if (pp == &p_tm) { + } else if (varp == &p_tm) { if (value < 0) { return e_positive; } - } else if (pp == &p_hi) { + } else if (varp == &p_hi) { if (value < 0) { return e_positive; } else if (value > 10000) { return e_invarg; } - } else if (pp == &p_pyx) { + } else if (varp == &p_pyx) { if (value == 0) { - *valuep = 3; + *newval = 3; } else if (value != 3) { return e_invarg; } - } else if (pp == &p_re) { + } else if (varp == &p_re) { if (value < 0 || value > 2) { return e_invarg; } - } else if (pp == &p_report) { + } else if (varp == &p_report) { if (value < 0) { return e_positive; } - } else if (pp == &p_so) { + } else if (varp == &p_so) { if (value < 0 && full_screen) { return e_positive; } - } else if (pp == &p_siso) { + } else if (varp == &p_siso) { if (value < 0 && full_screen) { return e_positive; } - } else if (pp == &p_cwh) { + } else if (varp == &p_cwh) { if (value < 1) { return e_positive; } - } else if (pp == &p_ut) { + } else if (varp == &p_ut) { if (value < 0) { return e_positive; } - } else if (pp == &p_ss) { + } else if (varp == &p_ss) { if (value < 0) { return e_positive; } - } else if (pp == &curwin->w_p_fdl || pp == &curwin->w_allbuf_opt.wo_fdl) { + } else if (varp == &curwin->w_p_fdl || varp == &curwin->w_allbuf_opt.wo_fdl) { if (value < 0) { return e_positive; } - } else if (pp == &curwin->w_p_cole || pp == &curwin->w_allbuf_opt.wo_cole) { + } else if (varp == &curwin->w_p_cole || varp == &curwin->w_allbuf_opt.wo_cole) { if (value < 0) { return e_positive; } else if (value > 3) { return e_invarg; } - } else if (pp == &curwin->w_p_nuw || pp == &curwin->w_allbuf_opt.wo_nuw) { + } else if (varp == &curwin->w_p_nuw || varp == &curwin->w_allbuf_opt.wo_nuw) { if (value < 1) { return e_positive; } else if (value > MAX_NUMBERWIDTH) { return e_invarg; } - } else if (pp == &curbuf->b_p_iminsert || pp == &p_iminsert) { + } else if (varp == &curbuf->b_p_iminsert || varp == &p_iminsert) { if (value < 0 || value > B_IMODE_LAST) { return e_invarg; } - } else if (pp == &curbuf->b_p_imsearch || pp == &p_imsearch) { + } else if (varp == &curbuf->b_p_imsearch || varp == &p_imsearch) { if (value < -1 || value > B_IMODE_LAST) { return e_invarg; } - } else if (pp == &curbuf->b_p_channel || pp == &p_channel) { + } else if (varp == &curbuf->b_p_channel || varp == &p_channel) { return e_invarg; - } else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) { + } else if (varp == &curbuf->b_p_scbk || varp == &p_scbk) { if (value < -1 || value > SB_MAX) { return e_invarg; } - } else if (pp == &curbuf->b_p_sw || pp == &p_sw) { + } else if (varp == &curbuf->b_p_sw || varp == &p_sw) { if (value < 0) { return e_positive; } - } else if (pp == &curbuf->b_p_ts || pp == &p_ts) { + } else if (varp == &curbuf->b_p_ts || varp == &p_ts) { if (value < 1) { return e_positive; } else if (value > TABSTOP_MAX) { return e_invarg; } - } else if (pp == &curbuf->b_p_tw || pp == &p_tw) { + } else if (varp == &curbuf->b_p_tw || varp == &p_tw) { if (value < 0) { return e_positive; } - } else if (pp == &p_wd) { + } else if (varp == &p_wd) { if (value < 0) { return e_positive; } } - return NULL; + return check_num_option_bounds(opt_idx, varp, newval, errbuf, errbuflen); } /// Called after an option changed: check if something needs to be redrawn. @@ -3004,14 +3029,15 @@ void check_redraw_for(buf_T *buf, win_T *win, uint32_t flags) } if ((flags & P_RBUF) || (flags & P_RWIN) || all) { - changed_window_setting_win(win); + if (flags & P_HLONLY) { + redraw_later(win, UPD_NOT_VALID); + } else { + changed_window_setting_win(win); + } } if (flags & P_RBUF) { redraw_buf_later(buf, UPD_NOT_VALID); } - if (flags & P_RWINONLY) { - redraw_later(win, UPD_NOT_VALID); - } if (all) { redraw_all_later(UPD_NOT_VALID); } @@ -3022,126 +3048,40 @@ void check_redraw(uint32_t flags) check_redraw_for(curbuf, curwin, flags); } -/// Find index for named option -/// -/// @param[in] arg Option to find index for. -/// @param[in] len Length of the option. -/// -/// @return Index of the option or -1 if option was not found. -int findoption_len(const char *const arg, const size_t len) -{ - const char *s; - static int quick_tab[27] = { 0, 0 }; // quick access table - - // For first call: Initialize the quick-access table. - // It contains the index for the first option that starts with a certain - // letter. There are 26 letters, plus the first "t_" option. - if (quick_tab[1] == 0) { - const char *p = options[0].fullname; - for (uint16_t i = 1; (s = options[i].fullname) != NULL; i++) { - if (s[0] != p[0]) { - if (s[0] == 't' && s[1] == '_') { - quick_tab[26] = i; - } else { - quick_tab[CHAR_ORD_LOW(s[0])] = i; - } - } - p = s; - } - } - - // Check for name starting with an illegal character. - if (len == 0 || arg[0] < 'a' || arg[0] > 'z') { - return -1; - } - - int opt_idx; - const bool is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); - if (is_term_opt) { - opt_idx = quick_tab[26]; - } else { - opt_idx = quick_tab[CHAR_ORD_LOW(arg[0])]; - } - // Match full name - for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) { - if (strncmp(arg, s, len) == 0 && s[len] == NUL) { - break; - } - } - if (s == NULL && !is_term_opt) { - opt_idx = quick_tab[CHAR_ORD_LOW(arg[0])]; - // Match short name - for (; options[opt_idx].fullname != NULL; opt_idx++) { - s = options[opt_idx].shortname; - if (s != NULL && strncmp(arg, s, len) == 0 && s[len] == NUL) { - break; - } - s = NULL; - } - } - if (s == NULL) { - opt_idx = -1; - } else { - // Nvim: handle option aliases. - if (strncmp(options[opt_idx].fullname, "viminfo", 7) == 0) { - if (strlen(options[opt_idx].fullname) == 7) { - return findoption_len("shada", 5); - } - assert(strcmp(options[opt_idx].fullname, "viminfofile") == 0); - return findoption_len("shadafile", 9); - } - } - return opt_idx; -} - bool is_tty_option(const char *name) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return (name[0] == 't' && name[1] == '_') - || strequal(name, "term") - || strequal(name, "ttytype"); + return find_tty_option_end(name) != NULL; } #define TCO_BUFFER_SIZE 8 -/// @param name TUI-related option -/// @param[out,allocated] value option string value -bool get_tty_option(const char *name, char **value) -{ - if (strequal(name, "t_Co")) { - if (value) { - if (t_colors <= 1) { - *value = xstrdup(""); - } else { - *value = xmalloc(TCO_BUFFER_SIZE); - snprintf(*value, TCO_BUFFER_SIZE, "%d", t_colors); - } - } - return true; - } - - if (strequal(name, "term")) { - if (value) { - *value = p_term ? xstrdup(p_term) : xstrdup("nvim"); - } - return true; - } - if (strequal(name, "ttytype")) { - if (value) { - *value = p_ttytype ? xstrdup(p_ttytype) : xstrdup("nvim"); - } - return true; - } +/// Get value of TTY option. +/// +/// @param name Name of TTY option. +/// +/// @return [allocated] TTY option value. Returns NIL_OPTVAL if option isn't a TTY option. +OptVal get_tty_option(const char *name) +{ + char *value = NULL; - if (is_tty_option(name)) { - if (value) { - // XXX: All other t_* options were removed in 3baba1e7. - *value = xstrdup(""); + if (strequal(name, "t_Co")) { + if (t_colors <= 1) { + value = xstrdup(""); + } else { + value = xmalloc(TCO_BUFFER_SIZE); + snprintf(value, TCO_BUFFER_SIZE, "%d", t_colors); } - return true; + } else if (strequal(name, "term")) { + value = p_term ? xstrdup(p_term) : xstrdup("nvim"); + } else if (strequal(name, "ttytype")) { + value = p_ttytype ? xstrdup(p_ttytype) : xstrdup("nvim"); + } else if (is_tty_option(name)) { + // XXX: All other t_* options were removed in 3baba1e7. + value = xstrdup(""); } - return false; + return value == NULL ? NIL_OPTVAL : CSTR_AS_OPTVAL(value); } bool set_tty_option(const char *name, char *value) @@ -3165,15 +3105,28 @@ bool set_tty_option(const char *name, char *value) return false; } -/// Find index for an option +/// Find index for an option. Don't go beyond `len` length. +/// +/// @param[in] name Option name. +/// @param len Option name length. +/// +/// @return Option index or kOptInvalid if option was not found. +OptIndex find_option_len(const char *const name, size_t len) + FUNC_ATTR_NONNULL_ALL +{ + int index = find_option_hash(name, len); + return index >= 0 ? option_hash_elems[index].opt_idx : kOptInvalid; +} + +/// Find index for an option. /// -/// @param[in] arg Option name. +/// @param[in] name Option name. /// -/// @return Option index or -1 if option was not found. -int findoption(const char *const arg) +/// @return Option index or kOptInvalid if option was not found. +OptIndex find_option(const char *const name) FUNC_ATTR_NONNULL_ALL { - return findoption_len(arg, strlen(arg)); + return find_option_len(name, strlen(name)); } /// Free an allocated OptVal. @@ -3223,27 +3176,7 @@ bool optval_equal(OptVal o1, OptVal o2) return o1.data.number == o2.data.number; case kOptValTypeString: return o1.data.string.size == o2.data.string.size - && strequal(o1.data.string.data, o2.data.string.data); - } - UNREACHABLE; -} - -/// Match type of OptVal with the type of the target option. Returns true if the types match and -/// false otherwise. -static bool optval_match_type(OptVal o, int opt_idx) -{ - assert(opt_idx >= 0); - uint32_t flags = options[opt_idx].flags; - - switch (o.type) { - case kOptValTypeNil: - return false; - case kOptValTypeBoolean: - return flags & P_BOOL; - case kOptValTypeNumber: - return flags & P_NUM; - case kOptValTypeString: - return flags & P_STRING; + && strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size); } UNREACHABLE; } @@ -3252,7 +3185,9 @@ static bool optval_match_type(OptVal o, int opt_idx) /// /// @param opt_idx Option index in options[] table. /// @param[out] varp Pointer to option variable. -OptVal optval_from_varp(int opt_idx, void *varp) +/// +/// @return Option value stored in varp. +OptVal optval_from_varp(OptIndex opt_idx, void *varp) { // Special case: 'modified' is b_changed, but we also want to consider it set when 'ff' or 'fenc' // changed. @@ -3260,19 +3195,17 @@ OptVal optval_from_varp(int opt_idx, void *varp) return BOOLEAN_OPTVAL(curbufIsChanged()); } - uint32_t flags = options[opt_idx].flags; - - OptValType type = kOptValTypeNil; - if (flags & P_BOOL) { - type = kOptValTypeBoolean; - } else if (flags & P_NUM) { - type = kOptValTypeNumber; - } else if (flags & P_STRING) { - type = kOptValTypeString; - } else { - abort(); + if (option_is_multitype(opt_idx)) { + // Multitype options are stored as OptVal. + return varp == NULL ? NIL_OPTVAL : *(OptVal *)varp; } + // If the option only supports a single type, it means that the index of the option's type flag + // corresponds to the value of the type enum. So get the index of the type flag using xctz() and + // use that as the option's type. + OptValType type = xctz(options[opt_idx].type_flags); + assert(type > kOptValTypeNil && type < kOptValTypeSize); + switch (type) { case kOptValTypeNil: return NIL_OPTVAL; @@ -3286,16 +3219,16 @@ OptVal optval_from_varp(int opt_idx, void *varp) UNREACHABLE; } -/// Set option var pointer value from Optval. +/// Set option var pointer value from OptVal. /// /// @param opt_idx Option index in options[] table. /// @param[out] varp Pointer to option variable. /// @param[in] value New option value. /// @param free_oldval Free old value. -static void set_option_varp(int opt_idx, void *varp, OptVal value, bool free_oldval) +static void set_option_varp(OptIndex opt_idx, void *varp, OptVal value, bool free_oldval) FUNC_ATTR_NONNULL_ARG(2) { - assert(optval_match_type(value, opt_idx)); + assert(option_has_type(opt_idx, value.type)); if (free_oldval) { optval_free(optval_from_varp(opt_idx, varp)); @@ -3303,7 +3236,7 @@ static void set_option_varp(int opt_idx, void *varp, OptVal value, bool free_old switch (value.type) { case kOptValTypeNil: - return; + abort(); case kOptValTypeBoolean: *(int *)varp = value.data.boolean; return; @@ -3388,13 +3321,13 @@ OptVal object_as_optval(Object o, bool *error) /// @param[in] varp Pointer to option variable. /// /// @return [allocated] Option value equal to the unset value for the option. -static OptVal optval_unset_local(int opt_idx, void *varp) +static OptVal optval_unset_local(OptIndex opt_idx, void *varp) { vimoption_T *opt = &options[opt_idx]; // For global-local options, use the unset value of the local value. if (opt->indir & PV_BOTH) { // String global-local options always use an empty string for the unset value. - if (opt->flags & P_STRING) { + if (option_has_type(opt_idx, kOptValTypeString)) { return STATIC_CSTR_TO_OPTVAL(""); } @@ -3410,43 +3343,29 @@ static OptVal optval_unset_local(int opt_idx, void *varp) } } // For options that aren't global-local, just set the local value to the global value. - return get_option_value(opt->fullname, NULL, OPT_GLOBAL, NULL); + return get_option_value(opt_idx, OPT_GLOBAL); } /// Get an allocated string containing a list of valid types for an option. /// For options with a singular type, it returns the name of the type. For options with multiple /// possible types, it returns a slash separated list of types. For example, if an option can be a /// number, boolean or string, the function returns "Number/Boolean/String" -static char *option_get_valid_types(int opt_idx) +static char *option_get_valid_types(OptIndex opt_idx) { - uint32_t flags = options[opt_idx].flags; - uint32_t type_count = 0; - StringBuilder str = KV_INITIAL_VALUE; kv_resize(str, 32); -#define OPTION_ADD_TYPE(typename) \ - do { \ - if (type_count == 0) { \ - kv_concat(str, typename); \ - } else { \ - kv_printf(str, "/%s", typename); \ - } \ - type_count++; \ - } while (0); - - if (flags & P_NUM) { - OPTION_ADD_TYPE("Number"); - } - if (flags & P_BOOL) { - OPTION_ADD_TYPE("Boolean"); - } - if (flags & P_STRING) { - OPTION_ADD_TYPE("String"); - } + // Iterate through every valid option value type and check if the option supports that type + for (OptValType type = 0; type < kOptValTypeSize; type++) { + if (option_has_type(opt_idx, type)) { + const char *typename = optval_type_get_name(type); - if (type_count == 0) { - abort(); + if (str.size == 0) { + kv_concat(str, typename); + } else { + kv_printf(str, "/%s", typename); + } + } } // Ensure that the string is NUL-terminated. @@ -3456,52 +3375,48 @@ static char *option_get_valid_types(int opt_idx) #undef OPTION_ADD_TYPE } -/// Gets the value for an option. +/// Check if option is hidden. /// -/// @param[in] name Option name. -/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL). -/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). -/// @param[out] hidden Whether option is hidden. +/// @param opt_idx Option index in options[] table. /// -/// @return [allocated] Option value. Returns NIL_OPTVAL for invalid options. -OptVal get_option_value(const char *name, uint32_t *flagsp, int scope, bool *hidden) +/// @return True if option is hidden, false otherwise. Returns false if option name is invalid. +bool is_option_hidden(OptIndex opt_idx) { - // Make sure that hidden and flagsp are never returned uninitialized - if (hidden != NULL) { - *hidden = false; - } - if (flagsp != NULL) { - *flagsp = 0; - } + return opt_idx == kOptInvalid ? false : get_varp(&options[opt_idx]) == NULL; +} - char *str; - if (get_tty_option(name, &str)) { - return CSTR_AS_OPTVAL(str); - } +/// Get option flags. +/// +/// @param opt_idx Option index in options[] table. +/// +/// @return Option flags. Returns 0 for invalid option name. +uint32_t get_option_flags(OptIndex opt_idx) +{ + return opt_idx == kOptInvalid ? 0 : options[opt_idx].flags; +} - int opt_idx = findoption(name); - if (opt_idx < 0) { // option not in the table +/// Gets the value for an option. +/// +/// @param opt_idx Option index in options[] table. +/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). +/// +/// @return [allocated] Option value. Returns NIL_OPTVAL for invalid option index. +OptVal get_option_value(OptIndex opt_idx, int scope) +{ + if (opt_idx == kOptInvalid) { // option not in the options[] table. return NIL_OPTVAL; } vimoption_T *opt = &options[opt_idx]; void *varp = get_varp_scope(opt, scope); - if (hidden != NULL) { - *hidden = varp == NULL; - } - - if (flagsp != NULL) { - // Return the P_xxxx option flags. - *flagsp = opt->flags; - } - return optval_copy(optval_from_varp(opt_idx, varp)); } /// Return information for option at 'opt_idx' -vimoption_T *get_option(int opt_idx) +vimoption_T *get_option(OptIndex opt_idx) { + assert(opt_idx != kOptInvalid); return &options[opt_idx]; } @@ -3527,7 +3442,7 @@ static bool is_option_local_value_unset(vimoption_T *opt, buf_T *buf, win_T *win /// Handle side-effects of setting an option. /// -/// @param opt_idx Index in options[] table. Must be >= 0. +/// @param opt_idx Index in options[] table. Must not be kOptInvalid. /// @param[in] varp Option variable pointer, cannot be NULL. /// @param old_value Old option value. /// @param new_value New option value. @@ -3538,9 +3453,10 @@ static bool is_option_local_value_unset(vimoption_T *opt, buf_T *buf, win_T *win /// @param errbuflen Length of error buffer. /// /// @return NULL on success, an untranslated error message on error. -static const char *did_set_option(int opt_idx, void *varp, OptVal old_value, OptVal new_value, +static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value, OptVal new_value, int opt_flags, bool *value_checked, bool value_replaced, - char *errbuf, size_t errbuflen) + char *errbuf, // NOLINT(readability-non-const-parameter) + size_t errbuflen) { vimoption_T *opt = &options[opt_idx]; const char *errmsg = NULL; @@ -3593,7 +3509,7 @@ static const char *did_set_option(int opt_idx, void *varp, OptVal old_value, Opt set_option_varp(opt_idx, varp, old_value, true); // When resetting some values, need to act on it. if (restore_chartab) { - (void)buf_init_chartab(curbuf, true); + buf_init_chartab(curbuf, true); } // Unset new_value as it is no longer valid. @@ -3605,7 +3521,7 @@ static const char *did_set_option(int opt_idx, void *varp, OptVal old_value, Opt new_value = optval_from_varp(opt_idx, varp); // Remember where the option was set. - set_option_sctx_idx(opt_idx, opt_flags, current_sctx); + set_option_sctx(opt_idx, opt_flags, current_sctx); // Free options that are in allocated memory. // Use "free_oldval", because recursiveness may change the flags (esp. init_highlight()). if (free_oldval) { @@ -3613,14 +3529,6 @@ static const char *did_set_option(int opt_idx, void *varp, OptVal old_value, Opt } opt->flags |= P_ALLOCED; - // Check the bound for num options. - if (new_value.type == kOptValTypeNumber) { - errmsg = check_num_option_bounds((OptInt *)varp, old_value.data.number, errbuf, errbuflen, - errmsg); - // Re-assign new_value because the new value was modified by the bound check. - new_value = optval_from_varp(opt_idx, varp); - } - if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 && (opt->indir & PV_BOTH)) { // Global option with local value set to use global value. // Free the local value and clear it. @@ -3647,7 +3555,7 @@ static const char *did_set_option(int opt_idx, void *varp, OptVal old_value, Opt do_spelllang_source(curwin); } - // In case 'columns' or 'ls' changed. + // In case 'ruler' or 'showcmd' or 'columns' or 'ls' changed. comp_col(); if (varp == &p_mouse) { @@ -3661,7 +3569,8 @@ static const char *did_set_option(int opt_idx, void *varp, OptVal old_value, Opt set_winbar(true); } - if (curwin->w_curswant != MAXCOL && (opt->flags & (P_CURSWANT | P_RALL)) != 0) { + if (curwin->w_curswant != MAXCOL + && (opt->flags & (P_CURSWANT | P_RALL)) != 0 && (opt->flags & P_HLONLY) == 0) { curwin->w_set_curswant = true; } @@ -3685,7 +3594,7 @@ static const char *did_set_option(int opt_idx, void *varp, OptVal old_value, Opt /// Set the value of an option using an OptVal. /// -/// @param opt_idx Index in options[] table. Must be >= 0. +/// @param opt_idx Index in options[] table. Must not be kOptInvalid. /// @param[in] varp Option variable pointer, cannot be NULL. /// @param value New option value. Might get freed. /// @param opt_flags Option flags. @@ -3694,23 +3603,16 @@ static const char *did_set_option(int opt_idx, void *varp, OptVal old_value, Opt /// @param errbuflen Length of error buffer. /// /// @return NULL on success, an untranslated error message on error. -static const char *set_option(const int opt_idx, void *varp, OptVal value, int opt_flags, +static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags, const bool value_replaced, char *errbuf, size_t errbuflen) { - assert(opt_idx >= 0 && varp != NULL); + assert(opt_idx != kOptInvalid && varp != NULL); const char *errmsg = NULL; bool value_checked = false; vimoption_T *opt = &options[opt_idx]; - static const char *optval_type_names[] = { - [kOptValTypeNil] = "Nil", - [kOptValTypeBoolean] = "Boolean", - [kOptValTypeNumber] = "Number", - [kOptValTypeString] = "String" - }; - if (value.type == kOptValTypeNil) { // Don't try to unset local value if scope is global. // TODO(famiu): Change this to forbid changing all non-local scopes when the API scope bug is @@ -3721,11 +3623,11 @@ static const char *set_option(const int opt_idx, void *varp, OptVal value, int o optval_free(value); value = optval_unset_local(opt_idx, varp); } - } else if (!optval_match_type(value, opt_idx)) { + } else if (!option_has_type(opt_idx, value.type)) { char *rep = optval_to_cstr(value); char *valid_types = option_get_valid_types(opt_idx); snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"), - opt->fullname, valid_types, optval_type_names[value.type], rep); + opt->fullname, valid_types, optval_type_get_name(value.type), rep); xfree(rep); xfree(valid_types); errmsg = errbuf; @@ -3767,9 +3669,7 @@ static const char *set_option(const int opt_idx, void *varp, OptVal value, int o OptVal used_old_value = oldval_is_global ? optval_from_varp(opt_idx, get_varp(opt)) : old_value; if (value.type == kOptValTypeNumber) { - errmsg = validate_num_option((OptInt *)varp, &value.data.number); - - // Don't change the value and return early if validation failed. + errmsg = validate_num_option(opt_idx, varp, &value.data.number, errbuf, errbuflen); if (errmsg != NULL) { goto err; } @@ -3825,29 +3725,20 @@ err: return errmsg; } -/// Set the value of an option +/// Set the value of an option. /// -/// @param[in] name Option name. +/// @param opt_idx Index in options[] table. Must not be kOptInvalid. /// @param[in] value Option value. If NIL_OPTVAL, the option value is cleared. /// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). /// /// @return NULL on success, an untranslated error message on error. -const char *set_option_value(const char *const name, const OptVal value, int opt_flags) - FUNC_ATTR_NONNULL_ARG(1) +const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt_flags) { - static char errbuf[IOSIZE]; - - if (is_tty_option(name)) { - return NULL; // Fail silently; many old vimrcs set t_xx options. - } - - int opt_idx = findoption(name); - if (opt_idx < 0) { - snprintf(errbuf, IOSIZE, _(e_unknown_option2), name); - return errbuf; - } + assert(opt_idx != kOptInvalid); + static char errbuf[IOSIZE]; uint32_t flags = options[opt_idx].flags; + // Disallow changing some options in the sandbox if (sandbox > 0 && (flags & P_SECURE)) { return _(e_sandbox); @@ -3859,58 +3750,283 @@ const char *set_option_value(const char *const name, const OptVal value, int opt return NULL; } - const char *errmsg = NULL; + return set_option(opt_idx, varp, optval_copy(value), opt_flags, true, errbuf, sizeof(errbuf)); +} - errmsg = set_option(opt_idx, varp, optval_copy(value), opt_flags, true, errbuf, sizeof(errbuf)); +/// Set the value of an option. Supports TTY options, unlike set_option_value(). +/// +/// @param name Option name. Used for error messages and for setting TTY options. +/// @param opt_idx Option indx in options[] table. If kOptInvalid, `name` is used to +/// check if the option is a TTY option, and an error is shown if it's not. +/// If the option is a TTY option, the function fails silently. +/// @param value Option value. If NIL_OPTVAL, the option value is cleared. +/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). +/// +/// @return NULL on success, an untranslated error message on error. +const char *set_option_value_handle_tty(const char *name, OptIndex opt_idx, const OptVal value, + int opt_flags) + FUNC_ATTR_NONNULL_ARG(1) +{ + static char errbuf[IOSIZE]; - return errmsg; + if (opt_idx == kOptInvalid) { + if (is_tty_option(name)) { + return NULL; // Fail silently; many old vimrcs set t_xx options. + } + + snprintf(errbuf, sizeof(errbuf), _(e_unknown_option2), name); + return errbuf; + } + + return set_option_value(opt_idx, value, opt_flags); } -/// Call set_option_value() and when an error is returned report it. +/// Call set_option_value() and when an error is returned, report it. /// -/// @param opt_flags OPT_LOCAL or 0 (both) -void set_option_value_give_err(const char *name, OptVal value, int opt_flags) +/// @param opt_idx Option index in options[] table. +/// @param value Option value. If NIL_OPTVAL, the option value is cleared. +/// @param opt_flags OPT_LOCAL or 0 (both) +void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_flags) { - const char *errmsg = set_option_value(name, value, opt_flags); + const char *errmsg = set_option_value(opt_idx, value, opt_flags); if (errmsg != NULL) { emsg(_(errmsg)); } } -bool is_option_allocated(const char *name) +/// Switch current context to get/set option value for window/buffer. +/// +/// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer. +/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param[in] from Target buffer/window. +/// @param[out] err Error message, if any. +/// +/// @return true if context was switched, false otherwise. +static bool switch_option_context(void *const ctx, OptReqScope req_scope, void *const from, + Error *err) { - int idx = findoption(name); - return idx >= 0 && (options[idx].flags & P_ALLOCED); + switch (req_scope) { + case kOptReqWin: { + win_T *const win = (win_T *)from; + switchwin_T *const switchwin = (switchwin_T *)ctx; + + if (win == curwin) { + return false; + } + + if (switch_win_noblock(switchwin, win, win_find_tabpage(win), true) + == FAIL) { + restore_win_noblock(switchwin, true); + + if (try_end(err)) { + return false; + } + api_set_error(err, kErrorTypeException, "Problem while switching windows"); + return false; + } + return true; + } + case kOptReqBuf: { + buf_T *const buf = (buf_T *)from; + aco_save_T *const aco = (aco_save_T *)ctx; + + if (buf == curbuf) { + return false; + } + aucmd_prepbuf(aco, buf); + return true; + } + case kOptReqGlobal: + return false; + } + UNREACHABLE; } -// Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number. -// When "has_lt" is true there is a '<' before "*arg_arg". -// Returns 0 when the key is not recognized. -int find_key_option_len(const char *arg_arg, size_t len, bool has_lt) +/// Restore context after getting/setting option for window/buffer. See switch_option_context() for +/// params. +static void restore_option_context(void *const ctx, OptReqScope req_scope) { - int key = 0; - const char *arg = arg_arg; + switch (req_scope) { + case kOptReqWin: + restore_win_noblock((switchwin_T *)ctx, true); + break; + case kOptReqBuf: + aucmd_restbuf((aco_save_T *)ctx); + break; + case kOptReqGlobal: + break; + } +} - // Don't use get_special_key_code() for t_xx, we don't want it to call - // add_termcap_entry(). - if (len >= 4 && arg[0] == 't' && arg[1] == '_') { - key = TERMCAP2KEY((uint8_t)arg[2], (uint8_t)arg[3]); - } else if (has_lt) { - arg--; // put arg at the '<' - int modifiers = 0; - key = find_special_key(&arg, len + 1, &modifiers, - FSK_KEYCODE | FSK_KEEP_X_KEY | FSK_SIMPLIFY, NULL); - if (modifiers) { // can't handle modifiers here - key = 0; - } +/// Get attributes for an option. +/// +/// @param opt_idx Option index in options[] table. +/// +/// @return Option attributes. +/// 0 for hidden or unknown option. +/// See SOPT_* in option_defs.h for other flags. +int get_option_attrs(OptIndex opt_idx) +{ + if (opt_idx == kOptInvalid) { + return 0; } - return key; + + vimoption_T *opt = get_option(opt_idx); + + // Hidden option + if (opt->var == NULL) { + return 0; + } + + int attrs = 0; + + if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) { + attrs |= SOPT_GLOBAL; + } + if (opt->indir & PV_WIN) { + attrs |= SOPT_WIN; + } else if (opt->indir & PV_BUF) { + attrs |= SOPT_BUF; + } + + return attrs; +} + +/// Check if option has a value in the requested scope. +/// +/// @param opt_idx Option index in options[] table. +/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// +/// @return true if option has a value in the requested scope, false otherwise. +static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope) +{ + if (opt_idx == kOptInvalid) { + return false; + } + + vimoption_T *opt = get_option(opt_idx); + + // Hidden option. + if (opt->var == NULL) { + return false; + } + // TTY option. + if (is_tty_option(opt->fullname)) { + return req_scope == kOptReqGlobal; + } + + switch (req_scope) { + case kOptReqGlobal: + return opt->var != VAR_WIN; + case kOptReqBuf: + return opt->indir & PV_BUF; + case kOptReqWin: + return opt->indir & PV_WIN; + } + UNREACHABLE; +} + +/// Get the option value in the requested scope. +/// +/// @param opt_idx Option index in options[] table. +/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param[in] from Pointer to buffer or window for local option value. +/// @param[out] err Error message, if any. +/// +/// @return Option value in the requested scope. Returns a Nil option value if option is not found, +/// hidden or if it isn't present in the requested scope. (i.e. has no global, window-local or +/// buffer-local value depending on opt_scope). +OptVal get_option_value_strict(OptIndex opt_idx, OptReqScope req_scope, void *from, Error *err) +{ + if (opt_idx == kOptInvalid || !option_has_scope(opt_idx, req_scope)) { + return NIL_OPTVAL; + } + + vimoption_T *opt = get_option(opt_idx); + switchwin_T switchwin; + aco_save_T aco; + void *ctx = req_scope == kOptReqWin ? (void *)&switchwin + : (req_scope == kOptReqBuf ? (void *)&aco : NULL); + bool switched = switch_option_context(ctx, req_scope, from, err); + if (ERROR_SET(err)) { + return NIL_OPTVAL; + } + + char *varp = get_varp_scope(opt, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL); + OptVal retv = optval_from_varp(opt_idx, varp); + + if (switched) { + restore_option_context(ctx, req_scope); + } + + return retv; +} + +/// Get option value for buffer / window. +/// +/// @param opt_idx Option index in options[] table. +/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL). +/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). +/// @param[out] hidden Whether option is hidden. +/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param[in] from Target buffer/window. +/// @param[out] err Error message, if any. +/// +/// @return Option value. Must be freed by caller. +OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptReqScope req_scope, + void *const from, Error *err) +{ + switchwin_T switchwin; + aco_save_T aco; + void *ctx = req_scope == kOptReqWin ? (void *)&switchwin + : (req_scope == kOptReqBuf ? (void *)&aco : NULL); + + bool switched = switch_option_context(ctx, req_scope, from, err); + if (ERROR_SET(err)) { + return NIL_OPTVAL; + } + + OptVal retv = get_option_value(opt_idx, scope); + + if (switched) { + restore_option_context(ctx, req_scope); + } + + return retv; } -static int find_key_option(const char *arg, bool has_lt) +/// Set option value for buffer / window. +/// +/// @param name Option name. +/// @param opt_idx Option index in options[] table. +/// @param[in] value Option value. +/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). +/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param[in] from Target buffer/window. +/// @param[out] err Error message, if any. +void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags, + const OptReqScope req_scope, void *const from, Error *err) + FUNC_ATTR_NONNULL_ARG(1) { - return find_key_option_len(arg, strlen(arg), has_lt); + switchwin_T switchwin; + aco_save_T aco; + void *ctx = req_scope == kOptReqWin ? (void *)&switchwin + : (req_scope == kOptReqBuf ? (void *)&aco : NULL); + + bool switched = switch_option_context(ctx, req_scope, from, err); + if (ERROR_SET(err)) { + return; + } + + const char *const errmsg = set_option_value_handle_tty(name, opt_idx, value, opt_flags); + if (errmsg) { + api_set_error(err, kErrorTypeException, "%s", errmsg); + } + + if (switched) { + restore_option_context(ctx, req_scope); + } } /// if 'all' == false: show changed options @@ -3940,33 +4056,35 @@ static void showoptions(bool all, int opt_flags) for (int run = 1; run <= 2 && !got_int; run++) { // collect the items in items[] int item_count = 0; - for (vimoption_T *p = &options[0]; p->fullname != NULL; p++) { + vimoption_T *opt; + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + opt = &options[opt_idx]; // apply :filter /pat/ - if (message_filtered(p->fullname)) { + if (message_filtered(opt->fullname)) { continue; } void *varp = NULL; if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) { - if (p->indir != PV_NONE) { - varp = get_varp_scope(p, opt_flags); + if (opt->indir != PV_NONE) { + varp = get_varp_scope(opt, opt_flags); } } else { - varp = get_varp(p); + varp = get_varp(opt); } - if (varp != NULL && (all || !optval_default(p, varp))) { + if (varp != NULL && (all || !optval_default(opt_idx, varp))) { int len; if (opt_flags & OPT_ONECOLUMN) { len = Columns; - } else if (p->flags & P_BOOL) { + } else if (option_has_type(opt_idx, kOptValTypeBoolean)) { len = 1; // a toggle option fits always } else { - option_value2string(p, opt_flags); - len = (int)strlen(p->fullname) + vim_strsize(NameBuff) + 1; + option_value2string(opt, opt_flags); + len = (int)strlen(opt->fullname) + vim_strsize(NameBuff) + 1; } if ((len <= INC - GAP && run == 1) || (len > INC - GAP && run == 2)) { - items[item_count++] = p; + items[item_count++] = opt; } } } @@ -4005,40 +4123,31 @@ static void showoptions(bool all, int opt_flags) } /// Return true if option "p" has its default value. -static int optval_default(vimoption_T *p, const void *varp) +static int optval_default(OptIndex opt_idx, void *varp) { - if (varp == NULL) { - return true; // hidden option is always at default - } - if (p->flags & P_NUM) { - return *(OptInt *)varp == (OptInt)(intptr_t)p->def_val; - } - if (p->flags & P_BOOL) { - return *(int *)varp == (int)(intptr_t)p->def_val; + vimoption_T *opt = &options[opt_idx]; + + // Hidden or immutable options always use their default value. + if (varp == NULL || opt->immutable) { + return true; } - // P_STRING - return strcmp(*(char **)varp, p->def_val) == 0; + + OptVal current_val = optval_from_varp(opt_idx, varp); + OptVal default_val = optval_from_varp(opt_idx, &opt->def_val); + + return optval_equal(current_val, default_val); } /// Send update to UIs with values of UI relevant options void ui_refresh_options(void) { - for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { uint32_t flags = options[opt_idx].flags; if (!(flags & P_UI_OPTION)) { continue; } String name = cstr_as_string(options[opt_idx].fullname); - void *varp = options[opt_idx].var; - Object value = OBJECT_INIT; - if (flags & P_BOOL) { - value = BOOLEAN_OBJ(*(int *)varp); - } else if (flags & P_NUM) { - value = INTEGER_OBJ(*(OptInt *)varp); - } else if (flags & P_STRING) { - // cstr_as_string handles NULL string - value = CSTR_AS_OBJ(*(char **)varp); - } + Object value = optval_as_object(optval_from_varp(opt_idx, options[opt_idx].var)); ui_call_option_set(name, value); } if (p_mouse != NULL) { @@ -4050,29 +4159,30 @@ void ui_refresh_options(void) /// must not be called with a hidden option! /// /// @param opt_flags OPT_LOCAL or OPT_GLOBAL -static void showoneopt(vimoption_T *p, int opt_flags) +static void showoneopt(vimoption_T *opt, int opt_flags) { int save_silent = silent_mode; silent_mode = false; - info_message = true; // use os_msg(), not os_errmsg() + info_message = true; // use stdout, not stderr - void *varp = get_varp_scope(p, opt_flags); + OptIndex opt_idx = get_opt_idx(opt); + void *varp = get_varp_scope(opt, opt_flags); // for 'modified' we also need to check if 'ff' or 'fenc' changed. - if ((p->flags & P_BOOL) && ((int *)varp == &curbuf->b_changed - ? !curbufIsChanged() : !*(int *)varp)) { + if (option_has_type(opt_idx, kOptValTypeBoolean) + && ((int *)varp == &curbuf->b_changed ? !curbufIsChanged() : !*(int *)varp)) { msg_puts("no"); - } else if ((p->flags & P_BOOL) && *(int *)varp < 0) { + } else if (option_has_type(opt_idx, kOptValTypeBoolean) && *(int *)varp < 0) { msg_puts("--"); } else { msg_puts(" "); } - msg_puts(p->fullname); - if (!(p->flags & P_BOOL)) { + msg_puts(opt->fullname); + if (!(option_has_type(opt_idx, kOptValTypeBoolean))) { msg_putchar('='); // put value string in NameBuff - option_value2string(p, opt_flags); + option_value2string(opt, opt_flags); msg_outtrans(NameBuff, 0); } @@ -4110,39 +4220,42 @@ int makeset(FILE *fd, int opt_flags, int local_only) // Do the loop over "options[]" twice: once for options with the // P_PRI_MKRC flag and once without. for (int pri = 1; pri >= 0; pri--) { - for (vimoption_T *p = &options[0]; p->fullname; p++) { - if (!(p->flags & P_NO_MKRC) - && ((pri == 1) == ((p->flags & P_PRI_MKRC) != 0))) { + vimoption_T *opt; + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + opt = &options[opt_idx]; + + if (!(opt->flags & P_NO_MKRC) + && ((pri == 1) == ((opt->flags & P_PRI_MKRC) != 0))) { // skip global option when only doing locals - if (p->indir == PV_NONE && !(opt_flags & OPT_GLOBAL)) { + if (opt->indir == PV_NONE && !(opt_flags & OPT_GLOBAL)) { continue; } // Do not store options like 'bufhidden' and 'syntax' in a vimrc // file, they are always buffer-specific. - if ((opt_flags & OPT_GLOBAL) && (p->flags & P_NOGLOB)) { + if ((opt_flags & OPT_GLOBAL) && (opt->flags & P_NOGLOB)) { continue; } - void *varp = get_varp_scope(p, opt_flags); // currently used value + void *varp = get_varp_scope(opt, opt_flags); // currently used value // Hidden options are never written. if (!varp) { continue; } // Global values are only written when not at the default value. - if ((opt_flags & OPT_GLOBAL) && optval_default(p, varp)) { + if ((opt_flags & OPT_GLOBAL) && optval_default(opt_idx, varp)) { continue; } if ((opt_flags & OPT_SKIPRTP) - && (p->var == &p_rtp || p->var == &p_pp)) { + && (opt->var == &p_rtp || opt->var == &p_pp)) { continue; } int round = 2; void *varp_local = NULL; // fresh value - if (p->indir != PV_NONE) { - if (p->var == VAR_WIN) { + if (opt->indir != PV_NONE) { + if (opt->var == VAR_WIN) { // skip window-local option when only doing globals if (!(opt_flags & OPT_LOCAL)) { continue; @@ -4150,8 +4263,8 @@ int makeset(FILE *fd, int opt_flags, int local_only) // When fresh value of window-local option is not at the // default, need to write it too. if (!(opt_flags & OPT_GLOBAL) && !local_only) { - void *varp_fresh = get_varp_scope(p, OPT_GLOBAL); // local value - if (!optval_default(p, varp_fresh)) { + void *varp_fresh = get_varp_scope(opt, OPT_GLOBAL); // local value + if (!optval_default(opt_idx, varp_fresh)) { round = 1; varp_local = varp; varp = varp_fresh; @@ -4170,28 +4283,28 @@ int makeset(FILE *fd, int opt_flags, int local_only) cmd = "setlocal"; } - if (p->flags & P_BOOL) { - if (put_setbool(fd, cmd, p->fullname, *(int *)varp) == FAIL) { + if (option_has_type(opt_idx, kOptValTypeBoolean)) { + if (put_setbool(fd, cmd, opt->fullname, *(int *)varp) == FAIL) { return FAIL; } - } else if (p->flags & P_NUM) { - if (put_setnum(fd, cmd, p->fullname, (OptInt *)varp) == FAIL) { + } else if (option_has_type(opt_idx, kOptValTypeNumber)) { + if (put_setnum(fd, cmd, opt->fullname, (OptInt *)varp) == FAIL) { return FAIL; } - } else { // P_STRING - int do_endif = false; + } else { // string + bool do_endif = false; // Don't set 'syntax' and 'filetype' again if the value is // already right, avoids reloading the syntax file. - if (p->indir == PV_SYN || p->indir == PV_FT) { - if (fprintf(fd, "if &%s != '%s'", p->fullname, + if (opt->indir == PV_SYN || opt->indir == PV_FT) { + if (fprintf(fd, "if &%s != '%s'", opt->fullname, *(char **)(varp)) < 0 || put_eol(fd) < 0) { return FAIL; } do_endif = true; } - if (put_setstring(fd, cmd, p->fullname, (char **)varp, p->flags) == FAIL) { + if (put_setstring(fd, cmd, opt->fullname, (char **)varp, opt->flags) == FAIL) { return FAIL; } if (do_endif) { @@ -4260,7 +4373,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char **valuep, uint64_ if (fprintf(fd, "%s %s+=", cmd, name) < 0) { goto fail; } - (void)copy_option_part(&p, part, size, ","); + copy_option_part(&p, part, size, ","); if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) { goto fail; } @@ -4402,7 +4515,7 @@ void *get_varp_scope(vimoption_T *p, int scope) /// Get pointer to option variable at 'opt_idx', depending on local or global /// scope. -void *get_option_varp_scope_from(int opt_idx, int scope, buf_T *buf, win_T *win) +void *get_option_varp_scope_from(OptIndex opt_idx, int scope, buf_T *buf, win_T *win) { return get_varp_scope_from(&(options[opt_idx]), scope, buf, win); } @@ -4587,6 +4700,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return &(buf->b_p_cfu); case PV_OFU: return &(buf->b_p_ofu); + case PV_URF: + return &(buf->b_p_urf); case PV_EOF: return &(buf->b_p_eof); case PV_EOL: @@ -4696,6 +4811,13 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return &(buf->b_p_wm); } +/// Get option index from option pointer +static inline OptIndex get_opt_idx(vimoption_T *opt) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + return (OptIndex)(opt - options); +} + /// Get pointer to option variable. static inline void *get_varp(vimoption_T *p) { @@ -4859,8 +4981,8 @@ void didset_window_options(win_T *wp, bool valid_cursor) check_colorcolumn(wp); briopt_check(wp); fill_culopt_flags(NULL, wp); - set_fillchars_option(wp, wp->w_p_fcs, true); - set_listchars_option(wp, wp->w_p_lcs, true); + set_chars_option(wp, wp->w_p_fcs, kFillchars, true, NULL, 0); + set_chars_option(wp, wp->w_p_lcs, kListchars, true, NULL, 0); parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl check_blending(wp); set_winbar_win(wp, false, valid_cursor); @@ -4869,19 +4991,19 @@ void didset_window_options(win_T *wp, bool valid_cursor) } /// Index into the options table for a buffer-local option enum. -static int buf_opt_idx[BV_COUNT]; +static OptIndex buf_opt_idx[BV_COUNT]; #define COPY_OPT_SCTX(buf, bv) buf->b_p_script_ctx[bv] = options[buf_opt_idx[bv]].last_set /// Initialize buf_opt_idx[] if not done already. static void init_buf_opt_idx(void) { - static int did_init_buf_opt_idx = false; + static bool did_init_buf_opt_idx = false; if (did_init_buf_opt_idx) { return; } did_init_buf_opt_idx = true; - for (int i = 0; options[i].fullname != NULL; i++) { + for (OptIndex i = 0; i < kOptIndexCount; i++) { if (options[i].indir & PV_BUF) { buf_opt_idx[options[i].indir & PV_MASK] = i; } @@ -4897,9 +5019,9 @@ static void init_buf_opt_idx(void) /// BCO_NOHELP Don't copy the values to a help buffer. void buf_copy_options(buf_T *buf, int flags) { - int should_copy = true; + bool should_copy = true; char *save_p_isk = NULL; // init for GCC - int did_isk = false; + bool did_isk = false; // Skip this when the option defaults have not been set yet. Happens when // main() allocates the first buffer. @@ -5007,6 +5129,8 @@ void buf_copy_options(buf_T *buf, int flags) set_buflocal_cfu_callback(buf); buf->b_p_ofu = xstrdup(p_ofu); COPY_OPT_SCTX(buf, BV_OFU); + buf->b_p_urf = xstrdup(p_urf); + COPY_OPT_SCTX(buf, BV_URF); set_buflocal_ofu_callback(buf); buf->b_p_tfu = xstrdup(p_tfu); COPY_OPT_SCTX(buf, BV_TFU); @@ -5017,7 +5141,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_vsts = xstrdup(p_vsts); COPY_OPT_SCTX(buf, BV_VSTS); if (p_vsts && p_vsts != empty_string_option) { - (void)tabstop_set(p_vsts, &buf->b_p_vsts_array); + tabstop_set(p_vsts, &buf->b_p_vsts_array); } else { buf->b_p_vsts_array = NULL; } @@ -5066,7 +5190,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_s.b_syn_isk = empty_string_option; buf->b_s.b_p_spc = xstrdup(p_spc); COPY_OPT_SCTX(buf, BV_SPC); - (void)compile_cap_prog(&buf->b_s); + compile_cap_prog(&buf->b_s); buf->b_s.b_p_spf = xstrdup(p_spf); COPY_OPT_SCTX(buf, BV_SPF); buf->b_s.b_p_spl = xstrdup(p_spl); @@ -5128,7 +5252,7 @@ void buf_copy_options(buf_T *buf, int flags) if (dont_do_help) { buf->b_p_isk = save_p_isk; if (p_vts && p_vts != empty_string_option && !buf->b_p_vts_array) { - (void)tabstop_set(p_vts, &buf->b_p_vts_array); + tabstop_set(p_vts, &buf->b_p_vts_array); } else { buf->b_p_vts_array = NULL; } @@ -5141,7 +5265,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_vts = xstrdup(p_vts); COPY_OPT_SCTX(buf, BV_VTS); if (p_vts && p_vts != empty_string_option && !buf->b_p_vts_array) { - (void)tabstop_set(p_vts, &buf->b_p_vts_array); + tabstop_set(p_vts, &buf->b_p_vts_array); } else { buf->b_p_vts_array = NULL; } @@ -5163,21 +5287,16 @@ void buf_copy_options(buf_T *buf, int flags) check_buf_options(buf); // make sure we don't have NULLs if (did_isk) { - (void)buf_init_chartab(buf, false); + buf_init_chartab(buf, false); } } /// Reset the 'modifiable' option and its default value. void reset_modifiable(void) { - int opt_idx; - curbuf->b_p_ma = false; p_ma = false; - opt_idx = findoption("ma"); - if (opt_idx >= 0) { - options[opt_idx].def_val = false; - } + options[kOptModifiable].def_val.boolean = false; } /// Set the global value for 'iminsert' to the local value. @@ -5192,7 +5311,7 @@ void set_imsearch_global(buf_T *buf) p_imsearch = buf->b_p_imsearch; } -static int expand_option_idx = -1; +static OptIndex expand_option_idx = kOptInvalid; static int expand_option_start_col = 0; static char expand_option_name[5] = { 't', '_', NUL, NUL, NUL }; static int expand_option_flags = 0; @@ -5242,8 +5361,8 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) char nextchar; uint32_t flags = 0; - int opt_idx = 0; - int is_term_option = false; + OptIndex opt_idx = 0; + bool is_term_option = false; if (*arg == '<') { while (*p != '>') { @@ -5282,13 +5401,13 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) return; } nextchar = *p; - opt_idx = findoption_len(arg, (size_t)(p - arg)); - if (opt_idx == -1 || options[opt_idx].var == NULL) { + opt_idx = find_option_len(arg, (size_t)(p - arg)); + if (opt_idx == kOptInvalid || options[opt_idx].var == NULL) { xp->xp_context = EXPAND_NOTHING; return; } flags = options[opt_idx].flags; - if (flags & P_BOOL) { + if (option_has_type(opt_idx, kOptValTypeBoolean)) { xp->xp_context = EXPAND_NOTHING; return; } @@ -5316,7 +5435,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) // Below are for handling expanding a specific option's value after the '=' or ':' if (is_term_option) { - expand_option_idx = -1; + expand_option_idx = kOptInvalid; } else { expand_option_idx = opt_idx; } @@ -5334,14 +5453,17 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) xp->xp_context = EXPAND_FILETYPE; return; } + if (options[opt_idx].var == &p_keymap) { + xp->xp_context = EXPAND_KEYMAP; + return; + } // Now pick. If the option has a custom expander, use that. Otherwise, just // fill with the existing option value. if (expand_option_subtract) { xp->xp_context = EXPAND_SETTING_SUBTRACT; return; - } else if (expand_option_idx >= 0 - && options[expand_option_idx].opt_expand_cb != NULL) { + } else if (expand_option_idx != kOptInvalid && options[expand_option_idx].opt_expand_cb != NULL) { xp->xp_context = EXPAND_STRING_SETTING; } else if (*xp->xp_pattern == NUL) { xp->xp_context = EXPAND_OLD_SETTING; @@ -5350,7 +5472,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) xp->xp_context = EXPAND_NOTHING; } - if (is_term_option || (flags & P_NUM)) { + if (is_term_option || option_has_type(opt_idx, kOptValTypeNumber)) { return; } @@ -5502,13 +5624,13 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM } } char *str; - for (size_t opt_idx = 0; (str = options[opt_idx].fullname) != NULL; - opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + str = options[opt_idx].fullname; if (options[opt_idx].var == NULL) { continue; } if (xp->xp_context == EXPAND_BOOL_SETTINGS - && !(options[opt_idx].flags & P_BOOL)) { + && !(option_has_type(opt_idx, kOptValTypeBoolean))) { continue; } @@ -5567,7 +5689,7 @@ static char *escape_option_str_cmdline(char *var) // The reverse is found at stropt_copy_value(). for (var = buf; *var != NUL; MB_PTR_ADV(var)) { if (var[0] == '\\' && var[1] == '\\' - && expand_option_idx >= 0 + && expand_option_idx != kOptInvalid && (options[expand_option_idx].flags & P_EXPAND) && vim_isfilec((uint8_t)var[2]) && (var[2] != '\\' || (var == buf && var[4] != '\\'))) { @@ -5586,12 +5708,12 @@ int ExpandOldSetting(int *numMatches, char ***matches) *numMatches = 0; *matches = xmalloc(sizeof(char *)); - // For a terminal key code expand_option_idx is < 0. - if (expand_option_idx < 0) { - expand_option_idx = findoption(expand_option_name); + // For a terminal key code expand_option_idx is kOptInvalid. + if (expand_option_idx == kOptInvalid) { + expand_option_idx = find_option(expand_option_name); } - if (expand_option_idx >= 0) { + if (expand_option_idx != kOptInvalid) { // Put string of option value in NameBuff. option_value2string(&options[expand_option_idx], expand_option_flags); var = NameBuff; @@ -5609,8 +5731,7 @@ int ExpandOldSetting(int *numMatches, char ***matches) /// Expansion handler for :set=/:set+= when the option has a custom expansion handler. int ExpandStringSetting(expand_T *xp, regmatch_T *regmatch, int *numMatches, char ***matches) { - if (expand_option_idx < 0 - || options[expand_option_idx].opt_expand_cb == NULL) { + if (expand_option_idx == kOptInvalid || options[expand_option_idx].opt_expand_cb == NULL) { // Not supposed to reach this. This function is only for options with // custom expansion callbacks. return FAIL; @@ -5642,7 +5763,7 @@ int ExpandStringSetting(expand_T *xp, regmatch_T *regmatch, int *numMatches, cha /// Expansion handler for :set-= int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, char ***matches) { - if (expand_option_idx < 0) { + if (expand_option_idx == kOptInvalid) { // term option return ExpandOldSetting(numMatches, matches); } @@ -5653,7 +5774,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c uint32_t option_flags = options[expand_option_idx].flags; - if (option_flags & P_NUM) { + if (option_has_type(expand_option_idx, kOptValTypeNumber)) { return ExpandOldSetting(numMatches, matches); } else if (option_flags & P_COMMA) { // Split the option by comma, then present each option to the user if @@ -5748,11 +5869,13 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c /// NameBuff[]. Must not be called with a hidden option! /// /// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL -static void option_value2string(vimoption_T *opp, int scope) +/// +/// TODO(famiu): Replace this with optval_to_cstr() if possible. +static void option_value2string(vimoption_T *opt, int scope) { - void *varp = get_varp_scope(opp, scope); + void *varp = get_varp_scope(opt, scope); - if (opp->flags & P_NUM) { + if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) { OptInt wc = 0; if (wc_use_keyname(varp, &wc)) { @@ -5765,11 +5888,11 @@ static void option_value2string(vimoption_T *opp, int scope) "%" PRId64, (int64_t)(*(OptInt *)varp)); } - } else { // P_STRING + } else { // string varp = *(char **)(varp); if (varp == NULL) { // Just in case. NameBuff[0] = NUL; - } else if (opp->flags & P_EXPAND) { + } else if (opt->flags & P_EXPAND) { home_replace(NULL, varp, NameBuff, MAXPATHL, false); } else { xstrlcpy(NameBuff, varp, MAXPATHL); @@ -5822,35 +5945,24 @@ void vimrc_found(char *fname, char *envname) } } -/// Check whether global option has been set +/// Check whether global option has been set. /// /// @param[in] name Option name. /// -/// @return True if it was set. -bool option_was_set(const char *name) +/// @return True if option was set. +bool option_was_set(OptIndex opt_idx) { - int idx; - - idx = findoption(name); - if (idx < 0) { // Unknown option. - return false; - } else if (options[idx].flags & P_WAS_SET) { - return true; - } - return false; + assert(opt_idx != kOptInvalid); + return options[opt_idx].flags & P_WAS_SET; } /// Reset the flag indicating option "name" was set. /// /// @param[in] name Option name. -void reset_option_was_set(const char *name) +void reset_option_was_set(OptIndex opt_idx) { - const int idx = findoption(name); - if (idx < 0) { - return; - } - - options[idx].flags &= ~P_WAS_SET; + assert(opt_idx != kOptInvalid); + options[opt_idx].flags &= ~P_WAS_SET; } /// fill_culopt_flags() -- called when 'culopt' changes value @@ -5949,17 +6061,14 @@ int option_set_callback_func(char *optval, Callback *optcb) return OK; } -static void didset_options_sctx(int opt_flags, char **buf) +static void didset_options_sctx(int opt_flags, int *buf) { for (int i = 0;; i++) { - if (buf[i] == NULL) { + if (buf[i] == kOptInvalid) { break; } - int idx = findoption(buf[i]); - if (idx >= 0) { - set_option_sctx_idx(idx, opt_flags, current_sctx); - } + set_option_sctx(buf[i], opt_flags, current_sctx); } } @@ -6099,7 +6208,7 @@ void set_fileformat(int eol_style, int opt_flags) // p is NULL if "eol_style" is EOL_UNKNOWN. if (p != NULL) { - set_string_option_direct("ff", -1, p, OPT_FREE | opt_flags, 0); + set_string_option_direct(kOptFileformat, p, opt_flags, 0); } // This may cause the buffer to become (un)modified. @@ -6170,43 +6279,22 @@ bool fish_like_shell(void) return strstr(path_tail(p_sh), "fish") != NULL; } -/// Return the number of requested sign columns, based on current -/// buffer signs and on user configuration. -int win_signcol_count(win_T *wp) -{ - if (wp->w_minscwidth <= SCL_NO) { - return 0; - } - - int needed_signcols = buf_signcols(wp->w_buffer, wp->w_maxscwidth); - int ret = MAX(wp->w_minscwidth, MIN(wp->w_maxscwidth, needed_signcols)); - assert(ret <= SIGN_SHOW_MAX); - return ret; -} - /// Get window or buffer local options dict_T *get_winbuf_options(const int bufopt) FUNC_ATTR_WARN_UNUSED_RESULT { dict_T *const d = tv_dict_alloc(); - for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) { - struct vimoption *opt = &options[opt_idx]; + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + vimoption_T *opt = &options[opt_idx]; if ((bufopt && (opt->indir & PV_BUF)) || (!bufopt && (opt->indir & PV_WIN))) { void *varp = get_varp(opt); if (varp != NULL) { - if (opt->flags & P_STRING) { - tv_dict_add_str(d, opt->fullname, strlen(opt->fullname), - *(const char **)varp); - } else if (opt->flags & P_NUM) { - tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), - *(OptInt *)varp); - } else { - tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *(int *)varp); - } + typval_T opt_tv = optval_as_tv(optval_from_varp(opt_idx, varp), true); + tv_dict_add_tv(d, opt->fullname, strlen(opt->fullname), &opt_tv); } } } @@ -6232,32 +6320,33 @@ int get_sidescrolloff_value(win_T *wp) return (int)(wp->w_p_siso < 0 ? p_siso : wp->w_p_siso); } -Dictionary get_vimoption(String name, int scope, buf_T *buf, win_T *win, Error *err) +Dictionary get_vimoption(String name, int scope, buf_T *buf, win_T *win, Arena *arena, Error *err) { - int opt_idx = findoption_len(name.data, name.size); - VALIDATE_S(opt_idx >= 0, "option (not found)", name.data, { + OptIndex opt_idx = find_option_len(name.data, name.size); + VALIDATE_S(opt_idx != kOptInvalid, "option (not found)", name.data, { return (Dictionary)ARRAY_DICT_INIT; }); - return vimoption2dict(&options[opt_idx], scope, buf, win); + return vimoption2dict(&options[opt_idx], scope, buf, win, arena); } -Dictionary get_all_vimoptions(void) +Dictionary get_all_vimoptions(Arena *arena) { - Dictionary retval = ARRAY_DICT_INIT; - for (size_t i = 0; options[i].fullname != NULL; i++) { - Dictionary opt_dict = vimoption2dict(&options[i], OPT_GLOBAL, curbuf, curwin); - PUT(retval, options[i].fullname, DICTIONARY_OBJ(opt_dict)); + Dictionary retval = arena_dict(arena, kOptIndexCount); + for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + Dictionary opt_dict = vimoption2dict(&options[opt_idx], OPT_GLOBAL, curbuf, curwin, arena); + PUT_C(retval, options[opt_idx].fullname, DICTIONARY_OBJ(opt_dict)); } return retval; } -static Dictionary vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *win) +static Dictionary vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *win, + Arena *arena) { - Dictionary dict = ARRAY_DICT_INIT; + Dictionary dict = arena_dict(arena, 13); - PUT(dict, "name", CSTR_TO_OBJ(opt->fullname)); - PUT(dict, "shortname", CSTR_TO_OBJ(opt->shortname)); + PUT_C(dict, "name", CSTR_AS_OBJ(opt->fullname)); + PUT_C(dict, "shortname", CSTR_AS_OBJ(opt->shortname)); const char *scope; if (opt->indir & PV_BUF) { @@ -6268,14 +6357,14 @@ static Dictionary vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, wi scope = "global"; } - PUT(dict, "scope", CSTR_TO_OBJ(scope)); + PUT_C(dict, "scope", CSTR_AS_OBJ(scope)); // welcome to the jungle - PUT(dict, "global_local", BOOLEAN_OBJ(opt->indir & PV_BOTH)); - PUT(dict, "commalist", BOOLEAN_OBJ(opt->flags & P_COMMA)); - PUT(dict, "flaglist", BOOLEAN_OBJ(opt->flags & P_FLAGLIST)); + PUT_C(dict, "global_local", BOOLEAN_OBJ(opt->indir & PV_BOTH)); + PUT_C(dict, "commalist", BOOLEAN_OBJ(opt->flags & P_COMMA)); + PUT_C(dict, "flaglist", BOOLEAN_OBJ(opt->flags & P_FLAGLIST)); - PUT(dict, "was_set", BOOLEAN_OBJ(opt->flags & P_WAS_SET)); + PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & P_WAS_SET)); LastSet last_set = { .channel_id = 0 }; if (req_scope == OPT_GLOBAL) { @@ -6293,29 +6382,37 @@ static Dictionary vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, wi } } - PUT(dict, "last_set_sid", INTEGER_OBJ(last_set.script_ctx.sc_sid)); - PUT(dict, "last_set_linenr", INTEGER_OBJ(last_set.script_ctx.sc_lnum)); - PUT(dict, "last_set_chan", INTEGER_OBJ((int64_t)last_set.channel_id)); + PUT_C(dict, "last_set_sid", INTEGER_OBJ(last_set.script_ctx.sc_sid)); + PUT_C(dict, "last_set_linenr", INTEGER_OBJ(last_set.script_ctx.sc_lnum)); + PUT_C(dict, "last_set_chan", INTEGER_OBJ((int64_t)last_set.channel_id)); - const char *type; - Object def; // TODO(bfredl): do you even nocp? - char *def_val = opt->def_val; - if (opt->flags & P_STRING) { - type = "string"; - def = CSTR_TO_OBJ(def_val ? def_val : ""); - } else if (opt->flags & P_NUM) { - type = "number"; - def = INTEGER_OBJ((Integer)(intptr_t)def_val); - } else if (opt->flags & P_BOOL) { - type = "boolean"; - def = BOOLEAN_OBJ((intptr_t)def_val); - } else { - type = ""; def = NIL; - } - PUT(dict, "type", CSTR_TO_OBJ(type)); - PUT(dict, "default", def); - PUT(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & P_NODUP))); + OptVal def = optval_from_varp(get_opt_idx(opt), &opt->def_val); + + PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_get_name(def.type))); + PUT_C(dict, "default", optval_as_object(def)); + PUT_C(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & P_NODUP))); return dict; } + +/// Check if option is multitype (supports multiple types). +static bool option_is_multitype(OptIndex opt_idx) +{ + const OptTypeFlags type_flags = get_option(opt_idx)->type_flags; + assert(type_flags != 0); + return !is_power_of_two(type_flags); +} + +/// Check if option supports a specific type. +bool option_has_type(OptIndex opt_idx, OptValType type) +{ + // Ensure that type flags variable can hold all types. + STATIC_ASSERT(kOptValTypeSize <= sizeof(OptTypeFlags) * 8, + "Option type_flags cannot fit all option types"); + // Ensure that the type is valid before accessing type_flags. + assert(type > kOptValTypeNil && type < kOptValTypeSize); + // Bitshift 1 by the value of type to get the type's corresponding flag, and check if it's set in + // the type_flags bit field. + return get_option(opt_idx)->type_flags & (1 << type); +} diff --git a/src/nvim/option.h b/src/nvim/option.h index ebf8e0417d..7cf880b19b 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -1,5 +1,6 @@ #pragma once +#include <stdbool.h> #include <stdint.h> #include <stdio.h> // IWYU pragma: keep @@ -8,7 +9,8 @@ #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep -#include "nvim/option_defs.h" // IWYU pragma: export +#include "nvim/macros_defs.h" +#include "nvim/option_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep /// The options that are local to a window or buffer have "indir" set to one of @@ -37,18 +39,17 @@ typedef enum { // buffers. Indicate this by setting "var" to VAR_WIN. #define VAR_WIN ((char *)-1) -typedef struct vimoption { - char *fullname; ///< full option name - char *shortname; ///< permissible abbreviation - uint32_t flags; ///< see above - void *var; ///< global option: pointer to variable; - ///< window-local option: VAR_WIN; - ///< buffer-local option: global value - idopt_T indir; ///< global option: PV_NONE; - ///< local option: indirect option index - ///< callback function to invoke after an option is modified to validate and - ///< apply the new value. - bool immutable; ///< option value cannot be changed from the default value. +typedef struct { + char *fullname; ///< full option name + char *shortname; ///< permissible abbreviation + uint32_t flags; ///< see above + OptTypeFlags type_flags; ///< option type flags, see OptValType + void *var; ///< global option: pointer to variable; + ///< window-local option: VAR_WIN; + ///< buffer-local option: global value + idopt_T indir; ///< global option: PV_NONE; + ///< local option: indirect option index + bool immutable; ///< option value cannot be changed from the default value. /// callback function to invoke after an option is modified to validate and /// apply the new value. @@ -59,7 +60,12 @@ typedef struct vimoption { opt_expand_cb_T opt_expand_cb; // TODO(famiu): Use OptVal for def_val. - void *def_val; ///< default values for variable (neovim!!) + union { + int boolean; + OptInt number; + char *string; + } def_val; ///< default value for variable + LastSet last_set; ///< script in which the option was last set } vimoption_T; @@ -75,28 +81,39 @@ enum { /// When OPT_GLOBAL and OPT_LOCAL are both missing, set both local and global /// values, get local value. typedef enum { - // TODO(famiu): See if `OPT_FREE` is really necessary and remove it if not. - OPT_FREE = 0x01, ///< Free old value if it was allocated. - OPT_GLOBAL = 0x02, ///< Use global value. - OPT_LOCAL = 0x04, ///< Use local value. - OPT_MODELINE = 0x08, ///< Option in modeline. - OPT_WINONLY = 0x10, ///< Only set window-local options. - OPT_NOWIN = 0x20, ///< Don’t set window-local options. - OPT_ONECOLUMN = 0x40, ///< list options one per line - OPT_NO_REDRAW = 0x80, ///< ignore redraw flags on option - OPT_SKIPRTP = 0x100, ///< "skiprtp" in 'sessionoptions' -} OptionFlags; - -/// Return value from get_option_value_strict + OPT_GLOBAL = 0x01, ///< Use global value. + OPT_LOCAL = 0x02, ///< Use local value. + OPT_MODELINE = 0x04, ///< Option in modeline. + OPT_WINONLY = 0x08, ///< Only set window-local options. + OPT_NOWIN = 0x10, ///< Don’t set window-local options. + OPT_ONECOLUMN = 0x20, ///< list options one per line + OPT_NO_REDRAW = 0x40, ///< ignore redraw flags on option + OPT_SKIPRTP = 0x80, ///< "skiprtp" in 'sessionoptions' +} OptionSetFlags; + +/// Return value from get_option_attrs(). enum { - SOPT_BOOL = 0x01, ///< Boolean option - SOPT_NUM = 0x02, ///< Number option - SOPT_STRING = 0x04, ///< String option - SOPT_GLOBAL = 0x08, ///< Option has global value - SOPT_WIN = 0x10, ///< Option has window-local value - SOPT_BUF = 0x20, ///< Option has buffer-local value + SOPT_GLOBAL = 0x01, ///< Option has global value + SOPT_WIN = 0x02, ///< Option has window-local value + SOPT_BUF = 0x04, ///< Option has buffer-local value }; +/// Get name of OptValType as a string. +static inline const char *optval_type_get_name(const OptValType type) +{ + switch (type) { + case kOptValTypeNil: + return "nil"; + case kOptValTypeBoolean: + return "boolean"; + case kOptValTypeNumber: + return "number"; + case kOptValTypeString: + return "string"; + } + UNREACHABLE; +} + // OptVal helper macros. #define NIL_OPTVAL ((OptVal) { .type = kOptValTypeNil }) #define BOOLEAN_OPTVAL(b) ((OptVal) { .type = kOptValTypeBoolean, .data.boolean = b }) diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index b2e8081a08..ae9ccd371c 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -6,16 +6,26 @@ #include "nvim/api/private/defs.h" #include "nvim/cmdexpand_defs.h" #include "nvim/regexp_defs.h" -#include "nvim/types_defs.h" -/// Option value type +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "options_enum.generated.h" +#endif + +/// Option value type. +/// These types are also used as type flags by using the type value as an index for the type_flags +/// bit field (@see option_has_type()). typedef enum { - kOptValTypeNil = 0, + kOptValTypeNil = -1, // Make sure Nil can't be bitshifted and used as an option type flag. kOptValTypeBoolean, kOptValTypeNumber, kOptValTypeString, } OptValType; +/// Always update this whenever a new option type is added. +#define kOptValTypeSize (kOptValTypeString + 1) + +typedef uint32_t OptTypeFlags; + typedef union { // boolean options are actually tri-states because they have a third "None" value. TriState boolean; @@ -43,10 +53,11 @@ typedef struct { /// Pointer to the option variable. The variable can be an OptInt (numeric /// option), an int (boolean option) or a char pointer (string option). void *os_varp; - int os_idx; + OptIndex os_idx; int os_flags; /// Old value of the option. + /// TODO(famiu): Convert `os_oldval` and `os_newval` to `OptVal` to accommodate multitype options. OptValData os_oldval; /// New value of the option. OptValData os_newval; @@ -66,6 +77,7 @@ typedef struct { /// is parameterized, then the "os_errbuf" buffer is used to store the error /// message (when it is not NULL). char *os_errbuf; + /// length of the error buffer size_t os_errbuflen; void *os_win; diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index b0e9ff9434..69d8f0833d 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -1,60 +1,56 @@ #pragma once #include "nvim/macros_defs.h" +#include "nvim/os/os_defs.h" +#include "nvim/sign_defs.h" #include "nvim/types_defs.h" // option_vars.h: definition of global variables for settable options // Option Flags -#define P_BOOL 0x01U ///< the option is boolean -#define P_NUM 0x02U ///< the option is numeric -#define P_STRING 0x04U ///< the option is a string -#define P_ALLOCED 0x08U ///< the string option is in allocated memory, +#define P_ALLOCED 0x01U ///< the option is in allocated memory, ///< must use free_string_option() when ///< assigning new value. Not set if default is ///< the same. -#define P_EXPAND 0x10U ///< environment expansion. NOTE: P_EXPAND can +#define P_EXPAND 0x02U ///< environment expansion. NOTE: P_EXPAND can ///< never be used for local or hidden options -#define P_NO_DEF_EXP 0x20U ///< do not expand default value -#define P_NODEFAULT 0x40U ///< don't set to default value -#define P_DEF_ALLOCED 0x80U ///< default value is in allocated memory, must +#define P_NO_DEF_EXP 0x04U ///< do not expand default value +#define P_NODEFAULT 0x08U ///< don't set to default value +#define P_DEF_ALLOCED 0x10U ///< default value is in allocated memory, must ///< use free() when assigning new value -#define P_WAS_SET 0x100U ///< option has been set/reset -#define P_NO_MKRC 0x200U ///< don't include in :mkvimrc output +#define P_WAS_SET 0x20U ///< option has been set/reset +#define P_NO_MKRC 0x40U ///< don't include in :mkvimrc output // when option changed, what to display: -#define P_UI_OPTION 0x400U ///< send option to remote UI -#define P_RTABL 0x800U ///< redraw tabline -#define P_RSTAT 0x1000U ///< redraw status lines -#define P_RWIN 0x2000U ///< redraw current window and recompute text -#define P_RBUF 0x4000U ///< redraw current buffer and recompute text -#define P_RALL 0x6000U ///< redraw all windows -#define P_RCLR 0x7000U ///< clear and redraw all - -#define P_COMMA 0x8000U ///< comma separated list -#define P_ONECOMMA 0x18000U ///< P_COMMA and cannot have two consecutive +#define P_UI_OPTION 0x80U ///< send option to remote UI +#define P_RTABL 0x100U ///< redraw tabline +#define P_RSTAT 0x200U ///< redraw status lines +#define P_RWIN 0x400U ///< redraw current window and recompute text +#define P_RBUF 0x800U ///< redraw current buffer and recompute text +#define P_RALL 0xC00U ///< redraw all windows and recompute text +#define P_RCLR 0xE00U ///< clear and redraw all and recompute text + +#define P_COMMA 0x1000U ///< comma separated list +#define P_ONECOMMA 0x3000U ///< P_COMMA and cannot have two consecutive ///< commas -#define P_NODUP 0x20000U ///< don't allow duplicate strings -#define P_FLAGLIST 0x40000U ///< list of single-char flags - -#define P_SECURE 0x80000U ///< cannot change in modeline or secure mode -#define P_GETTEXT 0x100000U ///< expand default value with _() -#define P_NOGLOB 0x200000U ///< do not use local value for global vimrc -#define P_NFNAME 0x400000U ///< only normal file name chars allowed -#define P_INSECURE 0x800000U ///< option was set from a modeline -#define P_PRI_MKRC 0x1000000U ///< priority for :mkvimrc (setting option +#define P_NODUP 0x4000U ///< don't allow duplicate strings +#define P_FLAGLIST 0x8000U ///< list of single-char flags + +#define P_SECURE 0x10000U ///< cannot change in modeline or secure mode +#define P_GETTEXT 0x20000U ///< expand default value with _() +#define P_NOGLOB 0x40000U ///< do not use local value for global vimrc +#define P_NFNAME 0x80000U ///< only normal file name chars allowed +#define P_INSECURE 0x100000U ///< option was set from a modeline +#define P_PRI_MKRC 0x200000U ///< priority for :mkvimrc (setting option ///< has side effects) -#define P_NO_ML 0x2000000U ///< not allowed in modeline -#define P_CURSWANT 0x4000000U ///< update curswant required; not needed +#define P_NO_ML 0x400000U ///< not allowed in modeline +#define P_CURSWANT 0x800000U ///< update curswant required; not needed ///< when there is a redraw flag -#define P_NDNAME 0x8000000U ///< only normal dir name chars allowed -#define P_RWINONLY 0x10000000U ///< only redraw current window -#define P_MLE 0x20000000U ///< under control of 'modelineexpr' -#define P_FUNC 0x40000000U ///< accept a function reference or a lambda -#define P_COLON 0x80000000U ///< values use colons to create sublists -// Warning: Currently we have used all 32 bits for option flags, and adding more -// flags will overflow it. Adding another flag will need to change how -// it's stored first. +#define P_NDNAME 0x1000000U ///< only normal dir name chars allowed +#define P_HLONLY 0x2000000U ///< option only changes highlight, not text +#define P_MLE 0x4000000U ///< under control of 'modelineexpr' +#define P_FUNC 0x8000000U ///< accept a function reference or a lambda +#define P_COLON 0x10000000U ///< values use colons to create sublists #define HIGHLIGHT_INIT \ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \ @@ -157,7 +153,6 @@ #define CPO_NUMCOL 'n' // 'number' column also used for text #define CPO_LINEOFF 'o' #define CPO_OVERNEW 'O' // silently overwrite new file -#define CPO_LISP 'p' // 'lisp' indenting #define CPO_FNAMEAPP 'P' // set file name for ":w >>file" #define CPO_JOINCOL 'q' // with "3J" use column after first join #define CPO_REDO 'r' @@ -223,7 +218,7 @@ enum { SHM_INTRO = 'I', ///< Intro messages. SHM_COMPLETIONMENU = 'c', ///< Completion menu messages. SHM_COMPLETIONSCAN = 'C', ///< Completion scanning messages. - SHM_RECORDING = 'q', ///< Short recording message. + SHM_RECORDING = 'q', ///< No recording message. SHM_FILEINFO = 'F', ///< No file info messages. SHM_SEARCHCOUNT = 'S', ///< No search stats: '[1/10]' }; @@ -733,6 +728,7 @@ EXTERN char *p_udir; ///< 'undodir' EXTERN int p_udf; ///< 'undofile' EXTERN OptInt p_ul; ///< 'undolevels' EXTERN OptInt p_ur; ///< 'undoreload' +EXTERN char* p_urf; ///< 'userregfunction' EXTERN OptInt p_uc; ///< 'updatecount' EXTERN OptInt p_ut; ///< 'updatetime' EXTERN char *p_shada; ///< 'shada' @@ -784,163 +780,18 @@ EXTERN int p_wa; ///< 'writeany' EXTERN int p_wb; ///< 'writebackup' EXTERN OptInt p_wd; ///< 'writedelay' EXTERN int p_cdh; ///< 'cdhome' - -/// "indir" values for buffer-local options. -/// These need to be defined globally, so that the BV_COUNT can be used with -/// b_p_script_stx[]. -enum { - BV_AI = 0, - BV_AR, - BV_BH, - BV_BKC, - BV_BT, - BV_EFM, - BV_GP, - BV_MP, - BV_BIN, - BV_BL, - BV_BOMB, - BV_CHANNEL, - BV_CI, - BV_CIN, - BV_CINK, - BV_CINO, - BV_CINW, - BV_CINSD, - BV_CM, - BV_CMS, - BV_COM, - BV_CPT, - BV_DICT, - BV_TSR, - BV_CSL, - BV_CFU, - BV_DEF, - BV_INC, - BV_EOF, - BV_EOL, - BV_FIXEOL, - BV_EP, - BV_ET, - BV_FENC, - BV_FP, - BV_BEXPR, - BV_FEX, - BV_FF, - BV_FLP, - BV_FO, - BV_FT, - BV_IMI, - BV_IMS, - BV_INDE, - BV_INDK, - BV_INEX, - BV_INF, - BV_ISK, - BV_KMAP, - BV_KP, - BV_LISP, - BV_LOP, - BV_LW, - BV_MENC, - BV_MA, - BV_ML, - BV_MOD, - BV_MPS, - BV_NF, - BV_OFU, - BV_PATH, - BV_PI, - BV_QE, - BV_RO, - BV_SCBK, - BV_SI, - BV_SMC, - BV_SYN, - BV_SPC, - BV_SPF, - BV_SPL, - BV_SPO, - BV_STS, - BV_SUA, - BV_SW, - BV_SWF, - BV_TFU, - BV_TSRFU, - BV_TAGS, - BV_TC, - BV_TS, - BV_TW, - BV_TX, - BV_UDF, - BV_UL, - BV_WM, - BV_VSTS, - BV_VTS, - BV_COUNT, // must be the last one -}; - -/// "indir" values for window-local options. -/// These need to be defined globally, so that the WV_COUNT can be used in the -/// window structure. -enum { - WV_LIST = 0, - WV_ARAB, - WV_COCU, - WV_COLE, - WV_CRBIND, - WV_BRI, - WV_BRIOPT, - WV_DIFF, - WV_FDC, - WV_FEN, - WV_FDI, - WV_FDL, - WV_FDM, - WV_FML, - WV_FDN, - WV_FDE, - WV_FDT, - WV_FMR, - WV_LBR, - WV_NU, - WV_RNU, - WV_VE, - WV_NUW, - WV_PVW, - WV_RL, - WV_RLC, - WV_SCBIND, - WV_SCROLL, - WV_SMS, - WV_SISO, - WV_SO, - WV_SPELL, - WV_CUC, - WV_CUL, - WV_CULOPT, - WV_CC, - WV_SBR, - WV_STC, - WV_STL, - WV_WFH, - WV_WFW, - WV_WRAP, - WV_SCL, - WV_WINHL, - WV_LCS, - WV_FCS, - WV_WINBL, - WV_WBR, - WV_COUNT, // must be the last one -}; - + /// // Value for b_p_ul indicating the global value must be used. #define NO_LOCAL_UNDOLEVEL (-123456) +#define ERR_BUFLEN 80 + #define SB_MAX 100000 // Maximum 'scrollback' value. -#define MAX_NUMBERWIDTH 20 // used for 'numberwidth' and 'statuscolumn' +#define MAX_NUMBERWIDTH 20 // used for 'numberwidth' + +// Maximum 'statuscolumn' width: number + sign + fold columns +#define MAX_STCWIDTH MAX_NUMBERWIDTH + SIGN_SHOW_MAX * SIGN_WIDTH + 9 #define TABSTOP_MAX 9999 diff --git a/src/nvim/options.lua b/src/nvim/options.lua index daaf73d241..632732d7b7 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2,10 +2,11 @@ --- @field full_name string --- @field desc? string --- @field abbreviation? string +--- @field alias? string|string[] --- @field short_desc? string|fun(): string --- @field varname? string --- @field pv_name? string ---- @field type 'bool'|'number'|'string' +--- @field type 'boolean'|'number'|'string' --- @field immutable? boolean --- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma' --- @field scope vim.option_scope[] @@ -44,10 +45,10 @@ --- |'statuslines' --- |'tabline' --- |'current_window' ---- |'current_window_only' --- |'current_buffer' --- |'all_windows' --- |'curswant' +--- |'highlight_only' --- |'ui_option' --- @param s string @@ -60,7 +61,7 @@ end --- @return fun(): string local function macros(s) return function() - return s + return '.string=' .. s end end @@ -68,7 +69,7 @@ end --- @return fun(): string local function imacros(s) return function() - return '(intptr_t)' .. s + return '.number=' .. s end end @@ -84,6 +85,7 @@ end return { cstr = cstr, --- @type vim.option_meta[] + --- The order of the options MUST be alphabetic for ":set all". options = { { abbreviation = 'al', @@ -94,56 +96,6 @@ return { type = 'number', }, { - abbreviation = 'arab', - cb = 'did_set_arabic', - defaults = { if_true = false }, - desc = [=[ - This option can be set to start editing Arabic text. - Setting this option will: - - Set the 'rightleft' option, unless 'termbidi' is set. - - Set the 'arabicshape' option, unless 'termbidi' is set. - - Set the 'keymap' option to "arabic"; in Insert mode CTRL-^ toggles - between typing English and Arabic key mapping. - - Set the 'delcombine' option - - Resetting this option will: - - Reset the 'rightleft' option. - - Disable the use of 'keymap' (without changing its value). - Note that 'arabicshape' and 'delcombine' are not reset (it is a global - option). - Also see |arabic.txt|. - ]=], - full_name = 'arabic', - redraw = { 'curswant' }, - scope = { 'window' }, - short_desc = N_('Arabic as a default second language'), - type = 'bool', - }, - { - abbreviation = 'arshape', - defaults = { if_true = true }, - desc = [=[ - When on and 'termbidi' is off, the required visual character - corrections that need to take place for displaying the Arabic language - take effect. Shaping, in essence, gets enabled; the term is a broad - one which encompasses: - a) the changing/morphing of characters based on their location - within a word (initial, medial, final and stand-alone). - b) the enabling of the ability to compose characters - c) the enabling of the required combining of some characters - When disabled the display shows each character's true stand-alone - form. - Arabic is a complex language which requires other settings, for - further details see |arabic.txt|. - ]=], - full_name = 'arabicshape', - redraw = { 'all_windows', 'ui_option' }, - scope = { 'global' }, - short_desc = N_('do shaping for Arabic characters'), - type = 'bool', - varname = 'p_arshape', - }, - { abbreviation = 'ari', defaults = { if_true = false }, desc = [=[ @@ -155,7 +107,7 @@ return { full_name = 'allowrevins', scope = { 'global' }, short_desc = N_('allow CTRL-_ in Insert and Command-line mode'), - type = 'bool', + type = 'boolean', varname = 'p_ari', }, { @@ -203,6 +155,56 @@ return { varname = 'p_ambw', }, { + abbreviation = 'arab', + cb = 'did_set_arabic', + defaults = { if_true = false }, + desc = [=[ + This option can be set to start editing Arabic text. + Setting this option will: + - Set the 'rightleft' option, unless 'termbidi' is set. + - Set the 'arabicshape' option, unless 'termbidi' is set. + - Set the 'keymap' option to "arabic"; in Insert mode CTRL-^ toggles + between typing English and Arabic key mapping. + - Set the 'delcombine' option + + Resetting this option will: + - Reset the 'rightleft' option. + - Disable the use of 'keymap' (without changing its value). + Note that 'arabicshape' and 'delcombine' are not reset (it is a global + option). + Also see |arabic.txt|. + ]=], + full_name = 'arabic', + redraw = { 'curswant' }, + scope = { 'window' }, + short_desc = N_('Arabic as a default second language'), + type = 'boolean', + }, + { + abbreviation = 'arshape', + defaults = { if_true = true }, + desc = [=[ + When on and 'termbidi' is off, the required visual character + corrections that need to take place for displaying the Arabic language + take effect. Shaping, in essence, gets enabled; the term is a broad + one which encompasses: + a) the changing/morphing of characters based on their location + within a word (initial, medial, final and stand-alone). + b) the enabling of the ability to compose characters + c) the enabling of the required combining of some characters + When disabled the display shows each character's true stand-alone + form. + Arabic is a complex language which requires other settings, for + further details see |arabic.txt|. + ]=], + full_name = 'arabicshape', + redraw = { 'all_windows', 'ui_option' }, + scope = { 'global' }, + short_desc = N_('do shaping for Arabic characters'), + type = 'boolean', + varname = 'p_arshape', + }, + { abbreviation = 'acd', cb = 'did_set_autochdir', defaults = { if_true = false }, @@ -217,7 +219,7 @@ return { full_name = 'autochdir', scope = { 'global' }, short_desc = N_('change directory to the file in the current window'), - type = 'bool', + type = 'boolean', varname = 'p_acd', }, { @@ -239,7 +241,7 @@ return { full_name = 'autoindent', scope = { 'buffer' }, short_desc = N_('take indent for new line from previous line'), - type = 'bool', + type = 'boolean', varname = 'p_ai', }, { @@ -252,14 +254,14 @@ return { from before it was deleted. When it appears again then it is read. |timestamp| If this option has a local value, use this command to switch back to - using the global value: > - :set autoread< + using the global value: >vim + set autoread< < ]=], full_name = 'autoread', scope = { 'global', 'buffer' }, short_desc = N_('autom. read file when changed outside of Vim'), - type = 'bool', + type = 'boolean', varname = 'p_ar', }, { @@ -284,7 +286,7 @@ return { full_name = 'autowrite', scope = { 'global' }, short_desc = N_('automatically write file if changed'), - type = 'bool', + type = 'boolean', varname = 'p_aw', }, { @@ -299,7 +301,7 @@ return { full_name = 'autowriteall', scope = { 'global' }, short_desc = N_("as 'autowrite', but works with more commands"), - type = 'bool', + type = 'boolean', varname = 'p_awa', }, { @@ -316,23 +318,19 @@ return { See |:hi-normal| if you want to set the background color explicitly. *g:colors_name* When a color scheme is loaded (the "g:colors_name" variable is set) - setting 'background' will cause the color scheme to be reloaded. If + changing 'background' will cause the color scheme to be reloaded. If the color scheme adjusts to the value of 'background' this will work. However, if the color scheme sets 'background' itself the effect may be undone. First delete the "g:colors_name" variable when needed. Normally this option would be set in the vimrc file. Possibly - depending on the terminal name. Example: > - :if $TERM ==# "xterm" - : set background=dark - :endif - < When this option is set, the default settings for the highlight groups + depending on the terminal name. Example: >vim + if $TERM ==# "xterm" + set background=dark + endif + < When this option is changed, the default settings for the highlight groups will change. To use other settings, place ":highlight" commands AFTER the setting of the 'background' option. - This option is also used in the "$VIMRUNTIME/syntax/syntax.vim" file - to select the colors for syntax highlighting. After changing this - option, you must load syntax.vim again to see the result. This can be - done with ":syntax on". ]=], expand_cb = 'expand_set_background', full_name = 'background', @@ -387,7 +385,7 @@ return { full_name = 'backup', scope = { 'global' }, short_desc = N_('keep backup file after overwriting a file'), - type = 'bool', + type = 'boolean', varname = 'p_bk', }, { @@ -498,12 +496,12 @@ return { use '//', instead of '\\'. - Environment variables are expanded |:set_env|. - Careful with '\' characters, type one before a space, type two to - get one in the option (see |option-backslash|), for example: > - :set bdir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces + get one in the option (see |option-backslash|), for example: >vim + set bdir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces < See also 'backup' and 'writebackup' options. - If you want to hide your backup files on Unix, consider this value: > - :set backupdir=./.backup,~/.backup,.,/tmp + If you want to hide your backup files on Unix, consider this value: >vim + set backupdir=./.backup,~/.backup,.,/tmp < You must create a ".backup" directory in each directory and in your home directory for this to work properly. The use of |:set+=| and |:set-=| is preferred when adding or removing @@ -535,8 +533,8 @@ return { If you like to keep a lot of backups, you could use a BufWritePre autocommand to change 'backupext' just before writing the file to - include a timestamp. > - :au BufWritePre * let &bex = '-' .. strftime("%Y%b%d%X") .. '~' + include a timestamp. >vim + au BufWritePre * let &bex = '-' .. strftime("%Y%b%d%X") .. '~' < Use 'backupdir' to put the backup in a different directory. ]=], full_name = 'backupext', @@ -573,7 +571,7 @@ return { Note that environment variables are not expanded. If you want to use $HOME you must expand it explicitly, e.g.: >vim - :let &backupskip = escape(expand('$HOME'), '\') .. '/tmp/*' + let &backupskip = escape(expand('$HOME'), '\') .. '/tmp/*' < Note that the default also makes sure that "crontab -e" works (when a backup would be made by renaming the original file crontab won't see @@ -671,7 +669,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('read/write/edit file in binary mode'), - type = 'bool', + type = 'boolean', varname = 'p_bin', }, { @@ -699,7 +697,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('a Byte Order Mark to the file'), - type = 'bool', + type = 'boolean', varname = 'p_bomb', }, { @@ -733,7 +731,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('wrapped line repeats indent'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'briopt', @@ -848,7 +846,7 @@ return { scope = { 'buffer' }, short_desc = N_('whether the buffer shows up in the buffer list'), tags = { 'E85' }, - type = 'bool', + type = 'boolean', varname = 'p_bl', }, { @@ -950,7 +948,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_(':cd without argument goes to the home directory'), - type = 'bool', + type = 'boolean', varname = 'p_cdh', }, { @@ -971,8 +969,8 @@ return { in the current directory first. If the default value taken from $CDPATH is not what you want, include a modified version of the following command in your vimrc file to - override it: > - :let &cdpath = ',' .. substitute(substitute($CDPATH, '[, ]', '\\\0', 'g'), ':', ',', 'g') + override it: >vim + let &cdpath = ',' .. substitute(substitute($CDPATH, '[, ]', '\\\0', 'g'), ':', ',', 'g') < This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. (parts of 'cdpath' can be passed to the shell to expand file names). @@ -997,9 +995,9 @@ return { The key used in Command-line Mode to open the command-line window. Only non-printable keys are allowed. The key can be specified as a single character, but it is difficult to - type. The preferred way is to use the <> notation. Examples: > - :exe "set cedit=\\<C-Y>" - :exe "set cedit=\\<Esc>" + type. The preferred way is to use the <> notation. Examples: >vim + exe "set cedit=\\<C-Y>" + exe "set cedit=\\<Esc>" < |Nvi| also has this option, but it only uses the first character. See |cmdwin|. ]=], @@ -1045,7 +1043,7 @@ return { Conversion between "latin1", "unicode", "ucs-2", "ucs-4" and "utf-8" is done internally by Vim, 'charconvert' is not used for this. Also used for Unicode conversion. - Example: > + Example: >vim set charconvert=CharConvert() fun CharConvert() system("recode " @@ -1088,7 +1086,7 @@ return { full_name = 'cindent', scope = { 'buffer' }, short_desc = N_('do C program indenting'), - type = 'bool', + type = 'boolean', varname = 'p_cin', }, { @@ -1129,6 +1127,25 @@ return { varname = 'p_cino', }, { + abbreviation = 'cinsd', + alloced = true, + defaults = { if_true = 'public,protected,private' }, + deny_duplicates = true, + desc = [=[ + Keywords that are interpreted as a C++ scope declaration by |cino-g|. + Useful e.g. for working with the Qt framework that defines additional + scope declarations "signals", "public slots" and "private slots": >vim + set cinscopedecls+=signals,public\ slots,private\ slots + < + ]=], + full_name = 'cinscopedecls', + list = 'onecomma', + scope = { 'buffer' }, + short_desc = N_("words that are recognized by 'cino-g'"), + type = 'string', + varname = 'p_cinsd', + }, + { abbreviation = 'cinw', alloced = true, defaults = { if_true = 'if,else,while,do,for,switch' }, @@ -1149,25 +1166,6 @@ return { varname = 'p_cinw', }, { - abbreviation = 'cinsd', - alloced = true, - defaults = { if_true = 'public,protected,private' }, - deny_duplicates = true, - desc = [=[ - Keywords that are interpreted as a C++ scope declaration by |cino-g|. - Useful e.g. for working with the Qt framework that defines additional - scope declarations "signals", "public slots" and "private slots": > - set cinscopedecls+=signals,public\ slots,private\ slots - < - ]=], - full_name = 'cinscopedecls', - list = 'onecomma', - scope = { 'buffer' }, - short_desc = N_("words that are recognized by 'cino-g'"), - type = 'string', - varname = 'p_cinsd', - }, - { abbreviation = 'cb', cb = 'did_set_clipboard', defaults = { if_true = '' }, @@ -1253,26 +1251,27 @@ return { highlighted with ColorColumn |hl-ColorColumn|. Useful to align text. Will make screen redrawing slower. The screen column can be an absolute number, or a number preceded with - '+' or '-', which is added to or subtracted from 'textwidth'. > + '+' or '-', which is added to or subtracted from 'textwidth'. >vim - :set cc=+1 " highlight column after 'textwidth' - :set cc=+1,+2,+3 " highlight three columns after 'textwidth' - :hi ColorColumn ctermbg=lightgrey guibg=lightgrey + set cc=+1 " highlight column after 'textwidth' + set cc=+1,+2,+3 " highlight three columns after 'textwidth' + hi ColorColumn ctermbg=lightgrey guibg=lightgrey < When 'textwidth' is zero then the items with '-' and '+' are not used. A maximum of 256 columns are highlighted. ]=], full_name = 'colorcolumn', list = 'onecomma', - redraw = { 'current_window' }, + redraw = { 'current_window', 'highlight_only' }, scope = { 'window' }, short_desc = N_('columns to highlight'), type = 'string', }, { abbreviation = 'co', + cb = 'did_set_lines_or_columns', defaults = { - if_true = macros('DFLT_COLS'), + if_true = imacros('DFLT_COLS'), doc = '80 or terminal width', }, desc = [=[ @@ -1285,8 +1284,8 @@ return { number of columns of the display, the display may be messed up. For the GUI it is always possible and Vim limits the number of columns to what fits on the screen. You can use this command to get the widest - window possible: > - :set columns=9999 + window possible: >vim + set columns=9999 < Minimum value is 12, maximum value is 10000. ]=], full_name = 'columns', @@ -1341,7 +1340,7 @@ return { full_name = 'compatible', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -1363,8 +1362,8 @@ return { k scan the files given with the 'dictionary' option kspell use the currently active spell checking |spell| k{dict} scan the file {dict}. Several "k" flags can be given, - patterns are valid too. For example: > - :set cpt=k/usr/dict/*,k~/spanish + patterns are valid too. For example: >vim + set cpt=k/usr/dict/*,k~/spanish < s scan the files given with the 'thesaurus' option s{tsr} scan the file {tsr}. Several "s" flags can be given, patterns are valid too. @@ -1394,65 +1393,6 @@ return { varname = 'p_cpt', }, { - abbreviation = 'cocu', - alloced = true, - cb = 'did_set_concealcursor', - defaults = { if_true = '' }, - desc = [=[ - Sets the modes in which text in the cursor line can also be concealed. - When the current mode is listed then concealing happens just like in - other lines. - n Normal mode - v Visual mode - i Insert mode - c Command line editing, for 'incsearch' - - 'v' applies to all lines in the Visual area, not only the cursor. - A useful value is "nc". This is used in help files. So long as you - are moving around text is concealed, but when starting to insert text - or selecting a Visual area the concealed text is displayed, so that - you can see what you are doing. - Keep in mind that the cursor position is not always where it's - displayed. E.g., when moving vertically it may change column. - ]=], - expand_cb = 'expand_set_concealcursor', - full_name = 'concealcursor', - list = 'flags', - redraw = { 'current_window' }, - scope = { 'window' }, - short_desc = N_('whether concealable text is hidden in cursor line'), - type = 'string', - }, - { - abbreviation = 'cole', - defaults = { if_true = 0 }, - desc = [=[ - Determine how text with the "conceal" syntax attribute |:syn-conceal| - is shown: - - Value Effect ~ - 0 Text is shown normally - 1 Each block of concealed text is replaced with one - character. If the syntax item does not have a custom - replacement character defined (see |:syn-cchar|) the - character defined in 'listchars' is used. - It is highlighted with the "Conceal" highlight group. - 2 Concealed text is completely hidden unless it has a - custom replacement character defined (see - |:syn-cchar|). - 3 Concealed text is completely hidden. - - Note: in the cursor line concealed text is not hidden, so that you can - edit and copy the text. This can be changed with the 'concealcursor' - option. - ]=], - full_name = 'conceallevel', - redraw = { 'current_window' }, - scope = { 'window' }, - short_desc = N_('whether concealable text is shown or hidden'), - type = 'number', - }, - { abbreviation = 'cfu', alloced = true, cb = 'did_set_completefunc', @@ -1502,13 +1442,17 @@ return { completion in the preview window. Only works in combination with "menu" or "menuone". - noinsert Do not insert any text for a match until the user selects + noinsert Do not insert any text for a match until the user selects a match from the menu. Only works in combination with "menu" or "menuone". No effect if "longest" is present. - noselect Do not select a match in the menu, force the user to + noselect Do not select a match in the menu, force the user to select one from the menu. Only works in combination with "menu" or "menuone". + + popup Show extra information about the currently selected + completion in a popup window. Only works in combination + with "menu" or "menuone". Overrides "preview". ]=], expand_cb = 'expand_set_completeopt', full_name = 'completeopt', @@ -1543,6 +1487,65 @@ return { varname = 'p_csl', }, { + abbreviation = 'cocu', + alloced = true, + cb = 'did_set_concealcursor', + defaults = { if_true = '' }, + desc = [=[ + Sets the modes in which text in the cursor line can also be concealed. + When the current mode is listed then concealing happens just like in + other lines. + n Normal mode + v Visual mode + i Insert mode + c Command line editing, for 'incsearch' + + 'v' applies to all lines in the Visual area, not only the cursor. + A useful value is "nc". This is used in help files. So long as you + are moving around text is concealed, but when starting to insert text + or selecting a Visual area the concealed text is displayed, so that + you can see what you are doing. + Keep in mind that the cursor position is not always where it's + displayed. E.g., when moving vertically it may change column. + ]=], + expand_cb = 'expand_set_concealcursor', + full_name = 'concealcursor', + list = 'flags', + redraw = { 'current_window' }, + scope = { 'window' }, + short_desc = N_('whether concealable text is hidden in cursor line'), + type = 'string', + }, + { + abbreviation = 'cole', + defaults = { if_true = 0 }, + desc = [=[ + Determine how text with the "conceal" syntax attribute |:syn-conceal| + is shown: + + Value Effect ~ + 0 Text is shown normally + 1 Each block of concealed text is replaced with one + character. If the syntax item does not have a custom + replacement character defined (see |:syn-cchar|) the + character defined in 'listchars' is used. + It is highlighted with the "Conceal" highlight group. + 2 Concealed text is completely hidden unless it has a + custom replacement character defined (see + |:syn-cchar|). + 3 Concealed text is completely hidden. + + Note: in the cursor line concealed text is not hidden, so that you can + edit and copy the text. This can be changed with the 'concealcursor' + option. + ]=], + full_name = 'conceallevel', + redraw = { 'current_window' }, + scope = { 'window' }, + short_desc = N_('whether concealable text is shown or hidden'), + type = 'number', + }, + { abbreviation = 'cf', defaults = { if_true = false }, desc = [=[ @@ -1558,7 +1561,7 @@ return { full_name = 'confirm', scope = { 'global' }, short_desc = N_('ask what to do about unsaved/read-only files'), - type = 'bool', + type = 'boolean', varname = 'p_confirm', }, { @@ -1578,7 +1581,7 @@ return { full_name = 'copyindent', scope = { 'buffer' }, short_desc = N_("make 'autoindent' use existing indent structure"), - type = 'bool', + type = 'boolean', varname = 'p_ci', }, { @@ -1706,9 +1709,6 @@ return { when it didn't exist when editing it. This is a protection against a file unexpectedly created by someone else. Vi didn't complain about this. - *cpo-p* - p Vi compatible Lisp indenting. When not present, a - slightly better algorithm is used. *cpo-P* P When included, a ":write" command that appends to a file will set the file name for the current buffer, if @@ -1843,7 +1843,7 @@ return { pv_name = 'p_crbind', scope = { 'window' }, short_desc = N_('move cursor in window as it moves in other windows'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'cuc', @@ -1853,16 +1853,16 @@ return { |hl-CursorColumn|. Useful to align text. Will make screen redrawing slower. If you only want the highlighting in the current window you can use - these autocommands: > + these autocommands: >vim au WinLeave * set nocursorline nocursorcolumn au WinEnter * set cursorline cursorcolumn < ]=], full_name = 'cursorcolumn', - redraw = { 'current_window_only' }, + redraw = { 'current_window', 'highlight_only' }, scope = { 'window' }, short_desc = N_('highlight the screen column of the cursor'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'cul', @@ -1874,10 +1874,10 @@ return { easier to see the selected text. ]=], full_name = 'cursorline', - redraw = { 'current_window_only' }, + redraw = { 'current_window', 'highlight_only' }, scope = { 'window' }, short_desc = N_('highlight the screen line of the cursor'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'culopt', @@ -1902,7 +1902,7 @@ return { expand_cb = 'expand_set_cursorlineopt', full_name = 'cursorlineopt', list = 'onecomma', - redraw = { 'current_window_only' }, + redraw = { 'current_window', 'highlight_only' }, scope = { 'window' }, short_desc = N_("settings for 'cursorline'"), type = 'string', @@ -1950,7 +1950,7 @@ return { < If the function is defined with `func_name : function() {...`: > ^\s*\ze\i\+\s*[:]\s*(*function\s*( < When using the ":set" command, you need to double the backslashes! - To avoid that use `:let` with a single quote string: > + To avoid that use `:let` with a single quote string: >vim let &l:define = '^\s*\ze\k\+\s*=\s*function(' < ]=], @@ -1978,7 +1978,7 @@ return { full_name = 'delcombine', scope = { 'global' }, short_desc = N_('delete combining characters on their own'), - type = 'bool', + type = 'boolean', varname = 'p_deco', }, { @@ -2030,7 +2030,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('diff mode for the current window'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'dex', @@ -2157,11 +2157,11 @@ return { patience patience diff algorithm histogram histogram diff algorithm - Examples: > - :set diffopt=internal,filler,context:4 - :set diffopt= - :set diffopt=internal,filler,foldcolumn:3 - :set diffopt-=internal " do NOT use the internal diff parser + Examples: >vim + set diffopt=internal,filler,context:4 + set diffopt= + set diffopt=internal,filler,foldcolumn:3 + set diffopt-=internal " do NOT use the internal diff parser < ]=], expand_cb = 'expand_set_diffopt', @@ -2183,7 +2183,7 @@ return { full_name = 'digraph', scope = { 'global' }, short_desc = N_('enable the entering of digraphs in Insert mode'), - type = 'bool', + type = 'boolean', varname = 'p_dg', }, { @@ -2222,8 +2222,8 @@ return { - A directory name may end in an ':' or '/'. - Environment variables are expanded |:set_env|. - Careful with '\' characters, type one before a space, type two to - get one in the option (see |option-backslash|), for example: > - :set dir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces + get one in the option (see |option-backslash|), for example: >vim + set dir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces < Editing the same file twice will result in a warning. Using "/tmp" on is discouraged: if the system crashes you lose the swap file. And @@ -2299,7 +2299,7 @@ return { full_name = 'edcompatible', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -2317,7 +2317,7 @@ return { redraw = { 'all_windows', 'ui_option' }, scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', varname = 'p_emoji', }, { @@ -2354,7 +2354,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('write CTRL-Z for last line in file'), - type = 'bool', + type = 'boolean', varname = 'p_eof', }, { @@ -2380,7 +2380,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('write <EOL> for last line in file'), - type = 'bool', + type = 'boolean', varname = 'p_eol', }, { @@ -2406,7 +2406,7 @@ return { full_name = 'equalalways', scope = { 'global' }, short_desc = N_('windows are automatically made the same size'), - type = 'bool', + type = 'boolean', varname = 'p_ea', }, { @@ -2442,7 +2442,7 @@ return { full_name = 'errorbells', scope = { 'global' }, short_desc = N_('ring the bell for error messages'), - type = 'bool', + type = 'boolean', varname = 'p_eb', }, { @@ -2493,8 +2493,8 @@ return { A list of autocommand event names, which are to be ignored. When set to "all" or when "all" is one of the items, all autocommand events are ignored, autocommands will not be executed. - Otherwise this is a comma-separated list of event names. Example: > - :set ei=WinEnter,WinLeave + Otherwise this is a comma-separated list of event names. Example: >vim + set ei=WinEnter,WinLeave < ]=], expand_cb = 'expand_set_eventignore', @@ -2517,7 +2517,7 @@ return { full_name = 'expandtab', scope = { 'buffer' }, short_desc = N_('use spaces when <Tab> is inserted'), - type = 'bool', + type = 'boolean', varname = 'p_et', }, { @@ -2539,7 +2539,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_('read .nvimrc and .exrc in the current directory'), - type = 'bool', + type = 'boolean', varname = 'p_exrc', }, { @@ -2614,7 +2614,7 @@ return { will work and the first entry of 'fileencodings' will be used (except "ucs-bom", which requires the BOM to be present). If you prefer another encoding use an BufReadPost autocommand event to test if your - preferred encoding is to be used. Example: > + preferred encoding is to be used. Example: >vim au BufReadPost * if search('\S', 'w') == 0 | \ set fenc=iso-2022-jp | endif < This sets 'fileencoding' to "iso-2022-jp" if the file does not contain @@ -2622,8 +2622,8 @@ return { When the |++enc| argument is used then the value of 'fileencodings' is not used. Note that 'fileencodings' is not used for a new file, the global value - of 'fileencoding' is used instead. You can set it with: > - :setglobal fenc=iso-8859-2 + of 'fileencoding' is used instead. You can set it with: >vim + setglobal fenc=iso-8859-2 < This means that a non-existing file may get a different encoding than an empty file. The special value "ucs-bom" can be used to check for a Unicode BOM @@ -2769,7 +2769,7 @@ return { full_name = 'fileignorecase', scope = { 'global' }, short_desc = N_('ignore case when using file names'), - type = 'bool', + type = 'boolean', varname = 'p_fic', }, { @@ -2787,11 +2787,11 @@ return { this use the ":filetype on" command. |:filetype| Setting this option to a different value is most useful in a modeline, for a file for which the file type is not automatically recognized. - Example, for in an IDL file: > + Example, for in an IDL file: >c /* vim: set filetype=idl : */ < |FileType| |filetypes| When a dot appears in the value then this separates two filetype - names. Example: > + names. Example: >c /* vim: set filetype=c.doxygen : */ < This will use the "c" filetype first, then the "doxygen" filetype. This works both for filetype plugins and for syntax files. More than @@ -2818,7 +2818,7 @@ return { Characters to fill the statuslines, vertical separators and special lines in the window. It is a comma-separated list of items. Each item has a name, a colon - and the value of that item: + and the value of that item: |E1511| item default Used for ~ stl ' ' statusline of the current window @@ -2851,12 +2851,12 @@ return { "vert", "vertleft", "vertright", "verthoriz", "foldsep" and "fold" default to single-byte alternatives. - Example: > - :set fillchars=stl:\ ,stlnc:\ ,vert:│,fold:·,diff:- + Example: >vim + set fillchars=stl:\ ,stlnc:\ ,vert:│,fold:·,diff:- < For the "stl", "stlnc", "foldopen", "foldclose" and "foldsep" items single-byte and multibyte characters are supported. But double-width - characters are not supported. + characters are not supported. |E1512| The highlighting used for these items: item highlight group ~ @@ -2901,7 +2901,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('make sure last line in file has <EOL>'), - type = 'bool', + type = 'boolean', varname = 'p_fixeol', }, { @@ -2960,7 +2960,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('set to display all folds open'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'fde', @@ -3181,6 +3181,9 @@ return { It is not allowed to change text or jump to another window while evaluating 'foldtext' |textlock|. + + When set to an empty string, foldtext is disabled, and the line + is displayed normally with highlighting and no line wrapping. ]=], full_name = 'foldtext', modelineexpr = true, @@ -3206,8 +3209,8 @@ return { automatic formatting. This can be empty. Don't insert it yet! - Example: > - :set formatexpr=mylang#Format() + Example: >vim + set formatexpr=mylang#Format() < This will invoke the mylang#Format() function in the autoload/mylang.vim file in 'runtimepath'. |autoload| @@ -3221,7 +3224,7 @@ return { the internal format mechanism. If the expression starts with s: or |<SID>|, then it is replaced with - the script ID (|local-function|). Example: > + the script ID (|local-function|). Example: >vim set formatexpr=s:MyFormatExpr() set formatexpr=<SID>SomeFormatExpr() < Otherwise, the expression is evaluated in the context of the script @@ -3241,27 +3244,6 @@ return { varname = 'p_fex', }, { - abbreviation = 'fo', - alloced = true, - cb = 'did_set_formatoptions', - defaults = { if_true = macros('DFLT_FO_VIM') }, - desc = [=[ - This is a sequence of letters which describes how automatic - formatting is to be done. - See |fo-table| for possible values and |gq| for how to format text. - Commas can be inserted for readability. - To avoid problems with flags that are added in the future, use the - "+=" and "-=" feature of ":set" |add-option-flags|. - ]=], - expand_cb = 'expand_set_formatoptions', - full_name = 'formatoptions', - list = 'flags', - scope = { 'buffer' }, - short_desc = N_('how automatic formatting is to be done'), - type = 'string', - varname = 'p_fo', - }, - { abbreviation = 'flp', alloced = true, defaults = { if_true = '^\\s*\\d\\+[\\]:.)}\\t ]\\s*' }, @@ -3283,6 +3265,27 @@ return { varname = 'p_flp', }, { + abbreviation = 'fo', + alloced = true, + cb = 'did_set_formatoptions', + defaults = { if_true = macros('DFLT_FO_VIM') }, + desc = [=[ + This is a sequence of letters which describes how automatic + formatting is to be done. + See |fo-table| for possible values and |gq| for how to format text. + Commas can be inserted for readability. + To avoid problems with flags that are added in the future, use the + "+=" and "-=" feature of ":set" |add-option-flags|. + ]=], + expand_cb = 'expand_set_formatoptions', + full_name = 'formatoptions', + list = 'flags', + scope = { 'buffer' }, + short_desc = N_('how automatic formatting is to be done'), + type = 'string', + varname = 'p_fo', + }, + { abbreviation = 'fp', defaults = { if_true = '' }, desc = [=[ @@ -3329,7 +3332,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_('whether to invoke fsync() after file write'), - type = 'bool', + type = 'boolean', varname = 'p_fs', }, { @@ -3346,14 +3349,14 @@ return { :s///g subst. one subst. all :s///gg subst. all subst. one - DEPRECATED: Setting this option may break plugins that are not aware - of this option. Also, many users get confused that adding the /g flag - has the opposite effect of that it normally does. + NOTE: Setting this option may break plugins that rely on the default + behavior of the 'g' flag. This will also make the 'g' flag have the + opposite effect of that documented in |:s_g|. ]=], full_name = 'gdefault', scope = { 'global' }, short_desc = N_('the ":substitute" flag \'g\' is default on'), - type = 'bool', + type = 'boolean', varname = 'p_gd', }, { @@ -3388,8 +3391,8 @@ return { will be included. Environment variables are expanded |:set_env|. See |option-backslash| about including spaces and backslashes. When your "grep" accepts the "-H" argument, use this to make ":grep" - also work well with a single file: > - :set grepprg=grep\ -nH + also work well with a single file: >vim + set grepprg=grep\ -nH < Special value: When 'grepprg' is set to "internal" the |:grep| command works like |:vimgrep|, |:lgrep| like |:lvimgrep|, |:grepadd| like |:vimgrepadd| and |:lgrepadd| like |:lvimgrepadd|. @@ -3415,11 +3418,11 @@ return { Configures the cursor style for each mode. Works in the GUI and many terminals. See |tui-cursor-shape|. - To disable cursor-styling, reset the option: > - :set guicursor= + To disable cursor-styling, reset the option: >vim + set guicursor= - < To enable mode shapes, "Cursor" highlight, and blinking: > - :set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr:hor20,o:hor50 + < To enable mode shapes, "Cursor" highlight, and blinking: >vim + set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr:hor20,o:hor50 \,a:blinkwait700-blinkoff400-blinkon250-Cursor/lCursor \,sm:block-blinkwait175-blinkoff150-blinkon175 @@ -3452,8 +3455,8 @@ return { the cursor starts blinking, blinkon is the time that the cursor is shown and blinkoff is the time that the cursor is not shown. Times are in msec. When one of - the numbers is zero, there is no blinking. E.g.: > - :set guicursor=n:blinkon0 + the numbers is zero, there is no blinking. E.g.: >vim + set guicursor=n:blinkon0 < - Default is "blinkon0" for each mode. {group-name} Highlight group that decides the color and font of the @@ -3491,9 +3494,9 @@ return { to do a common setting for all modes. For example, to switch off blinking: "a:blinkon0" - Examples of cursor highlighting: > - :highlight Cursor gui=reverse guifg=NONE guibg=NONE - :highlight Cursor gui=NONE guifg=bg guibg=fg + Examples of cursor highlighting: >vim + highlight Cursor gui=reverse guifg=NONE guibg=NONE + highlight Cursor gui=NONE guifg=bg guibg=fg < ]=], full_name = 'guicursor', @@ -3517,8 +3520,8 @@ return { Spaces after a comma are ignored. To include a comma in a font name precede it with a backslash. Setting an option requires an extra backslash before a space and a backslash. See also - |option-backslash|. For example: > - :set guifont=Screen15,\ 7x13,font\\,with\\,commas + |option-backslash|. For example: >vim + set guifont=Screen15,\ 7x13,font\\,with\\,commas < will make Vim try to use the font "Screen15" first, and if it fails it will try to use "7x13" and then "font,with,commas" instead. @@ -3529,14 +3532,14 @@ return { the case of X). The font names given should be "normal" fonts. Vim will try to find the related bold and italic fonts. - For Win32 and Mac OS: > - :set guifont=* + For Win32 and Mac OS: >vim + set guifont=* < will bring up a font requester, where you can pick the font you want. The font name depends on the GUI used. - For Mac OSX you can use something like this: > - :set guifont=Monaco:h10 + For Mac OSX you can use something like this: >vim + set guifont=Monaco:h10 < *E236* Note that the fonts must be mono-spaced (all characters have the same width). @@ -3561,9 +3564,9 @@ return { Use a ':' to separate the options. - A '_' can be used in the place of a space, so you don't need to use backslashes to escape the spaces. - - Examples: > - :set guifont=courier_new:h12:w5:b:cRUSSIAN - :set guifont=Andale_Mono:h7.5:w4.5 + - Examples: >vim + set guifont=courier_new:h12:w5:b:cRUSSIAN + set guifont=Andale_Mono:h7.5:w4.5 < ]=], deny_duplicates = true, @@ -3743,8 +3746,8 @@ return { When non-empty describes the text to use in a tooltip for the GUI tab pages line. When empty Vim will use a default tooltip. This option is otherwise just like 'guitablabel' above. - You can include a line break. Simplest method is to use |:let|: > - :let &guitabtooltip = "line one\nline two" + You can include a line break. Simplest method is to use |:let|: >vim + let &guitabtooltip = "line one\nline two" < ]=], enable_if = false, @@ -3812,8 +3815,8 @@ return { be used as a last resort. You can add "en" to prefer English over another language, but that will only find tags that exist in that language and not in the English help. - Example: > - :set helplang=de,it + Example: >vim + set helplang=de,it < This will first search German, then Italian and finally English help files. When using |CTRL-]| and ":help!" in a non-English help file Vim will @@ -3849,7 +3852,7 @@ return { full_name = 'hidden', scope = { 'global' }, short_desc = N_("don't unload buffer when it is |abandon|ed"), - type = 'bool', + type = 'boolean', varname = 'p_hid', }, { @@ -3885,7 +3888,7 @@ return { full_name = 'hkmap', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -3894,7 +3897,7 @@ return { full_name = 'hkmapp', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -3923,10 +3926,10 @@ return { with the 'h' flag in 'shada' |shada-h|. ]=], full_name = 'hlsearch', - redraw = { 'all_windows' }, + redraw = { 'all_windows', 'highlight_only' }, scope = { 'global' }, short_desc = N_('highlight matches with last search pattern'), - type = 'bool', + type = 'boolean', varname = 'p_hls', }, { @@ -3945,7 +3948,7 @@ return { full_name = 'icon', scope = { 'global' }, short_desc = N_('Vim set the text of the window icon'), - type = 'bool', + type = 'boolean', varname = 'p_icon', }, { @@ -3981,7 +3984,7 @@ return { full_name = 'ignorecase', scope = { 'global' }, short_desc = N_('ignore case in search patterns'), - type = 'bool', + type = 'boolean', varname = 'p_ic', }, { @@ -3998,7 +4001,7 @@ return { full_name = 'imcmdline', scope = { 'global' }, short_desc = N_('use IM when starting to edit a command line'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'imd', @@ -4016,12 +4019,12 @@ return { full_name = 'imdisable', scope = { 'global' }, short_desc = N_('do not use the IM in any mode'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'imi', cb = 'did_set_iminsert', - defaults = { if_true = macros('B_IMODE_NONE') }, + defaults = { if_true = imacros('B_IMODE_NONE') }, desc = [=[ Specifies whether :lmap or an Input Method (IM) is to be used in Insert mode. Valid values: @@ -4029,8 +4032,8 @@ return { 1 :lmap is ON and IM is off 2 :lmap is off and IM is ON To always reset the option to zero when leaving Insert mode with <Esc> - this can be used: > - :inoremap <ESC> <ESC>:set iminsert=0<CR> + this can be used: >vim + inoremap <ESC> <ESC>:set iminsert=0<CR> < This makes :lmap and IM turn off automatically when leaving Insert mode. Note that this option changes when using CTRL-^ in Insert mode @@ -4047,7 +4050,7 @@ return { }, { abbreviation = 'ims', - defaults = { if_true = macros('B_IMODE_USE_INSERT') }, + defaults = { if_true = imacros('B_IMODE_USE_INSERT') }, desc = [=[ Specifies whether :lmap or an Input Method (IM) is to be used when entering a search pattern. Valid values: @@ -4123,20 +4126,20 @@ return { defaults = { if_true = '' }, desc = [=[ Expression to be used to transform the string found with the 'include' - option to a file name. Mostly useful to change "." to "/" for Java: > - :setlocal includeexpr=substitute(v:fname,'\\.','/','g') + option to a file name. Mostly useful to change "." to "/" for Java: >vim + setlocal includeexpr=substitute(v:fname,'\\.','/','g') < The "v:fname" variable will be set to the file name that was detected. Note the double backslash: the `:set` command first halves them, then one remains in the value, where "\." matches a dot literally. For - simple character replacements `tr()` avoids the need for escaping: > - :setlocal includeexpr=tr(v:fname,'.','/') + simple character replacements `tr()` avoids the need for escaping: >vim + setlocal includeexpr=tr(v:fname,'.','/') < Also used for the |gf| command if an unmodified file name can't be found. Allows doing "gf" on the name after an 'include' statement. Also used for |<cfile>|. If the expression starts with s: or |<SID>|, then it is replaced with - the script ID (|local-function|). Example: > + the script ID (|local-function|). Example: >vim setlocal includeexpr=s:MyIncludeExpr(v:fname) setlocal includeexpr=<SID>SomeIncludeExpr(v:fname) < Otherwise, the expression is evaluated in the context of the script @@ -4178,7 +4181,7 @@ return { typing a search command. See also: 'hlsearch'. If you don't want to turn 'hlsearch' on, but want to highlight all matches while searching, you can turn on and off 'hlsearch' with - autocmd. Example: > + autocmd. Example: >vim augroup vimrc-incsearch-highlight autocmd! autocmd CmdlineEnter /,\? :set hlsearch @@ -4195,7 +4198,7 @@ return { full_name = 'incsearch', scope = { 'global' }, short_desc = N_('highlight match while typing search pattern'), - type = 'bool', + type = 'boolean', varname = 'p_is', }, { @@ -4215,7 +4218,7 @@ return { when the expression is evaluated (but it may be moved around). If the expression starts with s: or |<SID>|, then it is replaced with - the script ID (|local-function|). Example: > + the script ID (|local-function|). Example: >vim set indentexpr=s:MyIndentExpr() set indentexpr=<SID>SomeIndentExpr() < Otherwise, the expression is evaluated in the context of the script @@ -4229,8 +4232,8 @@ return { The evaluation of the expression must not have side effects! It must not change the text, jump to another window, etc. Afterwards the cursor position is always restored, thus the cursor may be moved. - Normally this option would be set to call a function: > - :set indentexpr=GetMyIndent() + Normally this option would be set to call a function: >vim + set indentexpr=GetMyIndent() < Error messages will be suppressed, unless the 'debug' option contains "msg". See |indent-expression|. @@ -4283,7 +4286,7 @@ return { full_name = 'infercase', scope = { 'buffer' }, short_desc = N_('adjust case of match for keyword completion'), - type = 'bool', + type = 'boolean', varname = 'p_inf', }, { @@ -4292,7 +4295,7 @@ return { full_name = 'insertmode', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -4469,7 +4472,7 @@ return { full_name = 'joinspaces', scope = { 'global' }, short_desc = N_('two spaces after a period with a join command'), - type = 'bool', + type = 'boolean', varname = 'p_js', }, { @@ -4558,9 +4561,9 @@ return { When "man" or "man -s" is used, Vim will automatically translate a [count] for the "K" command to a section number. See |option-backslash| about including spaces and backslashes. - Example: > - :set keywordprg=man\ -s - :set keywordprg=:Man + Example: >vim + set keywordprg=man\ -s + set keywordprg=:Man < This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. ]=], @@ -4591,10 +4594,10 @@ return { This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. - Example (for Greek, in UTF-8): *greek* > - :set langmap=ΑA,ΒB,ΨC,ΔD,ΕE,ΦF,ΓG,ΗH,ΙI,ΞJ,ΚK,ΛL,ΜM,ΝN,ΟO,ΠP,QQ,ΡR,ΣS,ΤT,ΘU,ΩV,WW,ΧX,ΥY,ΖZ,αa,βb,ψc,δd,εe,φf,γg,ηh,ιi,ξj,κk,λl,μm,νn,οo,πp,qq,ρr,σs,τt,θu,ωv,ςw,χx,υy,ζz - < Example (exchanges meaning of z and y for commands): > - :set langmap=zy,yz,ZY,YZ + Example (for Greek, in UTF-8): *greek* >vim + set langmap=ΑA,ΒB,ΨC,ΔD,ΕE,ΦF,ΓG,ΗH,ΙI,ΞJ,ΚK,ΛL,ΜM,ΝN,ΟO,ΠP,QQ,ΡR,ΣS,ΤT,ΘU,ΩV,WW,ΧX,ΥY,ΖZ,αa,βb,ψc,δd,εe,φf,γg,ηh,ιi,ξj,κk,λl,μm,νn,οo,πp,qq,ρr,σs,τt,θu,ωv,ςw,χx,υy,ζz + < Example (exchanges meaning of z and y for commands): >vim + set langmap=zy,yz,ZY,YZ < The 'langmap' option is a list of parts, separated with commas. Each part can be in one of two forms: @@ -4632,22 +4635,22 @@ return { defaults = { if_true = '' }, desc = [=[ Language to use for menu translation. Tells which file is loaded - from the "lang" directory in 'runtimepath': > + from the "lang" directory in 'runtimepath': >vim "lang/menu_" .. &langmenu .. ".vim" < (without the spaces). For example, to always use the Dutch menus, no - matter what $LANG is set to: > - :set langmenu=nl_NL.ISO_8859-1 + matter what $LANG is set to: >vim + set langmenu=nl_NL.ISO_8859-1 < When 'langmenu' is empty, |v:lang| is used. Only normal file name characters can be used, `/\*?[|<>` are illegal. If your $LANG is set to a non-English language but you do want to use - the English menus: > - :set langmenu=none + the English menus: >vim + set langmenu=none < This option must be set before loading menus, switching on filetype detection or syntax highlighting. Once the menus are defined setting - this option has no effect. But you could do this: > - :source $VIMRUNTIME/delmenu.vim - :set langmenu=de_DE.ISO_8859-1 - :source $VIMRUNTIME/menu.vim + this option has no effect. But you could do this: >vim + source $VIMRUNTIME/delmenu.vim + set langmenu=de_DE.ISO_8859-1 + source $VIMRUNTIME/menu.vim < Warning: This deletes all menus that you defined yourself! ]=], full_name = 'langmenu', @@ -4664,7 +4667,7 @@ return { full_name = 'langnoremap', scope = { 'global' }, short_desc = N_("do not apply 'langmap' to mapped characters"), - type = 'bool', + type = 'boolean', varname = 'p_lnr', }, { @@ -4679,7 +4682,7 @@ return { full_name = 'langremap', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', varname = 'p_lrm', }, { @@ -4718,7 +4721,7 @@ return { full_name = 'lazyredraw', scope = { 'global' }, short_desc = N_("don't redraw while executing macros"), - type = 'bool', + type = 'boolean', varname = 'p_lz', }, { @@ -4739,11 +4742,12 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('wrap long lines at a blank'), - type = 'bool', + type = 'boolean', }, { + cb = 'did_set_lines_or_columns', defaults = { - if_true = macros('DFLT_ROWS'), + if_true = imacros('DFLT_ROWS'), doc = '24 or terminal height', }, desc = [=[ @@ -4754,8 +4758,8 @@ return { option will cause the window size to be changed. When you only want to use the size for the GUI, put the command in your |gvimrc| file. Vim limits the number of lines to what fits on the screen. You can - use this command to get the tallest window possible: > - :set lines=999 + use this command to get the tallest window possible: >vim + set lines=999 < Minimum value is 2, maximum value is 1000. ]=], full_name = 'lines', @@ -4802,7 +4806,7 @@ return { full_name = 'lisp', scope = { 'buffer' }, short_desc = N_('indenting for Lisp'), - type = 'bool', + type = 'boolean', varname = 'p_lisp', }, { @@ -4857,8 +4861,8 @@ return { The cursor is displayed at the start of the space a Tab character occupies, not at the end as usual in Normal mode. To get this cursor - position while displaying Tabs with spaces, use: > - :set list lcs=tab:\ \ + position while displaying Tabs with spaces, use: >vim + set list lcs=tab:\ \ < Note that list mode will also affect formatting (set with 'textwidth' or 'wrapmargin') when 'cpoptions' includes 'L'. See 'listchars' for @@ -4868,7 +4872,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('<Tab> and <EOL>'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'lcs', @@ -4878,7 +4882,7 @@ return { deny_duplicates = true, desc = [=[ Strings to use in 'list' mode and for the |:list| command. It is a - comma-separated list of string settings. + comma-separated list of string settings. *E1511* *lcs-eol* eol:c Character to show at the end of each line. When @@ -4922,8 +4926,8 @@ return { lead:c Character to show for leading spaces. When omitted, leading spaces are blank. Overrides the "space" and "multispace" settings for leading spaces. You can - combine it with "tab:", for example: > - :set listchars+=tab:>-,lead:. + combine it with "tab:", for example: >vim + set listchars+=tab:>-,lead:. < *lcs-leadmultispace* leadmultispace:c... @@ -4957,19 +4961,19 @@ return { omitted. The characters ':' and ',' should not be used. UTF-8 characters can - be used. All characters must be single width. + be used. All characters must be single width. *E1512* - Each character can be specified as hex: > + Each character can be specified as hex: >vim set listchars=eol:\\x24 set listchars=eol:\\u21b5 set listchars=eol:\\U000021b5 < Note that a double backslash is used. The number of hex characters must be exactly 2 for \\x, 4 for \\u and 8 for \\U. - Examples: > - :set lcs=tab:>-,trail:- - :set lcs=tab:>-,eol:<,nbsp:% - :set lcs=extends:>,precedes:< + Examples: >vim + set lcs=tab:>-,trail:- + set lcs=tab:>-,eol:<,nbsp:% + set lcs=extends:>,precedes:< < |hl-NonText| highlighting will be used for "eol", "extends" and "precedes". |hl-Whitespace| for "nbsp", "space", "tab", "multispace", "lead" and "trail". @@ -4996,7 +5000,7 @@ return { full_name = 'loadplugins', scope = { 'global' }, short_desc = N_('load plugin scripts when starting up'), - type = 'bool', + type = 'boolean', varname = 'p_lpl', }, { @@ -5013,7 +5017,7 @@ return { full_name = 'magic', scope = { 'global' }, short_desc = N_('special characters in search patterns'), - type = 'bool', + type = 'boolean', varname = 'p_magic', }, { @@ -5053,8 +5057,8 @@ return { This would be mostly useful when you use MS-Windows. If iconv is enabled, setting 'makeencoding' to "char" has the same effect as - setting to the system locale encoding. Example: > - :set makeencoding=char " system locale is used + setting to the system locale encoding. Example: >vim + set makeencoding=char " system locale is used < ]=], expand_cb = 'expand_set_encoding', @@ -5076,11 +5080,11 @@ return { about including spaces and backslashes. Note that a '|' must be escaped twice: once for ":set" and once for the interpretation of a command. When you use a filter called - "myfilter" do it like this: > - :set makeprg=gmake\ \\\|\ myfilter + "myfilter" do it like this: >vim + set makeprg=gmake\ \\\|\ myfilter < The placeholder "$*" can be given (even multiple times) to specify - where the arguments will be included, for example: > - :set makeprg=latex\ \\\\nonstopmode\ \\\\input\\{$*} + where the arguments will be included, for example: >vim + set makeprg=latex\ \\\\nonstopmode\ \\\\input\\{$*} < This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. ]=], @@ -5105,12 +5109,12 @@ return { jump between two double quotes. The characters must be separated by a colon. The pairs must be separated by a comma. Example for including '<' and - '>' (for HTML): > - :set mps+=<:> + '>' (for HTML): >vim + set mps+=<:> < A more exotic example, to jump between the '=' and ';' in an - assignment, useful for languages like C and Java: > - :au FileType c,cpp,java set mps+==:; + assignment, useful for languages like C and Java: >vim + au FileType c,cpp,java set mps+==:; < For a more advanced way of using "%", see the matchit.vim plugin in the $VIMRUNTIME/plugin directory. |add-local-help| @@ -5156,6 +5160,7 @@ return { Increasing this limit above 200 also changes the maximum for Ex command recursion, see |E169|. See also |:function|. + Also used for maximum depth of callback functions. ]=], full_name = 'maxfuncdepth', scope = { 'global' }, @@ -5250,8 +5255,8 @@ return { The languages for which these numbers are important are Italian and Hungarian. The default works for when you have about 512 Mbyte. If - you have 1 Gbyte you could use: > - :set mkspellmem=900000,3000,800 + you have 1 Gbyte you could use: >vim + set mkspellmem=900000,3000,800 < If you have less than 512 Mbyte |:mkspell| may fail for some languages, no matter what you set 'mkspellmem' to. @@ -5280,7 +5285,7 @@ return { full_name = 'modeline', scope = { 'buffer' }, short_desc = N_('recognize modelines at start or end of file'), - type = 'bool', + type = 'boolean', varname = 'p_ml', }, { @@ -5297,7 +5302,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_('allow some options to be set in modeline'), - type = 'bool', + type = 'boolean', varname = 'p_mle', }, { @@ -5329,7 +5334,7 @@ return { scope = { 'buffer' }, short_desc = N_('changes to the text are not possible'), tags = { 'E21' }, - type = 'bool', + type = 'boolean', varname = 'p_ma', }, { @@ -5364,7 +5369,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('buffer has been modified'), - type = 'bool', + type = 'boolean', varname = 'p_mod', }, { @@ -5377,7 +5382,7 @@ return { full_name = 'more', scope = { 'global' }, short_desc = N_('listings when the whole screen is filled'), - type = 'bool', + type = 'boolean', varname = 'p_more', }, { @@ -5385,8 +5390,8 @@ return { defaults = { if_true = 'nvi' }, desc = [=[ Enables mouse support. For example, to enable the mouse in Normal mode - and Visual mode: > - :set mouse=nv + and Visual mode: >vim + set mouse=nv < To temporarily disable mouse support, hold the shift key while using the mouse. @@ -5443,7 +5448,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('keyboard focus follows the mouse'), - type = 'bool', + type = 'boolean', varname = 'p_mousef', }, { @@ -5459,7 +5464,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('hide mouse pointer while typing'), - type = 'bool', + type = 'boolean', varname = 'p_mh', }, { @@ -5496,19 +5501,19 @@ return { Note that you can further refine the meaning of buttons with mappings. See |mouse-overview|. But mappings are NOT used for modeless selection. - Example: > - :map <S-LeftMouse> <RightMouse> - :map <S-LeftDrag> <RightDrag> - :map <S-LeftRelease> <RightRelease> - :map <2-S-LeftMouse> <2-RightMouse> - :map <2-S-LeftDrag> <2-RightDrag> - :map <2-S-LeftRelease> <2-RightRelease> - :map <3-S-LeftMouse> <3-RightMouse> - :map <3-S-LeftDrag> <3-RightDrag> - :map <3-S-LeftRelease> <3-RightRelease> - :map <4-S-LeftMouse> <4-RightMouse> - :map <4-S-LeftDrag> <4-RightDrag> - :map <4-S-LeftRelease> <4-RightRelease> + Example: >vim + map <S-LeftMouse> <RightMouse> + map <S-LeftDrag> <RightDrag> + map <S-LeftRelease> <RightRelease> + map <2-S-LeftMouse> <2-RightMouse> + map <2-S-LeftDrag> <2-RightDrag> + map <2-S-LeftRelease> <2-RightRelease> + map <3-S-LeftMouse> <3-RightMouse> + map <3-S-LeftDrag> <3-RightDrag> + map <3-S-LeftRelease> <3-RightRelease> + map <4-S-LeftMouse> <4-RightMouse> + map <4-S-LeftDrag> <4-RightDrag> + map <4-S-LeftRelease> <4-RightRelease> < Mouse commands requiring the CTRL modifier can be simulated by typing the "g" key before using the mouse: @@ -5536,7 +5541,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('deliver mouse move events to input queue'), - type = 'bool', + type = 'boolean', varname = 'p_mousemev', }, { @@ -5556,8 +5561,8 @@ return { for vertical scrolling). You can disable mouse scrolling by using a count of 0. - Example: > - :set mousescroll=ver:5,hor:2 + Example: >vim + set mousescroll=ver:5,hor:2 < Will make Nvim scroll 5 lines at a time when scrolling vertically, and scroll 2 columns at a time when scrolling horizontally. ]=], @@ -5633,8 +5638,8 @@ return { Any modes not specified or shapes not available use the normal mouse pointer. - Example: > - :set mouseshape=s:udsizing,m:no + Example: >vim + set mouseshape=s:udsizing,m:no < will make the mouse turn to a sizing arrow over the status lines and indicate no input when the hit-enter prompt is displayed (since clicking the mouse has no effect in this state.) @@ -5733,7 +5738,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('print the line number in front of each line'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'nuw', @@ -5797,7 +5802,7 @@ return { full_name = 'opendevice', scope = { 'global' }, short_desc = N_('allow reading/writing devices on MS-Windows'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'opfunc', @@ -5864,7 +5869,7 @@ return { pri_mkrc = true, scope = { 'global' }, short_desc = N_('pasting text'), - type = 'bool', + type = 'boolean', varname = 'p_paste', }, { @@ -5932,30 +5937,30 @@ return { provided that the file being searched for has a relative path (not starting with "/", "./" or "../"). The directories in the 'path' option may be relative or absolute. - - Use commas to separate directory names: > - :set path=.,/usr/local/include,/usr/include + - Use commas to separate directory names: >vim + set path=.,/usr/local/include,/usr/include < - Spaces can also be used to separate directory names. To have a space in a directory name, precede it with an extra backslash, and - escape the space: > - :set path=.,/dir/with\\\ space + escape the space: >vim + set path=.,/dir/with\\\ space < - To include a comma in a directory name precede it with an extra - backslash: > - :set path=.,/dir/with\\,comma - < - To search relative to the directory of the current file, use: > - :set path=. + backslash: >vim + set path=.,/dir/with\\,comma + < - To search relative to the directory of the current file, use: >vim + set path=. < - To search in the current directory use an empty string between two - commas: > - :set path=,, + commas: >vim + set path=,, < - A directory name may end in a ':' or '/'. - Environment variables are expanded |:set_env|. - When using |netrw.vim| URLs can be used. For example, adding "https://www.vim.org" will make ":find index.html" work. - Search upwards and downwards in a directory tree using "*", "**" and ";". See |file-searching| for info and syntax. - - Careful with '\' characters, type two to get one in the option: > - :set path=.,c:\\include - < Or just use '/' instead: > - :set path=.,c:/include + - Careful with '\' characters, type two to get one in the option: >vim + set path=.,c:\\include + < Or just use '/' instead: >vim + set path=.,c:/include < Don't forget "." or files won't even be found in the same directory as the file! The maximum length is limited. How much depends on the system, mostly @@ -5964,14 +5969,14 @@ return { 'path', see |:checkpath|. The use of |:set+=| and |:set-=| is preferred when adding or removing directories from the list. This avoids problems when a future version - uses another default. To remove the current directory use: > - :set path-= - < To add the current directory use: > - :set path+= + uses another default. To remove the current directory use: >vim + set path-= + < To add the current directory use: >vim + set path+= < To use an environment variable, you probably need to replace the separator. Here is an example to append $INCL, in which directory - names are separated with a semi-colon: > - :let &path = &path .. "," .. substitute($INCL, ';', ',', 'g') + names are separated with a semi-colon: >vim + let &path = &path .. "," .. substitute($INCL, ';', ',', 'g') < Replace the ';' with a ':' or whatever separator is used. Note that this doesn't work when $INCL contains a comma or white space. ]=], @@ -6004,7 +6009,7 @@ return { full_name = 'preserveindent', scope = { 'buffer' }, short_desc = N_('preserve the indent structure when reindenting'), - type = 'bool', + type = 'boolean', varname = 'p_pi', }, { @@ -6035,14 +6040,14 @@ return { scope = { 'window' }, short_desc = N_('identifies the preview window'), tags = { 'E590' }, - type = 'bool', + type = 'boolean', }, { defaults = { if_true = true }, full_name = 'prompt', scope = { 'global' }, short_desc = N_('enable prompt in Ex mode'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -6056,10 +6061,10 @@ return { It is possible to override the level for individual highlights within the popupmenu using |highlight-blend|. For instance, to enable - transparency but force the current selected element to be fully opaque: > + transparency but force the current selected element to be fully opaque: >vim - :set pumblend=15 - :hi PmenuSel blend=0 + set pumblend=15 + hi PmenuSel blend=0 < UI-dependent. Works best with RGB colors. 'termguicolors' ]=], @@ -6176,7 +6181,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('disallow writing the buffer'), - type = 'bool', + type = 'boolean', varname = 'p_ro', }, { @@ -6292,14 +6297,14 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('show relative line number in front of each line'), - type = 'bool', + type = 'boolean', }, { defaults = { if_true = true }, full_name = 'remap', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -6328,7 +6333,7 @@ return { full_name = 'revins', scope = { 'global' }, short_desc = N_('inserting characters will work backwards'), - type = 'bool', + type = 'boolean', varname = 'p_ri', }, { @@ -6349,7 +6354,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('window is right-to-left oriented'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'rlc', @@ -6403,7 +6408,7 @@ return { redraw = { 'statuslines' }, scope = { 'global' }, short_desc = N_('show cursor line and column in the status line'), - type = 'bool', + type = 'boolean', varname = 'p_ru', }, { @@ -6419,8 +6424,8 @@ return { The default ruler width is 17 characters. To make the ruler 15 characters wide, put "%15(" at the start and "%)" at the end. - Example: > - :set rulerformat=%15(%c%V\ %p%%%) + Example: >vim + set rulerformat=%15(%c%V\ %p%%%) < ]=], full_name = 'rulerformat', @@ -6513,8 +6518,8 @@ return { runtime files. For speed, use as few items as possible and avoid wildcards. See |:runtime|. - Example: > - :set runtimepath=~/vimruntime,/mygroup/vim,$VIMRUNTIME + Example: >vim + set runtimepath=~/vimruntime,/mygroup/vim,$VIMRUNTIME < This will use the directory "~/vimruntime" first (containing your personal Nvim runtime files), then "/mygroup/vim", and finally "$VIMRUNTIME" (the default runtime files). @@ -6557,27 +6562,6 @@ return { type = 'number', }, { - abbreviation = 'sms', - cb = 'did_set_smoothscroll', - defaults = { if_true = false }, - desc = [=[ - Scrolling works with screen lines. When 'wrap' is set and the first - line in the window wraps part of it may not be visible, as if it is - above the window. "<<<" is displayed at the start of the first line, - highlighted with |hl-NonText|. - You may also want to add "lastline" to the 'display' option to show as - much of the last line as possible. - NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y - and scrolling with the mouse. - ]=], - full_name = 'smoothscroll', - pv_name = 'p_sms', - redraw = { 'current_window' }, - scope = { 'window' }, - short_desc = N_("scroll by screen lines when 'wrap' is set"), - type = 'bool', - }, - { abbreviation = 'scbk', cb = 'did_set_scrollback', defaults = { @@ -6619,7 +6603,7 @@ return { pv_name = 'p_scbind', scope = { 'window' }, short_desc = N_('scroll in window as other windows scroll'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'sj', @@ -6648,7 +6632,7 @@ return { in the middle of the window (except at the start or end of the file or when long lines wrap). After using the local value, go back the global value with one of - these two: > + these two: >vim setlocal scrolloff< setlocal scrolloff=-1 < For scrolling horizontally see 'sidescrolloff'. @@ -6721,7 +6705,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', varname = 'p_secure', }, { @@ -6830,6 +6814,7 @@ return { }, { abbreviation = 'sd', + alias = { 'vi', 'viminfo' }, cb = 'did_set_shada', defaults = { if_true = "!,'100,<50,s10,h", @@ -6927,8 +6912,8 @@ return { 2^8 < 10240 < 2^16) + 10240 bytes (requested maximum item contents size) = 10253 bytes. - Example: > - :set shada='50,<1000,s100,:0,n~/nvim/shada + Example: >vim + set shada='50,<1000,s100,:0,n~/nvim/shada < '50 Marks will be remembered for the last 50 files you edited. @@ -6961,6 +6946,7 @@ return { }, { abbreviation = 'sdf', + alias = { 'vif', 'viminfofile' }, defaults = { if_true = '' }, deny_duplicates = true, desc = [=[ @@ -6998,12 +6984,12 @@ return { Environment variables are expanded |:set_env|. If the name of the shell contains a space, you need to enclose it in - quotes. Example with quotes: > - :set shell=\"c:\program\ files\unix\sh.exe\"\ -f + quotes. Example with quotes: >vim + set shell=\"c:\program\ files\unix\sh.exe\"\ -f < Note the backslash before each quote (to avoid starting a comment) and each space (to avoid ending the option value), so better use |:let-&| - like this: > - :let &shell='"C:\Program Files\unix\sh.exe" -f' + like this: >vim + let &shell='"C:\Program Files\unix\sh.exe" -f' < Also note that the "-f" is not inside the quotes, because it is not part of the command name. *shell-unquoting* @@ -7026,7 +7012,7 @@ return { Note that such processing is done after |:set| did its own round of unescaping, so to keep yourself sane use |:let-&| like shown above. *shell-powershell* - To use PowerShell: > + To use PowerShell: >vim let &shell = executable('pwsh') ? 'pwsh' : 'powershell' let &shellcmdflag = '-NoLogo -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();$PSDefaultParameterValues[''Out-File:Encoding'']=''utf8'';Remove-Alias -Force -ErrorAction SilentlyContinue tee;' let &shellredir = '2>&1 | %%{ "$_" } | Out-File %s; exit $LastExitCode' @@ -7197,7 +7183,7 @@ return { existing file names, thus this option needs to be set before opening any file for best results. This might change in the future. 'shellslash' only works when a backslash can be used as a path - separator. To test if this is so use: > + separator. To test if this is so use: >vim if exists('+shellslash') < Also see 'completeslash'. ]=], @@ -7205,7 +7191,7 @@ return { full_name = 'shellslash', scope = { 'global' }, short_desc = N_('use forward slash for shell file names'), - type = 'bool', + type = 'boolean', varname = 'p_ssl', }, { @@ -7226,10 +7212,27 @@ return { full_name = 'shelltemp', scope = { 'global' }, short_desc = N_('whether to use a temp file for shell commands'), - type = 'bool', + type = 'boolean', varname = 'p_stmp', }, { + abbreviation = 'sxe', + defaults = { if_true = '' }, + desc = [=[ + When 'shellxquote' is set to "(" then the characters listed in this + option will be escaped with a '^' character. This makes it possible + to execute most external commands with cmd.exe. + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. + ]=], + full_name = 'shellxescape', + scope = { 'global' }, + secure = true, + short_desc = N_("characters to escape when 'shellxquote' is ("), + type = 'string', + varname = 'p_sxe', + }, + { abbreviation = 'sxq', defaults = { condition = 'MSWIN', @@ -7256,23 +7259,6 @@ return { varname = 'p_sxq', }, { - abbreviation = 'sxe', - defaults = { if_true = '' }, - desc = [=[ - When 'shellxquote' is set to "(" then the characters listed in this - option will be escaped with a '^' character. This makes it possible - to execute most external commands with cmd.exe. - This option cannot be set from a |modeline| or in the |sandbox|, for - security reasons. - ]=], - full_name = 'shellxescape', - scope = { 'global' }, - secure = true, - short_desc = N_("characters to escape when 'shellxquote' is ("), - type = 'string', - varname = 'p_sxe', - }, - { abbreviation = 'sr', defaults = { if_true = false }, desc = [=[ @@ -7283,7 +7269,7 @@ return { full_name = 'shiftround', scope = { 'global' }, short_desc = N_('round indent to multiple of shiftwidth'), - type = 'bool', + type = 'boolean', varname = 'p_sr', }, { @@ -7343,9 +7329,10 @@ return { match", "Pattern not found", "Back at original", etc. C don't give messages while scanning for ins-completion *shm-C* items, for instance "scanning tags" - q use "recording" instead of "recording @a" *shm-q* + q do not show "recording @a" when recording a macro *shm-q* F don't give the file info when editing a file, like *shm-F* - `:silent` was used for the command + `:silent` was used for the command; note that this also + affects messages from 'autoread' reloading S do not show search count message when searching, e.g. *shm-S* "[1/5]" @@ -7373,9 +7360,9 @@ return { defaults = { if_true = '' }, desc = [=[ String to put at the start of lines that have been wrapped. Useful - values are "> " or "+++ ": > - :let &showbreak = "> " - :let &showbreak = '+++ ' + values are "> " or "+++ ": >vim + let &showbreak = "> " + let &showbreak = '+++ ' < Only printable single-cell characters are allowed, excluding <Tab> and comma (in a future version the comma might be used to separate the part that is shown at the end and at the start of a line). @@ -7384,8 +7371,8 @@ return { If you want the 'showbreak' to appear in between line numbers, add the "n" flag to 'cpoptions'. A window-local value overrules a global value. If the global value is - set and you want no value in the current window use NONE: > - :setlocal showbreak=NONE + set and you want no value in the current window use NONE: >vim + setlocal showbreak=NONE < ]=], full_name = 'showbreak', @@ -7415,7 +7402,7 @@ return { full_name = 'showcmd', scope = { 'global' }, short_desc = N_('show (partial) command in status line'), - type = 'bool', + type = 'boolean', varname = 'p_sc', }, { @@ -7458,7 +7445,7 @@ return { full_name = 'showfulltag', scope = { 'global' }, short_desc = N_('show full tag pattern when completing tag'), - type = 'bool', + type = 'boolean', varname = 'p_sft', }, { @@ -7484,7 +7471,7 @@ return { full_name = 'showmatch', scope = { 'global' }, short_desc = N_('briefly jump to matching bracket if insert one'), - type = 'bool', + type = 'boolean', varname = 'p_sm', }, { @@ -7498,7 +7485,7 @@ return { full_name = 'showmode', scope = { 'global' }, short_desc = N_('message on status line to show current mode'), - type = 'bool', + type = 'boolean', varname = 'p_smd', }, { @@ -7551,16 +7538,16 @@ return { horizontally centered in the window, as long as one does not come too close to the beginning of the line. After using the local value, go back the global value with one of - these two: > + these two: >vim setlocal sidescrolloff< setlocal sidescrolloff=-1 < Example: Try this together with 'sidescroll' and 'listchars' as in the following example to never allow the cursor to move - onto the "extends" character: > + onto the "extends" character: >vim - :set nowrap sidescroll=1 listchars=extends:>,precedes:< - :set sidescrolloff=1 + set nowrap sidescroll=1 listchars=extends:>,precedes:< + set sidescrolloff=1 < ]=], full_name = 'sidescrolloff', @@ -7613,7 +7600,7 @@ return { full_name = 'smartcase', scope = { 'global' }, short_desc = N_('no ignore case when pattern has uppercase'), - type = 'bool', + type = 'boolean', varname = 'p_scs', }, { @@ -7643,7 +7630,7 @@ return { full_name = 'smartindent', scope = { 'buffer' }, short_desc = N_('smart autoindenting for C programs'), - type = 'bool', + type = 'boolean', varname = 'p_si', }, { @@ -7664,10 +7651,31 @@ return { full_name = 'smarttab', scope = { 'global' }, short_desc = N_("use 'shiftwidth' when inserting <Tab>"), - type = 'bool', + type = 'boolean', varname = 'p_sta', }, { + abbreviation = 'sms', + cb = 'did_set_smoothscroll', + defaults = { if_true = false }, + desc = [=[ + Scrolling works with screen lines. When 'wrap' is set and the first + line in the window wraps part of it may not be visible, as if it is + above the window. "<<<" is displayed at the start of the first line, + highlighted with |hl-NonText|. + You may also want to add "lastline" to the 'display' option to show as + much of the last line as possible. + NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y + and scrolling with the mouse. + ]=], + full_name = 'smoothscroll', + pv_name = 'p_sms', + redraw = { 'current_window' }, + scope = { 'window' }, + short_desc = N_("scroll by screen lines when 'wrap' is set"), + type = 'boolean', + }, + { abbreviation = 'sts', defaults = { if_true = 0 }, desc = [=[ @@ -7701,10 +7709,10 @@ return { The languages are specified with 'spelllang'. ]=], full_name = 'spell', - redraw = { 'current_window' }, + redraw = { 'current_window', 'highlight_only' }, scope = { 'window' }, short_desc = N_('spell checking'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'spc', @@ -7723,7 +7731,7 @@ return { |set-spc-auto|. ]=], full_name = 'spellcapcheck', - redraw = { 'current_buffer' }, + redraw = { 'current_buffer', 'highlight_only' }, scope = { 'buffer' }, short_desc = N_('pattern to locate end of a sentence'), type = 'string', @@ -7739,7 +7747,7 @@ return { Name of the word list file where words are added for the |zg| and |zw| commands. It must end in ".{encoding}.add". You need to include the path, otherwise the file is placed in the current directory. - The path may include characters from 'isfname', space, comma and '@'. + The path may include characters from 'isfname', ' ', ',', '@' and ':'. *E765* It may also be a comma-separated list of names. A count before the |zg| and |zw| commands can be used to access each. This allows using @@ -7775,7 +7783,7 @@ return { deny_duplicates = true, desc = [=[ A comma-separated list of word list names. When the 'spell' option is - on spellchecking will be done for these languages. Example: > + on spellchecking will be done for these languages. Example: >vim set spelllang=en_us,nl,medical < This means US English, Dutch and medical words are recognized. Words that are not recognized will be highlighted. @@ -7814,13 +7822,38 @@ return { expand = true, full_name = 'spelllang', list = 'onecomma', - redraw = { 'current_buffer' }, + redraw = { 'current_buffer', 'highlight_only' }, scope = { 'buffer' }, short_desc = N_('language(s) to do spell checking for'), type = 'string', varname = 'p_spl', }, { + abbreviation = 'spo', + cb = 'did_set_spelloptions', + defaults = { if_true = '' }, + deny_duplicates = true, + desc = [=[ + A comma-separated list of options for spell checking: + camel When a word is CamelCased, assume "Cased" is a + separate word: every upper-case character in a word + that comes after a lower case character indicates the + start of a new word. + noplainbuffer Only spellcheck a buffer when 'syntax' is enabled, + or when extmarks are set within the buffer. Only + designated regions of the buffer are spellchecked in + this case. + ]=], + expand_cb = 'expand_set_spelloptions', + full_name = 'spelloptions', + list = 'onecomma', + redraw = { 'current_buffer', 'highlight_only' }, + scope = { 'buffer' }, + secure = true, + type = 'string', + varname = 'p_spo', + }, + { abbreviation = 'sps', cb = 'did_set_spellsuggest', defaults = { if_true = 'best' }, @@ -7884,8 +7917,8 @@ return { 'verbose' option to a non-zero value. Only one of "best", "double" or "fast" may be used. The others may - appear several times in any order. Example: > - :set sps=file:~/.config/nvim/sugg,best,expr:MySuggest() + appear several times in any order. Example: >vim + set sps=file:~/.config/nvim/sugg,best,expr:MySuggest() < This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. @@ -7901,31 +7934,6 @@ return { varname = 'p_sps', }, { - abbreviation = 'spo', - cb = 'did_set_spelloptions', - defaults = { if_true = '' }, - deny_duplicates = true, - desc = [=[ - A comma-separated list of options for spell checking: - camel When a word is CamelCased, assume "Cased" is a - separate word: every upper-case character in a word - that comes after a lower case character indicates the - start of a new word. - noplainbuffer Only spellcheck a buffer when 'syntax' is enabled, - or when extmarks are set within the buffer. Only - designated regions of the buffer are spellchecked in - this case. - ]=], - expand_cb = 'expand_set_spelloptions', - full_name = 'spelloptions', - list = 'onecomma', - redraw = { 'current_buffer' }, - scope = { 'buffer' }, - secure = true, - type = 'string', - varname = 'p_spo', - }, - { abbreviation = 'sb', defaults = { if_true = false }, desc = [=[ @@ -7935,7 +7943,7 @@ return { full_name = 'splitbelow', scope = { 'global' }, short_desc = N_('new window from split is below the current one'), - type = 'bool', + type = 'boolean', varname = 'p_sb', }, { @@ -7973,7 +7981,7 @@ return { full_name = 'splitright', scope = { 'global' }, short_desc = N_('new window is put right of the current one'), - type = 'bool', + type = 'boolean', varname = 'p_spr', }, { @@ -7994,7 +8002,7 @@ return { full_name = 'startofline', scope = { 'global' }, short_desc = N_('commands move cursor to first non-blank in line'), - type = 'bool', + type = 'boolean', varname = 'p_sol', vim = false, }, @@ -8017,9 +8025,13 @@ return { %s sign column for currently drawn line %C fold column for currently drawn line - NOTE: To draw the sign and fold columns, their items must be included in - 'statuscolumn'. Even when they are not included, the status column width - will adapt to the 'signcolumn' and 'foldcolumn' width. + The 'statuscolumn' width follows that of the default columns and + adapts to the |'numberwidth'|, |'signcolumn'| and |'foldcolumn'| option + values (regardless of whether the sign and fold items are present). + Additionally, the 'statuscolumn' grows with the size of the evaluated + format string, up to a point (following the maximum size of the default + fold, sign and number columns). Shrinking only happens when the number + of lines in a buffer changes, or the 'statuscolumn' option is set. The |v:lnum| variable holds the line number to be drawn. The |v:relnum| variable holds the relative line number to be drawn. @@ -8027,6 +8039,9 @@ return { when drawing the actual buffer line, and positive when drawing the wrapped part of a buffer line. + When using |v:relnum|, keep in mind that cursor movement by itself will + not cause the 'statuscolumn' to update unless |'relativenumber'| is set. + NOTE: The %@ click execute function item is supported as well but the specified function will be the same for each row in the same column. It cannot be switched out through a dynamic 'statuscolumn' format, the @@ -8034,21 +8049,21 @@ return { Examples: >vim " Relative number with bar separator and click handlers: - :set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T + set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T " Right aligned relative cursor line number: - :let &stc='%=%{v:relnum?v:relnum:v:lnum} ' + let &stc='%=%{v:relnum?v:relnum:v:lnum} ' " Line numbers in hexadecimal for non wrapped part of lines: - :let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} ' + let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} ' " Human readable line numbers with thousands separator: - :let &stc='%{substitute(v:lnum,"\\d\\zs\\ze\\' + let &stc='%{substitute(v:lnum,"\\d\\zs\\ze\\' . '%(\\d\\d\\d\\)\\+$",",","g")}' " Both relative and absolute line numbers with different " highlighting for odd and even relative numbers: - :let &stc='%#NonText#%{&nu?v:lnum:""}' . + let &stc='%#NonText#%{&nu?v:lnum:""}' . '%=%{&rnu&&(v:lnum%2)?"\ ".v:relnum:""}' . '%#LineNr#%{&rnu&&!(v:lnum%2)?"\ ".v:relnum:""}' @@ -8078,8 +8093,8 @@ return { be given as "%%". When the option starts with "%!" then it is used as an expression, - evaluated and the result is used as the option value. Example: > - :set statusline=%!MyStatusLine() + evaluated and the result is used as the option value. Example: >vim + set statusline=%!MyStatusLine() < The *g:statusline_winid* variable will be set to the |window-ID| of the window that the status line belongs to. The result can contain %{} items that will be evaluated too. @@ -8160,7 +8175,7 @@ return { return value of expr contains "%" items they will get expanded. The expression can contain the "}" character, the end of expression is denoted by "%}". - For example: > + For example: >vim func! Stl_filename() abort return "%t" endfunc @@ -8172,16 +8187,17 @@ return { ) - End of item group. No width fields allowed. T N For 'tabline': start of tab page N label. Use %T or %X to end the label. Clicking this label with left mouse button switches - to the specified tab page. + to the specified tab page, while clicking it with middle mouse + button closes the specified tab page. X N For 'tabline': start of close tab N label. Use %X or %T to end the label, e.g.: %3Xclose%X. Use %999X for a "close current - tab" label. Clicking this label with left mouse button closes - specified tab page. - @ N Start of execute function label. Use %X or %T to - end the label, e.g.: %10@SwitchBuffer@foo.c%X. Clicking this - label runs specified function: in the example when clicking once - using left mouse button on "foo.c" "SwitchBuffer(10, 1, 'l', - ' ')" expression will be run. Function receives the + tab" label. Clicking this label with left mouse button closes + the specified tab page. + @ N Start of execute function label. Use %X or %T to end the label, + e.g.: %10@SwitchBuffer@foo.c%X. Clicking this label runs the + specified function: in the example when clicking once using left + mouse button on "foo.c", a `SwitchBuffer(10, 1, 'l', ' ')` + expression will be run. The specified function receives the following arguments in order: 1. minwid field value or zero if no N was specified 2. number of mouse clicks to detect multiple clicks @@ -8227,8 +8243,8 @@ return { When all items in a group becomes an empty string (i.e. flags that are not set) and a minwid is not set for the group, the whole group will become empty. This will make a group like the following disappear - completely from the statusline when none of the flags are set. > - :set statusline=...%(\ [%M%R%H]%)... + completely from the statusline when none of the flags are set. >vim + set statusline=...%(\ [%M%R%H]%)... < Beware that an expression is evaluated each and every time the status line is displayed. *stl-%{* *g:actual_curbuf* *g:actual_curwin* @@ -8259,23 +8275,23 @@ return { edit your vimrc or whatever with "vim --clean" to get it right. Examples: - Emulate standard status line with 'ruler' set > - :set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P - < Similar, but add ASCII value of char under the cursor (like "ga") > - :set statusline=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P - < Display byte count and byte value, modified flag in red. > - :set statusline=%<%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' - :hi User1 term=inverse,bold cterm=inverse,bold ctermfg=red - < Display a ,GZ flag if a compressed file is loaded > - :set statusline=...%r%{VarExists('b:gzflag','\ [GZ]')}%h... - < In the |:autocmd|'s: > - :let b:gzflag = 1 - < And: > - :unlet b:gzflag - < And define this function: > - :function VarExists(var, val) - : if exists(a:var) | return a:val | else | return '' | endif - :endfunction + Emulate standard status line with 'ruler' set >vim + set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P + < Similar, but add ASCII value of char under the cursor (like "ga") >vim + set statusline=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P + < Display byte count and byte value, modified flag in red. >vim + set statusline=%<%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' + hi User1 term=inverse,bold cterm=inverse,bold ctermfg=red + < Display a ,GZ flag if a compressed file is loaded >vim + set statusline=...%r%{VarExists('b:gzflag','\ [GZ]')}%h... + < In the |:autocmd|'s: >vim + let b:gzflag = 1 + < And: >vim + unlet b:gzflag + < And define this function: >vim + function VarExists(var, val) + if exists(a:var) | return a:val | else | return '' | endif + endfunction < ]=], full_name = 'statusline', @@ -8317,8 +8333,8 @@ return { deny_duplicates = true, desc = [=[ Comma-separated list of suffixes, which are used when searching for a - file for the "gf", "[I", etc. commands. Example: > - :set suffixesadd=.java + file for the "gf", "[I", etc. commands. Example: >vim + set suffixesadd=.java < ]=], full_name = 'suffixesadd', @@ -8356,7 +8372,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('whether to use a swapfile for a buffer'), - type = 'bool', + type = 'boolean', varname = 'p_swf', }, { @@ -8428,19 +8444,19 @@ return { Otherwise this option does not always reflect the current syntax (the b:current_syntax variable does). This option is most useful in a modeline, for a file which syntax is - not automatically recognized. Example, in an IDL file: > + not automatically recognized. Example, in an IDL file: >c /* vim: set syntax=idl : */ < When a dot appears in the value then this separates two filetype - names. Example: > + names. Example: >c /* vim: set syntax=c.doxygen : */ < This will use the "c" syntax first, then the "doxygen" syntax. Note that the second one must be prepared to be loaded as an addition, otherwise it will be skipped. More than one dot may appear. - To switch off syntax highlighting for the current file, use: > - :set syntax=OFF + To switch off syntax highlighting for the current file, use: >vim + set syntax=OFF < To switch syntax highlighting on according to the current value of the - 'filetype' option: > - :set syntax=ON + 'filetype' option: >vim + set syntax=ON < What actually happens when setting the 'syntax' option is that the Syntax autocommand event is triggered with the value as argument. This option is not copied to another buffer, independent of the 's' or @@ -8456,28 +8472,6 @@ return { varname = 'p_syn', }, { - abbreviation = 'tfu', - cb = 'did_set_tagfunc', - defaults = { if_true = '' }, - desc = [=[ - This option specifies a function to be used to perform tag searches. - The function gets the tag pattern and should return a List of matching - tags. See |tag-function| for an explanation of how to write the - function and an example. The value can be the name of a function, a - |lambda| or a |Funcref|. See |option-value-function| for more - information. - This option cannot be set from a |modeline| or in the |sandbox|, for - security reasons. - ]=], - full_name = 'tagfunc', - func = true, - scope = { 'buffer' }, - secure = true, - short_desc = N_('function used to perform tag searches'), - type = 'string', - varname = 'p_tfu', - }, - { abbreviation = 'tal', cb = 'did_set_tabline', defaults = { if_true = '' }, @@ -8632,7 +8626,7 @@ return { full_name = 'tagbsearch', scope = { 'global' }, short_desc = N_('use binary searching in tags files'), - type = 'bool', + type = 'boolean', varname = 'p_tbs', }, { @@ -8656,6 +8650,28 @@ return { varname = 'p_tc', }, { + abbreviation = 'tfu', + cb = 'did_set_tagfunc', + defaults = { if_true = '' }, + desc = [=[ + This option specifies a function to be used to perform tag searches. + The function gets the tag pattern and should return a List of matching + tags. See |tag-function| for an explanation of how to write the + function and an example. The value can be the name of a function, a + |lambda| or a |Funcref|. See |option-value-function| for more + information. + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. + ]=], + full_name = 'tagfunc', + func = true, + scope = { 'buffer' }, + secure = true, + short_desc = N_('function used to perform tag searches'), + type = 'string', + varname = 'p_tfu', + }, + { abbreviation = 'tl', defaults = { if_true = 0 }, desc = [=[ @@ -8677,7 +8693,7 @@ return { full_name = 'tagrelative', scope = { 'global' }, short_desc = N_('file names in tag file are relative'), - type = 'bool', + type = 'boolean', varname = 'p_tr', }, { @@ -8727,7 +8743,7 @@ return { full_name = 'tagstack', scope = { 'global' }, short_desc = N_('push tags onto the tag stack'), - type = 'bool', + type = 'boolean', varname = 'p_tgst', }, { @@ -8746,7 +8762,7 @@ return { full_name = 'termbidi', scope = { 'global' }, short_desc = N_('terminal takes care of bi-directionality'), - type = 'bool', + type = 'boolean', varname = 'p_tbidi', }, { @@ -8764,12 +8780,16 @@ return { Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight| attributes instead of "cterm" attributes. |guifg| Requires an ISO-8613-3 compatible terminal. + + Nvim will automatically attempt to determine if the host terminal + supports 24-bit color and will enable this option if it does + (unless explicitly disabled by the user). ]=], full_name = 'termguicolors', redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('Terminal true color support'), - type = 'bool', + type = 'boolean', varname = 'p_tgc', }, { @@ -8816,7 +8836,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('synchronize redraw output with the host terminal'), - type = 'bool', + type = 'boolean', varname = 'p_termsync', }, { @@ -8824,7 +8844,7 @@ return { full_name = 'terse', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -8840,7 +8860,7 @@ return { When 'formatexpr' is set it will be used to break the line. ]=], full_name = 'textwidth', - redraw = { 'current_buffer' }, + redraw = { 'current_buffer', 'highlight_only' }, scope = { 'buffer' }, short_desc = N_('maximum width of text that is being inserted'), type = 'number', @@ -8906,7 +8926,7 @@ return { full_name = 'tildeop', scope = { 'global' }, short_desc = N_('tilde command "~" behaves like an operator'), - type = 'bool', + type = 'boolean', varname = 'p_to', }, { @@ -8921,7 +8941,7 @@ return { full_name = 'timeout', scope = { 'global' }, short_desc = N_('time out on mappings and key codes'), - type = 'bool', + type = 'boolean', varname = 'p_timeout', }, { @@ -8955,7 +8975,7 @@ return { full_name = 'title', scope = { 'global' }, short_desc = N_('Vim set the title of the window'), - type = 'bool', + type = 'boolean', varname = 'p_title', }, { @@ -9005,13 +9025,13 @@ return { expanded according to the rules used for 'statusline'. This option cannot be set in a modeline when 'modelineexpr' is off. - Example: > - :auto BufEnter * let &titlestring = hostname() .. "/" .. expand("%:p") - :set title titlestring=%<%F%=%l/%L-%P titlelen=70 + Example: >vim + auto BufEnter * let &titlestring = hostname() .. "/" .. expand("%:p") + set title titlestring=%<%F%=%l/%L-%P titlelen=70 < The value of 'titlelen' is used to align items in the middle or right of the available space. - Some people prefer to have the file name first: > - :set titlestring=%t%(\ %M%)%(\ (%{expand(\"%:~:.:h\")})%)%(\ %a%) + Some people prefer to have the file name first: >vim + set titlestring=%t%(\ %M%)%(\ (%{expand(\"%:~:.:h\")})%)%(\ %a%) < Note the use of "%{ }" and an expression to get the path of the file, without the file name. The "%( %)" constructs are used to add a separating space only when needed. @@ -9044,7 +9064,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('out on mappings'), - type = 'bool', + type = 'boolean', varname = 'p_ttimeout', }, { @@ -9069,7 +9089,7 @@ return { no_mkrc = true, scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -9125,7 +9145,7 @@ return { full_name = 'undofile', scope = { 'buffer' }, short_desc = N_('save undo information in a file'), - type = 'bool', + type = 'boolean', varname = 'p_udf', }, { @@ -9137,13 +9157,13 @@ return { is kept in memory, higher numbers will cause more memory to be used. Nevertheless, a single change can already use a large amount of memory. Set to 0 for Vi compatibility: One level of undo and "u" undoes - itself: > + itself: >vim set ul=0 < But you can also get Vi compatibility by including the 'u' flag in 'cpoptions', and still be able to use CTRL-R to repeat undo. Also see |undo-two-ways|. Set to -1 for no undo at all. You might want to do this only for the - current buffer: > + current buffer: >vim setlocal ul=-1 < This helps when you run out of memory for a single change. @@ -9217,6 +9237,64 @@ return { varname = 'p_ut', }, { + abbreviation='urf', + full_name='userregfunc', + desc= [=[ + This option specifies a function to be used to handle any registers + that Neovim does not natively handle. This option unlocks all + characters to be used as registers by the user. + + The 'userregfunc' function is called each time a user register is read + from or written to. + + The 'userregfunc' function must take the following parameters: + + {action} The action being done on this register (either 'yank' + or 'put' + + {register} The string holding the name of the register. This + is always a single character, though multi-byte + characters are allowed. + + {content} If the action is 'yank' this is the content being + yanked into the register. The content is a dictionary + with the following items: + + {lines} The lines being yanked, as a list. + + {type} The type of yank, either "line", "char", or + "block" + + {width} The width in case of "block" mode. + + {additional_data} Additional data. (can be returned in + put mode). + + In case the action is 'put', the 'userregfunc' function should return + the content to place in that location. The content can either be a + string, in which case "char" mode is inferred, or it can return a + dictionary of the same template that populates 'content'. + + A very simple example of a 'userregfunc' function that behaves exactly + like traditional registers would look like: > + + let s:contents = {} + function! MyUserregFunction(action, register, content) abort + if a:action == "put" + return get(s:contents, a:register, "") + else + let s:contents[a:register] = a:content + endif + endfunction + set userregfunc=MyUserregFunction +< + ]=], + short_desc=N_("Function used to define behavior of user-defined registers."), + type='string', scope={'buffer'}, + varname='p_urf', + defaults={if_true=""} + }, + { abbreviation = 'vsts', cb = 'did_set_varsofttabstop', defaults = { if_true = '' }, @@ -9229,8 +9307,8 @@ return { For example, when editing assembly language files where statements start in the 9th column and comments in the 41st, it may be useful - to use the following: > - :set varsofttabstop=8,32,8 + to use the following: >vim + set varsofttabstop=8,32,8 < This will set soft tabstops with 8 and 8 + 32 spaces, and 8 more for every column thereafter. @@ -9251,8 +9329,8 @@ return { desc = [=[ A list of the number of spaces that a <Tab> in the file counts for, separated by commas. Each value corresponds to one tab, with the - final value applying to all subsequent tabs. For example: > - :set vartabstop=4,20,10,8 + final value applying to all subsequent tabs. For example: >vim + set vartabstop=4,20,10,8 < This will make the first tab 4 spaces wide, the second 20 spaces, the third 10 spaces, and all following tabs 8 spaces. @@ -9273,15 +9351,15 @@ return { desc = [=[ Sets the verbosity level. Also set by |-V| and |:verbose|. - Tracing of options in Lua scripts is activated at level 1; Lua scripts - are not traced with verbose=0, for performance. + Tracing of assignments to options, mappings, etc. in Lua scripts is + enabled at level 1; Lua scripts are not traced when 'verbose' is 0, + for performance. If greater than or equal to a given level, Nvim produces the following messages: Level Messages ~ ---------------------------------------------------------------------- - 1 Lua assignments to options, mappings, etc. 2 When a file is ":source"'ed, or |shada| file is read or written. 3 UI info, terminal capabilities. 4 Shell commands. @@ -9371,22 +9449,6 @@ return { varname = 'p_vop', }, { - abbreviation = 'vi', - full_name = 'viminfo', - nodefault = true, - scope = { 'global' }, - short_desc = N_('Alias for shada'), - type = 'string', - }, - { - abbreviation = 'vif', - full_name = 'viminfofile', - nodefault = true, - scope = { 'global' }, - short_desc = N_('Alias for shadafile instead'), - type = 'string', - }, - { abbreviation = 've', cb = 'did_set_virtualedit', defaults = { if_true = '' }, @@ -9437,7 +9499,7 @@ return { full_name = 'visualbell', scope = { 'global' }, short_desc = N_('use visual bell instead of beeping'), - type = 'bool', + type = 'boolean', varname = 'p_vb', }, { @@ -9449,7 +9511,7 @@ return { full_name = 'warn', scope = { 'global' }, short_desc = N_('for shell command when buffer was changed'), - type = 'bool', + type = 'boolean', varname = 'p_warn', }, { @@ -9470,8 +9532,8 @@ return { ~ "~" Normal [ <Left> Insert and Replace ] <Right> Insert and Replace - For example: > - :set ww=<,>,[,] + For example: >vim + set ww=<,>,[,] < allows wrap only when cursor keys are used. When the movement keys are used in combination with a delete or change operator, the <EOL> also counts for a character. This makes "3h" @@ -9508,8 +9570,8 @@ return { Some keys will not work, such as CTRL-C, <CR> and Enter. <Esc> can be used, but hitting it twice in a row will still exit command-line as a failsafe measure. - Although 'wc' is a number option, you can set it to a special key: > - :set wc=<Tab> + Although 'wc' is a number option, you can set it to a special key: >vim + set wc=<Tab> < ]=], full_name = 'wildchar', @@ -9527,9 +9589,9 @@ return { recognized when used inside a macro. You can find "spare" command-line keys suitable for this option by looking at |ex-edit-index|. Normally you'll never actually type 'wildcharm', just use it in mappings that - automatically invoke completion mode, e.g.: > - :set wcm=<C-Z> - :cnoremap ss so $vim/sessions/*.vim<C-Z> + automatically invoke completion mode, e.g.: >vim + set wcm=<C-Z> + cnoremap ss so $vim/sessions/*.vim<C-Z> < Then after typing :ss you can use CTRL-P & CTRL-N. ]=], full_name = 'wildcharm', @@ -9549,8 +9611,8 @@ return { |globpath()| unless a flag is passed to disable this. The pattern is used like with |:autocmd|, see |autocmd-pattern|. Also see 'suffixes'. - Example: > - :set wildignore=*.o,*.obj + Example: >vim + set wildignore=*.o,*.obj < The use of |:set+=| and |:set-=| is preferred when adding or removing a pattern from the list. This avoids problems when a future version uses another default. @@ -9574,7 +9636,7 @@ return { full_name = 'wildignorecase', scope = { 'global' }, short_desc = N_('ignore case when completing file names'), - type = 'bool', + type = 'boolean', varname = 'p_wic', }, { @@ -9613,16 +9675,16 @@ return { completion. If you want <Left> and <Right> to move the cursor instead of selecting - a different match, use this: > - :cnoremap <Left> <Space><BS><Left> - :cnoremap <Right> <Space><BS><Right> + a different match, use this: >vim + cnoremap <Left> <Space><BS><Left> + cnoremap <Right> <Space><BS><Right> < |hl-WildMenu| highlights the current match. ]=], full_name = 'wildmenu', scope = { 'global' }, short_desc = N_('use menu for command line completion'), - type = 'bool', + type = 'boolean', varname = 'p_wmnu', }, { @@ -9662,16 +9724,16 @@ return { and sort buffers by time last used (other than the current buffer). - Examples: > - :set wildmode=full - < Complete first full match, next match, etc. (the default) > - :set wildmode=longest,full - < Complete longest common string, then each full match > - :set wildmode=list:full - < List all matches and complete each full match > - :set wildmode=list,full - < List all matches without completing, then each full match > - :set wildmode=longest,list + Examples: >vim + set wildmode=full + < Complete first full match, next match, etc. (the default) >vim + set wildmode=longest,full + < Complete longest common string, then each full match >vim + set wildmode=list:full + < List all matches and complete each full match >vim + set wildmode=list,full + < List all matches without completing, then each full match >vim + set wildmode=longest,list < Complete longest common string, then list alternatives. More info here: |cmdline-completion|. ]=], @@ -9783,45 +9845,12 @@ return { UI-dependent. Works best with RGB colors. 'termguicolors' ]=], full_name = 'winblend', - redraw = { 'current_window' }, + redraw = { 'current_window', 'highlight_only' }, scope = { 'window' }, short_desc = N_('Controls transparency level for floating windows'), type = 'number', }, { - abbreviation = 'winhl', - alloced = true, - cb = 'did_set_winhighlight', - defaults = { if_true = '' }, - deny_duplicates = true, - desc = [=[ - Window-local highlights. Comma-delimited list of highlight - |group-name| pairs "{hl-from}:{hl-to},..." where each {hl-from} is - a |highlight-groups| item to be overridden by {hl-to} group in - the window. - - Note: highlight namespaces take precedence over 'winhighlight'. - See |nvim_win_set_hl_ns()| and |nvim_set_hl()|. - - Highlights of vertical separators are determined by the window to the - left of the separator. The 'tabline' highlight of a tabpage is - decided by the last-focused window of the tabpage. Highlights of - the popupmenu are determined by the current window. Highlights in the - message area cannot be overridden. - - Example: show a different color for non-current windows: > - set winhighlight=Normal:MyNormal,NormalNC:MyNormalNC - < - ]=], - expand_cb = 'expand_set_winhighlight', - full_name = 'winhighlight', - list = 'onecommacolon', - redraw = { 'current_window' }, - scope = { 'window' }, - short_desc = N_('Setup window-local highlights'), - type = 'string', - }, - { abbreviation = 'wi', cb = 'did_set_window', defaults = { @@ -9846,6 +9875,35 @@ return { varname = 'p_window', }, { + abbreviation = 'wfh', + defaults = { if_true = false }, + desc = [=[ + Keep the window height when windows are opened or closed and + 'equalalways' is set. Also for |CTRL-W_=|. Set by default for the + |preview-window| and |quickfix-window|. + The height may be changed anyway when running out of room. + ]=], + full_name = 'winfixheight', + redraw = { 'statuslines' }, + scope = { 'window' }, + short_desc = N_('keep window height when opening/closing windows'), + type = 'boolean', + }, + { + abbreviation = 'wfw', + defaults = { if_true = false }, + desc = [=[ + Keep the window width when windows are opened or closed and + 'equalalways' is set. Also for |CTRL-W_=|. + The width may be changed anyway when running out of room. + ]=], + full_name = 'winfixwidth', + redraw = { 'statuslines' }, + scope = { 'window' }, + short_desc = N_('keep window width when opening/closing windows'), + type = 'boolean', + }, + { abbreviation = 'wh', cb = 'did_set_winheight', defaults = { if_true = 1 }, @@ -9859,7 +9917,7 @@ return { Other windows will be only 'winminheight' high. This has the drawback that ":all" will create only two windows. To avoid "vim -o 1 2 3 4" to create only two windows, set the option after startup is done, - using the |VimEnter| event: > + using the |VimEnter| event: >vim au VimEnter * set winheight=999 < Minimum value is 1. The height is not adjusted after one of the commands that change the @@ -9875,33 +9933,37 @@ return { varname = 'p_wh', }, { - abbreviation = 'wfh', - defaults = { if_true = false }, - desc = [=[ - Keep the window height when windows are opened or closed and - 'equalalways' is set. Also for |CTRL-W_=|. Set by default for the - |preview-window| and |quickfix-window|. - The height may be changed anyway when running out of room. - ]=], - full_name = 'winfixheight', - redraw = { 'statuslines' }, - scope = { 'window' }, - short_desc = N_('keep window height when opening/closing windows'), - type = 'bool', - }, - { - abbreviation = 'wfw', - defaults = { if_true = false }, + abbreviation = 'winhl', + alloced = true, + cb = 'did_set_winhighlight', + defaults = { if_true = '' }, + deny_duplicates = true, desc = [=[ - Keep the window width when windows are opened or closed and - 'equalalways' is set. Also for |CTRL-W_=|. - The width may be changed anyway when running out of room. + Window-local highlights. Comma-delimited list of highlight + |group-name| pairs "{hl-from}:{hl-to},..." where each {hl-from} is + a |highlight-groups| item to be overridden by {hl-to} group in + the window. + + Note: highlight namespaces take precedence over 'winhighlight'. + See |nvim_win_set_hl_ns()| and |nvim_set_hl()|. + + Highlights of vertical separators are determined by the window to the + left of the separator. The 'tabline' highlight of a tabpage is + decided by the last-focused window of the tabpage. Highlights of + the popupmenu are determined by the current window. Highlights in the + message area cannot be overridden. + + Example: show a different color for non-current windows: >vim + set winhighlight=Normal:MyNormal,NormalNC:MyNormalNC + < ]=], - full_name = 'winfixwidth', - redraw = { 'statuslines' }, + expand_cb = 'expand_set_winhighlight', + full_name = 'winhighlight', + list = 'onecommacolon', + redraw = { 'current_window', 'highlight_only' }, scope = { 'window' }, - short_desc = N_('keep window width when opening/closing windows'), - type = 'bool', + short_desc = N_('Setup window-local highlights'), + type = 'string', }, { abbreviation = 'wmh', @@ -9981,9 +10043,9 @@ return { horizontally. The line will be broken in the middle of a word if necessary. See 'linebreak' to get the break at a word boundary. - To make scrolling horizontally a bit more useful, try this: > - :set sidescroll=5 - :set listchars+=precedes:<,extends:> + To make scrolling horizontally a bit more useful, try this: >vim + set sidescroll=5 + set listchars+=precedes:<,extends:> < See 'sidescroll', 'listchars' and |wrap-off|. This option can't be set from a |modeline| when the 'diff' option is on. @@ -9992,7 +10054,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('lines wrap and continue on the next line'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'wm', @@ -10023,7 +10085,7 @@ return { scope = { 'global' }, short_desc = N_('searches wrap around the end of the file'), tags = { 'E384', 'E385' }, - type = 'bool', + type = 'boolean', varname = 'p_ws', }, { @@ -10038,7 +10100,7 @@ return { full_name = 'write', scope = { 'global' }, short_desc = N_('to a file is allowed'), - type = 'bool', + type = 'boolean', varname = 'p_write', }, { @@ -10050,7 +10112,7 @@ return { full_name = 'writeany', scope = { 'global' }, short_desc = N_('write to file with no need for "!" override'), - type = 'bool', + type = 'boolean', varname = 'p_wa', }, { @@ -10073,7 +10135,7 @@ return { full_name = 'writebackup', scope = { 'global' }, short_desc = N_('make a backup before overwriting a file'), - type = 'bool', + type = 'boolean', varname = 'p_wb', }, { diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 281ec86171..4be08b28f5 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -19,9 +19,9 @@ #include "nvim/eval/vars.h" #include "nvim/ex_getln.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" +#include "nvim/grid.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" @@ -39,6 +39,7 @@ #include "nvim/os/os.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/spell.h" #include "nvim/spellfile.h" #include "nvim/spellsuggest.h" @@ -61,6 +62,10 @@ static const char e_backupext_and_patchmode_are_equal[] = N_("E589: 'backupext' and 'patchmode' are equal"); static const char e_showbreak_contains_unprintable_or_wide_character[] = N_("E595: 'showbreak' contains unprintable or wide character"); +static const char e_wrong_number_of_characters_for_field_str[] + = N_("E1511: Wrong number of characters for field \"%s\""); +static const char e_wrong_character_width_for_field_str[] + = N_("E1512: Wrong character width for field \"%s\""); static char *(p_ambw_values[]) = { "single", "double", NULL }; static char *(p_bg_values[]) = { "light", "dark", NULL }; @@ -117,7 +122,7 @@ static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent", "syntax", "diff", NULL }; static char *(p_fcl_values[]) = { "all", NULL }; static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "noinsert", "noselect", - NULL }; + "popup", NULL }; #ifdef BACKSLASH_IN_FILENAME static char *(p_csl_values[]) = { "slash", "backslash", NULL }; #endif @@ -149,21 +154,21 @@ static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_LINES, /// option values. void didset_string_options(void) { - (void)opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true); - (void)opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, true); - (void)opt_strings_flags(p_bo, p_bo_values, &bo_flags, true); - (void)opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true); - (void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true); - (void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true); - (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true); - (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true); - (void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true); - (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false); - (void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true); - (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true); - (void)opt_strings_flags(p_swb, p_swb_values, &swb_flags, true); - (void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true); - (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true); + opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true); + opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, true); + opt_strings_flags(p_bo, p_bo_values, &bo_flags, true); + opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true); + opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true); + opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true); + opt_strings_flags(p_dy, p_dy_values, &dy_flags, true); + opt_strings_flags(p_jop, p_jop_values, &jop_flags, true); + opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true); + opt_strings_flags(p_tc, p_tc_values, &tc_flags, false); + opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true); + opt_strings_flags(p_ve, p_ve_values, &ve_flags, true); + opt_strings_flags(p_swb, p_swb_values, &swb_flags, true); + opt_strings_flags(p_wop, p_wop_values, &wop_flags, true); + opt_strings_flags(p_cb, p_cb_values, &cb_flags, true); } char *illegal_char(char *errbuf, size_t errbuflen, int c) @@ -284,30 +289,17 @@ static void set_string_option_global(vimoption_T *opt, char **varp) /// Set a string option to a new value (without checking the effect). /// The string is copied into allocated memory. -/// if ("opt_idx" == -1) "name" is used, otherwise "opt_idx" is used. +/// if ("opt_idx" == kOptInvalid) "name" is used, otherwise "opt_idx" is used. /// When "set_sid" is zero set the scriptID to current_sctx.sc_sid. When /// "set_sid" is SID_NONE don't set the scriptID. Otherwise set the scriptID to /// "set_sid". /// -/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL. +/// @param opt_flags Option flags. /// /// TODO(famiu): Remove this and its win/buf variants. -void set_string_option_direct(const char *name, int opt_idx, const char *val, int opt_flags, - int set_sid) +void set_string_option_direct(OptIndex opt_idx, const char *val, int opt_flags, scid_T set_sid) { - int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; - int idx = opt_idx; - - if (idx == -1) { // Use name. - idx = findoption(name); - if (idx < 0) { // Not found (should not happen). - internal_error("set_string_option_direct()"); - siemsg(_("For option %s"), name); - return; - } - } - - vimoption_T *opt = get_option(idx); + vimoption_T *opt = get_option(opt_idx); if (opt->var == NULL) { // can't set hidden option return; @@ -315,53 +307,53 @@ void set_string_option_direct(const char *name, int opt_idx, const char *val, in assert(opt->var != &p_shada); + bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; char *s = xstrdup(val); - { - char **varp = (char **)get_varp_scope(opt, both ? OPT_LOCAL : opt_flags); - if ((opt_flags & OPT_FREE) && (opt->flags & P_ALLOCED)) { - free_string_option(*varp); - } - *varp = s; + char **varp = (char **)get_varp_scope(opt, both ? OPT_LOCAL : opt_flags); - // For buffer/window local option may also set the global value. - if (both) { - set_string_option_global(opt, varp); - } + if (opt->flags & P_ALLOCED) { + free_string_option(*varp); + } + *varp = s; - opt->flags |= P_ALLOCED; + // For buffer/window local option may also set the global value. + if (both) { + set_string_option_global(opt, varp); + } - // When setting both values of a global option with a local value, - // make the local value empty, so that the global value is used. - if ((opt->indir & PV_BOTH) && both) { - free_string_option(*varp); - *varp = empty_string_option; - } - if (set_sid != SID_NONE) { - sctx_T script_ctx; + opt->flags |= P_ALLOCED; - if (set_sid == 0) { - script_ctx = current_sctx; - } else { - script_ctx.sc_sid = set_sid; - script_ctx.sc_seq = 0; - script_ctx.sc_lnum = 0; - } - set_option_sctx_idx(idx, opt_flags, script_ctx); + // When setting both values of a global option with a local value, + // make the local value empty, so that the global value is used. + if ((opt->indir & PV_BOTH) && both) { + free_string_option(*varp); + *varp = empty_string_option; + } + if (set_sid != SID_NONE) { + sctx_T script_ctx; + + if (set_sid == 0) { + script_ctx = current_sctx; + } else { + script_ctx.sc_sid = set_sid; + script_ctx.sc_seq = 0; + script_ctx.sc_lnum = 0; } + set_option_sctx(opt_idx, opt_flags, script_ctx); } } /// Like set_string_option_direct(), but for a window-local option in "wp". /// Blocks autocommands to avoid the old curwin becoming invalid. -void set_string_option_direct_in_win(win_T *wp, const char *name, int opt_idx, const char *val, - int opt_flags, int set_sid) +void set_string_option_direct_in_win(win_T *wp, OptIndex opt_idx, const char *val, int opt_flags, + int set_sid) { win_T *save_curwin = curwin; block_autocmds(); curwin = wp; curbuf = curwin->w_buffer; - set_string_option_direct(name, opt_idx, val, opt_flags, set_sid); + set_string_option_direct(opt_idx, val, opt_flags, set_sid); curwin = save_curwin; curbuf = curwin->w_buffer; unblock_autocmds(); @@ -369,14 +361,14 @@ void set_string_option_direct_in_win(win_T *wp, const char *name, int opt_idx, c /// Like set_string_option_direct(), but for a buffer-local option in "buf". /// Blocks autocommands to avoid the old curwin becoming invalid. -void set_string_option_direct_in_buf(buf_T *buf, const char *name, int opt_idx, const char *val, - int opt_flags, int set_sid) +void set_string_option_direct_in_buf(buf_T *buf, OptIndex opt_idx, const char *val, int opt_flags, + int set_sid) { buf_T *save_curbuf = curbuf; block_autocmds(); curbuf = buf; - set_string_option_direct(name, opt_idx, val, opt_flags, set_sid); + set_string_option_direct(opt_idx, val, opt_flags, set_sid); curbuf = save_curbuf; unblock_autocmds(); } @@ -443,7 +435,7 @@ int check_signcolumn(win_T *wp) const char *check_stl_option(char *s) { int groupdepth = 0; - static char errbuf[80]; + static char errbuf[ERR_BUFLEN]; while (*s) { // Check for valid keys after % sequences @@ -677,12 +669,17 @@ int expand_set_ambiwidth(optexpand_T *args, int *numMatches, char ***matches) } /// The 'background' option is changed. -const char *did_set_background(optset_T *args FUNC_ATTR_UNUSED) +const char *did_set_background(optset_T *args) { if (check_opt_strings(p_bg, p_bg_values, false) != OK) { return e_invarg; } + if (args->os_oldval.string.data[0] == *p_bg) { + // Value was not changed + return NULL; + } + int dark = (*p_bg == 'd'); init_highlight(false, false); @@ -757,7 +754,7 @@ const char *did_set_backupcopy(optset_T *args) + ((*flags & BKC_YES) != 0) + ((*flags & BKC_NO) != 0) != 1) { // Must have exactly one of "auto", "yes" and "no". - (void)opt_strings_flags(oldval, p_bkc_values, flags, true); + opt_strings_flags(oldval, p_bkc_values, flags, true); return e_invarg; } } @@ -800,6 +797,22 @@ int expand_set_belloff(optexpand_T *args, int *numMatches, char ***matches) matches); } +/// The 'breakat' option is changed. +const char *did_set_breakat(optset_T *args FUNC_ATTR_UNUSED) +{ + for (int i = 0; i < 256; i++) { + breakat_flags[i] = false; + } + + if (p_breakat != NULL) { + for (char *p = p_breakat; *p; p++) { + breakat_flags[(uint8_t)(*p)] = true; + } + } + + return NULL; +} + /// The 'breakindentopt' option is changed. const char *did_set_breakindentopt(optset_T *args) { @@ -885,18 +898,17 @@ int expand_set_casemap(optexpand_T *args, int *numMatches, char ***matches) } /// The global 'listchars' or 'fillchars' option is changed. -static const char *did_set_global_listfillchars(win_T *win, char *val, bool opt_lcs, int opt_flags) +static const char *did_set_global_chars_option(win_T *win, char *val, CharsOption what, + int opt_flags, char *errbuf, size_t errbuflen) { const char *errmsg = NULL; - char **local_ptr = opt_lcs ? &win->w_p_lcs : &win->w_p_fcs; + char **local_ptr = (what == kListchars) ? &win->w_p_lcs : &win->w_p_fcs; // only apply the global value to "win" when it does not have a // local value - if (opt_lcs) { - errmsg = set_listchars_option(win, val, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL)); - } else { - errmsg = set_fillchars_option(win, val, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL)); - } + errmsg = set_chars_option(win, val, what, + **local_ptr == NUL || !(opt_flags & OPT_GLOBAL), + errbuf, errbuflen); if (errmsg != NULL) { return errmsg; } @@ -912,14 +924,9 @@ static const char *did_set_global_listfillchars(win_T *win, char *val, bool opt_ // again, it was changed when setting the global value. // If no error was returned above, we don't expect an error // here, so ignore the return value. - if (opt_lcs) { - if (*wp->w_p_lcs == NUL) { - (void)set_listchars_option(wp, wp->w_p_lcs, true); - } - } else { - if (*wp->w_p_fcs == NUL) { - (void)set_fillchars_option(wp, wp->w_p_fcs, true); - } + char *opt = (what == kListchars) ? wp->w_p_lcs : wp->w_p_fcs; + if (*opt == NUL) { + set_chars_option(wp, opt, what, true, errbuf, errbuflen); } } @@ -935,13 +942,18 @@ const char *did_set_chars_option(optset_T *args) char **varp = (char **)args->os_varp; const char *errmsg = NULL; - if (varp == &p_lcs // global 'listchars' - || varp == &p_fcs) { // global 'fillchars' - errmsg = did_set_global_listfillchars(win, *varp, varp == &p_lcs, args->os_flags); + if (varp == &p_lcs) { // global 'listchars' + errmsg = did_set_global_chars_option(win, *varp, kListchars, args->os_flags, + args->os_errbuf, args->os_errbuflen); + } else if (varp == &p_fcs) { // global 'fillchars' + errmsg = did_set_global_chars_option(win, *varp, kFillchars, args->os_flags, + args->os_errbuf, args->os_errbuflen); } else if (varp == &win->w_p_lcs) { // local 'listchars' - errmsg = set_listchars_option(win, *varp, true); + errmsg = set_chars_option(win, *varp, kListchars, true, + args->os_errbuf, args->os_errbuflen); } else if (varp == &win->w_p_fcs) { // local 'fillchars' - errmsg = set_fillchars_option(win, *varp, true); + errmsg = set_chars_option(win, *varp, kFillchars, true, + args->os_errbuf, args->os_errbuflen); } return errmsg; @@ -1229,7 +1241,7 @@ const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED) if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) { return e_invarg; } - (void)init_chartab(); + init_chartab(); msg_grid_validate(); return NULL; } @@ -1434,7 +1446,7 @@ int expand_set_foldcolumn(optexpand_T *args, int *numMatches, char ***matches) const char *did_set_foldexpr(optset_T *args) { win_T *win = (win_T *)args->os_win; - (void)did_set_optexpr(args); + did_set_optexpr(args); if (foldmethodIsExpr(win)) { foldUpdateAll(win); } @@ -1529,7 +1541,15 @@ int expand_set_formatoptions(optexpand_T *args, int *numMatches, char ***matches /// The 'guicursor' option is changed. const char *did_set_guicursor(optset_T *args FUNC_ATTR_UNUSED) { - return parse_shape_opt(SHAPE_CURSOR); + const char *errmsg = parse_shape_opt(SHAPE_CURSOR); + if (errmsg != NULL) { + return errmsg; + } + if (VIsual_active) { + // In Visual mode cursor may be drawn differently. + redrawWinline(curwin, curwin->w_cursor.lnum); + } + return NULL; } /// The 'helpfile' option is changed. @@ -1946,6 +1966,10 @@ const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED) if (*p_sel == NUL || check_opt_strings(p_sel, p_sel_values, false) != OK) { return e_invarg; } + if (VIsual_active) { + // Visual selection may be drawn differently. + redraw_curbuf_later(UPD_INVERTED); + } return NULL; } @@ -1982,7 +2006,7 @@ const char *did_set_sessionoptions(optset_T *args) if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) { // Don't allow both "sesdir" and "curdir". const char *oldval = args->os_oldval.string.data; - (void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true); + opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true); return e_invarg; } return NULL; @@ -2076,7 +2100,13 @@ const char *did_set_showbreak(optset_T *args) /// The 'showcmdloc' option is changed. const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED) { - return did_set_opt_strings(p_sloc, p_sloc_values, true); + const char *errmsg = did_set_opt_strings(p_sloc, p_sloc_values, false); + + if (errmsg == NULL) { + comp_col(); + } + + return errmsg; } int expand_set_showcmdloc(optexpand_T *args, int *numMatches, char ***matches) @@ -2096,6 +2126,8 @@ const char *did_set_signcolumn(optset_T *args) if (check_signcolumn(win) != OK) { return e_invarg; } + int scwidth = win->w_minscwidth <= 0 ? 0 : MIN(win->w_maxscwidth, win->w_scwidth); + win->w_scwidth = MAX(win->w_minscwidth, scwidth); // When changing the 'signcolumn' to or from 'number', recompute the // width of the number column if 'number' or 'relativenumber' is set. if ((*oldval == 'n' && *(oldval + 1) == 'u') || win->w_minscwidth == SCL_NUM) { @@ -2610,7 +2642,7 @@ static const char e_conflicts_with_value_of_fillchars[] /// Calls mb_cptr2char_adv(p) and returns the character. /// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used. /// Returns 0 for invalid hex or invalid UTF-8 byte. -static int get_encoded_char_adv(const char **p) +static schar_T get_encoded_char_adv(const char **p) { const char *s = *p; @@ -2625,71 +2657,82 @@ static int get_encoded_char_adv(const char **p) num = num * 256 + n; } *p += 2; - return (int)num; + return (char2cells((int)num) > 1) ? 0 : schar_from_char((int)num); } - // TODO(bfredl): use schar_T representation and utfc_ptr2len - int clen = utf_ptr2len(s); - int c = mb_cptr2char_adv(p); - if (clen == 1 && c > 127) { // Invalid UTF-8 byte - return 0; - } - return c; + int clen = utfc_ptr2len(s); + int firstc; + schar_T c = utfc_ptr2schar(s, &firstc); + *p += clen; + // Invalid UTF-8 byte or doublewidth not allowed + return ((clen == 1 && firstc > 127) || char2cells(firstc) > 1) ? 0 : c; } struct chars_tab { - int *cp; ///< char value + schar_T *cp; ///< char value const char *name; ///< char id - int def; ///< default value - int fallback; ///< default value when "def" isn't single-width + const char *def; ///< default value + const char *fallback; ///< default value when "def" isn't single-width }; static fcs_chars_T fcs_chars; static const struct chars_tab fcs_tab[] = { - { &fcs_chars.stl, "stl", ' ', NUL }, - { &fcs_chars.stlnc, "stlnc", ' ', NUL }, - { &fcs_chars.wbr, "wbr", ' ', NUL }, - { &fcs_chars.horiz, "horiz", 0x2500, '-' }, // ─ - { &fcs_chars.horizup, "horizup", 0x2534, '-' }, // ┴ - { &fcs_chars.horizdown, "horizdown", 0x252c, '-' }, // ┬ - { &fcs_chars.vert, "vert", 0x2502, '|' }, // │ - { &fcs_chars.vertleft, "vertleft", 0x2524, '|' }, // ┤ - { &fcs_chars.vertright, "vertright", 0x251c, '|' }, // ├ - { &fcs_chars.verthoriz, "verthoriz", 0x253c, '+' }, // ┼ - { &fcs_chars.fold, "fold", 0x00b7, '-' }, // · - { &fcs_chars.foldopen, "foldopen", '-', NUL }, - { &fcs_chars.foldclosed, "foldclose", '+', NUL }, - { &fcs_chars.foldsep, "foldsep", 0x2502, '|' }, // │ - { &fcs_chars.diff, "diff", '-', NUL }, - { &fcs_chars.msgsep, "msgsep", ' ', NUL }, - { &fcs_chars.eob, "eob", '~', NUL }, - { &fcs_chars.lastline, "lastline", '@', NUL }, + { &fcs_chars.stl, "stl", " ", NULL }, + { &fcs_chars.stlnc, "stlnc", " ", NULL }, + { &fcs_chars.wbr, "wbr", " ", NULL }, + { &fcs_chars.horiz, "horiz", "─", "-" }, + { &fcs_chars.horizup, "horizup", "┴", "-" }, + { &fcs_chars.horizdown, "horizdown", "┬", "-" }, + { &fcs_chars.vert, "vert", "│", "|" }, + { &fcs_chars.vertleft, "vertleft", "┤", "|" }, + { &fcs_chars.vertright, "vertright", "├", "|" }, + { &fcs_chars.verthoriz, "verthoriz", "┼", "+" }, + { &fcs_chars.fold, "fold", "·", "-" }, + { &fcs_chars.foldopen, "foldopen", "-", NULL }, + { &fcs_chars.foldclosed, "foldclose", "+", NULL }, + { &fcs_chars.foldsep, "foldsep", "│", "|" }, + { &fcs_chars.diff, "diff", "-", NULL }, + { &fcs_chars.msgsep, "msgsep", " ", NULL }, + { &fcs_chars.eob, "eob", "~", NULL }, + { &fcs_chars.lastline, "lastline", "@", NULL }, }; static lcs_chars_T lcs_chars; static const struct chars_tab lcs_tab[] = { - { &lcs_chars.eol, "eol", NUL, NUL }, - { &lcs_chars.ext, "extends", NUL, NUL }, - { &lcs_chars.nbsp, "nbsp", NUL, NUL }, - { &lcs_chars.prec, "precedes", NUL, NUL }, - { &lcs_chars.space, "space", NUL, NUL }, - { &lcs_chars.tab2, "tab", NUL, NUL }, - { &lcs_chars.lead, "lead", NUL, NUL }, - { &lcs_chars.trail, "trail", NUL, NUL }, - { &lcs_chars.conceal, "conceal", NUL, NUL }, - { NULL, "multispace", NUL, NUL }, - { NULL, "leadmultispace", NUL, NUL }, + { &lcs_chars.eol, "eol", NULL, NULL }, + { &lcs_chars.ext, "extends", NULL, NULL }, + { &lcs_chars.nbsp, "nbsp", NULL, NULL }, + { &lcs_chars.prec, "precedes", NULL, NULL }, + { &lcs_chars.space, "space", NULL, NULL }, + { &lcs_chars.tab2, "tab", NULL, NULL }, + { &lcs_chars.lead, "lead", NULL, NULL }, + { &lcs_chars.trail, "trail", NULL, NULL }, + { &lcs_chars.conceal, "conceal", NULL, NULL }, + { NULL, "multispace", NULL, NULL }, + { NULL, "leadmultispace", NULL, NULL }, }; +static char *field_value_err(char *errbuf, size_t errbuflen, const char *fmt, const char *field) +{ + if (errbuf == NULL) { + return ""; + } + vim_snprintf(errbuf, errbuflen, _(fmt), field); + return errbuf; +} + /// Handle setting 'listchars' or 'fillchars'. /// Assume monocell characters /// -/// @param value points to either the global or the window-local value. -/// @param is_listchars is true for "listchars" and false for "fillchars". -/// @param apply if false, do not store the flags, only check for errors. +/// @param value points to either the global or the window-local value. +/// @param what kListchars or kFillchars +/// @param apply if false, do not store the flags, only check for errors. +/// @param errbuf buffer for error message, can be NULL if it won't be used. +/// @param errbuflen size of error buffer. +/// /// @return error message, NULL if it's OK. -static const char *set_chars_option(win_T *wp, const char *value, const bool is_listchars, - const bool apply) +const char *set_chars_option(win_T *wp, const char *value, CharsOption what, bool apply, + char *errbuf, size_t errbuflen) { const char *last_multispace = NULL; // Last occurrence of "multispace:" const char *last_lmultispace = NULL; // Last occurrence of "leadmultispace:" @@ -2698,7 +2741,7 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ const struct chars_tab *tab; int entries; - if (is_listchars) { + if (what == kListchars) { tab = lcs_tab; entries = ARRAY_SIZE(lcs_tab); if (wp->w_p_lcs[0] == NUL) { @@ -2720,23 +2763,24 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ if (tab[i].cp != NULL) { // XXX: Characters taking 2 columns is forbidden (TUI limitation?). // Set old defaults in this case. - *(tab[i].cp) = char2cells(tab[i].def) == 1 ? tab[i].def : tab[i].fallback; + *(tab[i].cp) = schar_from_str((tab[i].def && ptr2cells(tab[i].def) == 1) + ? tab[i].def : tab[i].fallback); } } - if (is_listchars) { + if (what == kListchars) { lcs_chars.tab1 = NUL; lcs_chars.tab3 = NUL; if (multispace_len > 0) { - lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int)); + lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(schar_T)); lcs_chars.multispace[multispace_len] = NUL; } else { lcs_chars.multispace = NULL; } if (lead_multispace_len > 0) { - lcs_chars.leadmultispace = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(int)); + lcs_chars.leadmultispace = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(schar_T)); lcs_chars.leadmultispace[lead_multispace_len] = NUL; } else { lcs_chars.leadmultispace = NULL; @@ -2749,34 +2793,36 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ int i; for (i = 0; i < entries; i++) { const size_t len = strlen(tab[i].name); - if (!(strncmp(p, tab[i].name, len) == 0 - && p[len] == ':' - && p[len + 1] != NUL)) { + if (!(strncmp(p, tab[i].name, len) == 0 && p[len] == ':')) { continue; } - if (is_listchars && strcmp(tab[i].name, "multispace") == 0) { + if (what == kListchars && strcmp(tab[i].name, "multispace") == 0) { const char *s = p + len + 1; if (round == 0) { // Get length of lcs-multispace string in the first round last_multispace = p; multispace_len = 0; while (*s != NUL && *s != ',') { - int c1 = get_encoded_char_adv(&s); - if (c1 == 0 || char2cells(c1) > 1) { - return e_invarg; + schar_T c1 = get_encoded_char_adv(&s); + if (c1 == 0) { + return field_value_err(errbuf, errbuflen, + e_wrong_character_width_for_field_str, + tab[i].name); } multispace_len++; } if (multispace_len == 0) { // lcs-multispace cannot be an empty string - return e_invarg; + return field_value_err(errbuf, errbuflen, + e_wrong_number_of_characters_for_field_str, + tab[i].name); } p = s; } else { int multispace_pos = 0; while (*s != NUL && *s != ',') { - int c1 = get_encoded_char_adv(&s); + schar_T c1 = get_encoded_char_adv(&s); if (p == last_multispace) { lcs_chars.multispace[multispace_pos++] = c1; } @@ -2786,28 +2832,32 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ break; } - if (is_listchars && strcmp(tab[i].name, "leadmultispace") == 0) { + if (what == kListchars && strcmp(tab[i].name, "leadmultispace") == 0) { const char *s = p + len + 1; if (round == 0) { // get length of lcs-leadmultispace string in first round last_lmultispace = p; lead_multispace_len = 0; while (*s != NUL && *s != ',') { - int c1 = get_encoded_char_adv(&s); - if (c1 == 0 || char2cells(c1) > 1) { - return e_invarg; + schar_T c1 = get_encoded_char_adv(&s); + if (c1 == 0) { + return field_value_err(errbuf, errbuflen, + e_wrong_character_width_for_field_str, + tab[i].name); } lead_multispace_len++; } if (lead_multispace_len == 0) { // lcs-leadmultispace cannot be an empty string - return e_invarg; + return field_value_err(errbuf, errbuflen, + e_wrong_number_of_characters_for_field_str, + tab[i].name); } p = s; } else { int multispace_pos = 0; while (*s != NUL && *s != ',') { - int c1 = get_encoded_char_adv(&s); + schar_T c1 = get_encoded_char_adv(&s); if (p == last_lmultispace) { lcs_chars.leadmultispace[multispace_pos++] = c1; } @@ -2818,23 +2868,37 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ } const char *s = p + len + 1; - int c1 = get_encoded_char_adv(&s); - if (c1 == 0 || char2cells(c1) > 1) { - return e_invarg; + if (*s == NUL) { + return field_value_err(errbuf, errbuflen, + e_wrong_number_of_characters_for_field_str, + tab[i].name); + } + schar_T c1 = get_encoded_char_adv(&s); + if (c1 == 0) { + return field_value_err(errbuf, errbuflen, + e_wrong_character_width_for_field_str, + tab[i].name); } - int c2 = 0, c3 = 0; + schar_T c2 = 0; + schar_T c3 = 0; if (tab[i].cp == &lcs_chars.tab2) { if (*s == NUL) { - return e_invarg; + return field_value_err(errbuf, errbuflen, + e_wrong_number_of_characters_for_field_str, + tab[i].name); } c2 = get_encoded_char_adv(&s); - if (c2 == 0 || char2cells(c2) > 1) { - return e_invarg; + if (c2 == 0) { + return field_value_err(errbuf, errbuflen, + e_wrong_character_width_for_field_str, + tab[i].name); } if (!(*s == ',' || *s == NUL)) { c3 = get_encoded_char_adv(&s); - if (c3 == 0 || char2cells(c3) > 1) { - return e_invarg; + if (c3 == 0) { + return field_value_err(errbuf, errbuflen, + e_wrong_character_width_for_field_str, + tab[i].name); } } } @@ -2851,6 +2915,10 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ } p = s; break; + } else { + return field_value_err(errbuf, errbuflen, + e_wrong_number_of_characters_for_field_str, + tab[i].name); } } @@ -2865,7 +2933,7 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ } if (apply) { - if (is_listchars) { + if (what == kListchars) { xfree(wp->w_p_lcs_chars.multispace); xfree(wp->w_p_lcs_chars.leadmultispace); wp->w_p_lcs_chars = lcs_chars; @@ -2877,18 +2945,6 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ return NULL; // no error } -/// Handle the new value of 'fillchars'. -const char *set_fillchars_option(win_T *wp, char *val, bool apply) -{ - return set_chars_option(wp, val, false, apply); -} - -/// Handle the new value of 'listchars'. -const char *set_listchars_option(win_T *wp, char *val, bool apply) -{ - return set_chars_option(wp, val, true, apply); -} - /// Function given to ExpandGeneric() to obtain possible arguments of the /// 'fillchars' option. char *get_fillchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) @@ -2917,17 +2973,17 @@ char *get_listchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) /// @return an untranslated error message if any of them is invalid, NULL otherwise. const char *check_chars_options(void) { - if (set_listchars_option(curwin, p_lcs, false) != NULL) { + if (set_chars_option(curwin, p_lcs, kListchars, false, NULL, 0) != NULL) { return e_conflicts_with_value_of_listchars; } - if (set_fillchars_option(curwin, p_fcs, false) != NULL) { + if (set_chars_option(curwin, p_fcs, kFillchars, false, NULL, 0) != NULL) { return e_conflicts_with_value_of_fillchars; } FOR_ALL_TAB_WINDOWS(tp, wp) { - if (set_listchars_option(wp, wp->w_p_lcs, true) != NULL) { + if (set_chars_option(wp, wp->w_p_lcs, kListchars, true, NULL, 0) != NULL) { return e_conflicts_with_value_of_listchars; } - if (set_fillchars_option(wp, wp->w_p_fcs, true) != NULL) { + if (set_chars_option(wp, wp->w_p_fcs, kFillchars, true, NULL, 0) != NULL) { return e_conflicts_with_value_of_fillchars; } } diff --git a/src/nvim/optionstr.h b/src/nvim/optionstr.h index b317e55b7e..7001f18098 100644 --- a/src/nvim/optionstr.h +++ b/src/nvim/optionstr.h @@ -2,9 +2,14 @@ #include <stdint.h> // IWYU pragma: keep -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/option_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep + +typedef enum { + kFillchars, + kListchars, +} CharsOption; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "optionstr.h.generated.h" diff --git a/src/nvim/os/dl.c b/src/nvim/os/dl.c index 1a8d847f79..c6982e4fa8 100644 --- a/src/nvim/os/dl.c +++ b/src/nvim/os/dl.c @@ -5,7 +5,7 @@ #include <stdint.h> #include <uv.h> -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/os/dl.h" diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 8620c79069..5b1cb01976 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -13,9 +13,9 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/macros_defs.h" @@ -25,8 +25,10 @@ #include "nvim/option_vars.h" #include "nvim/os/fs.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/version.h" #include "nvim/vim_defs.h" @@ -46,6 +48,10 @@ # include <sys/utsname.h> #endif +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/env.c.generated.h" +#endif + // Because `uv_os_getenv` requires allocating, we must manage a map to maintain // the behavior of `os_getenv`. static PMap(cstr_t) envmap = MAP_INIT; @@ -55,8 +61,7 @@ static PMap(cstr_t) envmap = MAP_INIT; const char *os_getenv(const char *name) FUNC_ATTR_NONNULL_ALL { - char *e; - size_t size = 64; + char *e = NULL; if (name[0] == '\0') { return NULL; } @@ -72,23 +77,31 @@ const char *os_getenv(const char *name) } pmap_del2(&envmap, name); } - e = xmalloc(size); - r = uv_os_getenv(name, e, &size); +#define INIT_SIZE 64 + size_t size = INIT_SIZE; + char buf[INIT_SIZE]; + r = uv_os_getenv(name, buf, &size); if (r == UV_ENOBUFS) { - e = xrealloc(e, size); + e = xmalloc(size); r = uv_os_getenv(name, e, &size); - } - if (r != 0 || size == 0 || e[0] == '\0') { - xfree(e); + if (r != 0 || size == 0 || e[0] == '\0') { + XFREE_CLEAR(e); + goto end; + } + } else if (r != 0 || size == 0 || buf[0] == '\0') { e = NULL; goto end; + } else { + // NB: `size` param of uv_os_getenv() includes the NUL-terminator, + // except when it does not include the NUL-terminator. + e = xmemdupz(buf, size); } pmap_put(cstr_t)(&envmap, xstrdup(name), e); end: if (r != 0 && r != UV_ENOENT && r != UV_UNKNOWN) { ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r)); } - return (e == NULL || size == 0 || e[0] == '\0') ? NULL : e; + return e; } /// Returns true if environment variable `name` is defined (even if empty). @@ -507,6 +520,17 @@ void free_homedir(void) xfree(homedir); } +void free_envmap(void) +{ + cstr_t name; + ptr_t e; + map_foreach(&envmap, name, e, { + xfree((char *)name); + xfree(e); + }); + map_destroy(cstr_t, &envmap); +} + #endif /// Call expand_env() and store the result in an allocated string. @@ -562,6 +586,9 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es bool copy_char; bool mustfree; // var was allocated, need to free it later bool at_start = true; // at start of a name +#if defined(BACKSLASH_IN_FILENAME) + char *const save_dst = dst; +#endif int prefix_len = (prefix == NULL) ? 0 : (int)strlen(prefix); @@ -572,7 +599,7 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es if (src[0] == '`' && src[1] == '=') { var = src; src += 2; - (void)skip_expr(&src, NULL); + skip_expr(&src, NULL); if (*src == '`') { src++; } @@ -604,7 +631,7 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es while (c-- > 0 && *tail != NUL && *tail != '}') { *var++ = *tail++; } - } else // NOLINT + } else #endif { while (c-- > 0 && *tail != NUL && vim_isIDc((uint8_t)(*tail))) { @@ -702,7 +729,7 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es // with it, skip a character if (after_pathsep(dst, dst + c) #if defined(BACKSLASH_IN_FILENAME) - && dst[-1] != ':' + && (dst == save_dst || dst[-1] != ':') #endif && vim_ispathsep(*tail)) { tail++; @@ -904,10 +931,7 @@ char *vim_getenv(const char *name) // Don't do this when default_vimruntime_dir is non-empty. char *vim_path = NULL; if (vimruntime -#ifdef HAVE_PATHDEF - && *default_vimruntime_dir == NUL -#endif - ) { + && *default_vimruntime_dir == NUL) { kos_env_path = os_getenv("VIM"); if (kos_env_path != NULL) { vim_path = vim_version_dir(kos_env_path); @@ -966,7 +990,6 @@ char *vim_getenv(const char *name) assert(vim_path != exe_name); } -#ifdef HAVE_PATHDEF // When there is a pathdef.c file we can use default_vim_dir and // default_vimruntime_dir if (vim_path == NULL) { @@ -980,7 +1003,6 @@ char *vim_getenv(const char *name) } } } -#endif // Set the environment variable, so that the new value can be found fast // next time, and others can also use it (e.g. Perl). @@ -1051,7 +1073,7 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si size_t usedlen = 0; size_t flen = strlen(homedir_env_mod); char *fbuf = NULL; - (void)modify_fname(":p", false, &usedlen, &homedir_env_mod, &fbuf, &flen); + modify_fname(":p", false, &usedlen, &homedir_env_mod, &fbuf, &flen); flen = strlen(homedir_env_mod); assert(homedir_env_mod != homedir_env); if (vim_ispathsep(homedir_env_mod[flen - 1])) { diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 79d6ac08e7..da6fb13768 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -12,7 +12,7 @@ #include <uv.h> #include "auto/config.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/macros_defs.h" @@ -22,12 +22,9 @@ #include "nvim/os/fs.h" #include "nvim/os/os_defs.h" #include "nvim/rbuffer.h" +#include "nvim/rbuffer_defs.h" #include "nvim/types_defs.h" -#ifdef MSWIN -# include "nvim/os/os_win_console.h" -#endif - #ifdef HAVE_SYS_UIO_H # include <sys/uio.h> #endif @@ -132,69 +129,15 @@ int file_open_fd(FileDescriptor *const ret_fp, const int fd, const int flags) return 0; } -/// Like file_open(), but allocate and return ret_fp -/// -/// @param[out] error Error code, or 0 on success. @see os_strerror() -/// @param[in] fname File name to open. -/// @param[in] flags Flags, @see FileOpenFlags. -/// @param[in] mode Permissions for the newly created file (ignored if flags -/// does not have kFileCreate\*). -/// -/// @return [allocated] Opened file or NULL in case of error. -FileDescriptor *file_open_new(int *const error, const char *const fname, const int flags, - const int mode) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - FileDescriptor *const fp = xmalloc(sizeof(*fp)); - if ((*error = file_open(fp, fname, flags, mode)) != 0) { - xfree(fp); - return NULL; - } - return fp; -} - -/// Like file_open_fd(), but allocate and return ret_fp -/// -/// @param[out] error Error code, or 0 on success. @see os_strerror() -/// @param[in] fd File descriptor to wrap. -/// @param[in] flags Flags, @see FileOpenFlags. -/// @param[in] mode Permissions for the newly created file (ignored if flags -/// does not have FILE_CREATE\*). -/// -/// @return [allocated] Opened file or NULL in case of error. -FileDescriptor *file_open_fd_new(int *const error, const int fd, const int flags) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT -{ - FileDescriptor *const fp = xmalloc(sizeof(*fp)); - if ((*error = file_open_fd(fp, fd, flags)) != 0) { - xfree(fp); - return NULL; - } - return fp; -} - /// Opens standard input as a FileDescriptor. -FileDescriptor *file_open_stdin(void) - FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +int file_open_stdin(FileDescriptor *fp) + FUNC_ATTR_WARN_UNUSED_RESULT { - int error; - int stdin_dup_fd; - if (stdin_fd > 0) { - stdin_dup_fd = stdin_fd; - } else { - stdin_dup_fd = os_dup(STDIN_FILENO); -#ifdef MSWIN - // Replace the original stdin with the console input handle. - os_replace_stdin_to_conin(); -#endif - } - FileDescriptor *const stdin_dup = file_open_fd_new(&error, stdin_dup_fd, - kFileReadOnly|kFileNonBlocking); - assert(stdin_dup != NULL); + int error = file_open_fd(fp, os_open_stdin_fd(), kFileReadOnly|kFileNonBlocking); if (error != 0) { ELOG("failed to open stdin: %s", os_strerror(error)); } - return stdin_dup; + return error; } /// Close file and free its buffer @@ -215,20 +158,6 @@ int file_close(FileDescriptor *const fp, const bool do_fsync) return flush_error; } -/// Close and free file obtained using file_open_new() -/// -/// @param[in,out] fp File to close. -/// @param[in] do_fsync If true, use fsync() to write changes to disk. -/// -/// @return 0 or error code. -int file_free(FileDescriptor *const fp, const bool do_fsync) - FUNC_ATTR_NONNULL_ALL -{ - const int ret = file_close(fp, do_fsync); - xfree(fp); - return ret; -} - /// Flush file modifications to disk /// /// @param[in,out] fp File to work with. @@ -437,24 +366,6 @@ ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size) return (ptrdiff_t)read_bytes; } -/// Msgpack callback for writing to a file -/// -/// @param data File to write to. -/// @param[in] buf Data to write. -/// @param[in] len Length of the data to write. -/// -/// @return 0 in case of success, -1 in case of error. -int msgpack_file_write(void *data, const char *buf, size_t len) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - assert(len < PTRDIFF_MAX); - const ptrdiff_t written_bytes = file_write((FileDescriptor *)data, buf, len); - if (written_bytes < 0) { - return msgpack_file_write_error((int)written_bytes); - } - return 0; -} - /// Print error which occurs when failing to write msgpack data /// /// @param[in] error Error code of the error to print. diff --git a/src/nvim/os/fileio.h b/src/nvim/os/fileio.h index 72e7984c8a..e8fd2209db 100644 --- a/src/nvim/os/fileio.h +++ b/src/nvim/os/fileio.h @@ -1,20 +1,8 @@ #pragma once -#include <stdbool.h> -#include <stddef.h> +#include <stddef.h> // IWYU pragma: keep -#include "nvim/func_attr.h" -#include "nvim/rbuffer.h" - -/// Structure used to read from/write to file -typedef struct { - int fd; ///< File descriptor. - int _error; ///< Error code for use with RBuffer callbacks or zero. - RBuffer *rv; ///< Read or write buffer. - bool wr; ///< True if file is in write mode. - bool eof; ///< True if end of file was encountered. - bool non_blocking; ///< True if EAGAIN should not restart syscalls. -} FileDescriptor; +#include "nvim/os/fileio_defs.h" // IWYU pragma: keep /// file_open() flags typedef enum { @@ -37,33 +25,6 @@ typedef enum { kFileMkDir = 256, } FileOpenFlags; -static inline bool file_eof(const FileDescriptor *fp) - REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; - -/// Check whether end of file was encountered -/// -/// @param[in] fp File to check. -/// -/// @return true if it was, false if it was not or read operation was never -/// performed. -static inline bool file_eof(const FileDescriptor *const fp) -{ - return fp->eof && rbuffer_size(fp->rv) == 0; -} - -static inline int file_fd(const FileDescriptor *fp) - REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; - -/// Return the file descriptor associated with the FileDescriptor structure -/// -/// @param[in] fp File to check. -/// -/// @return File descriptor. -static inline int file_fd(const FileDescriptor *const fp) -{ - return fp->fd; -} - enum { /// Read or write buffer size /// diff --git a/src/nvim/os/fileio_defs.h b/src/nvim/os/fileio_defs.h new file mode 100644 index 0000000000..3dc8c7b22a --- /dev/null +++ b/src/nvim/os/fileio_defs.h @@ -0,0 +1,43 @@ +#pragma once + +#include <stdbool.h> + +#include "nvim/func_attr.h" +#include "nvim/rbuffer_defs.h" + +/// Structure used to read from/write to file +typedef struct { + int fd; ///< File descriptor. + int _error; ///< Error code for use with RBuffer callbacks or zero. + RBuffer *rv; ///< Read or write buffer. + bool wr; ///< True if file is in write mode. + bool eof; ///< True if end of file was encountered. + bool non_blocking; ///< True if EAGAIN should not restart syscalls. +} FileDescriptor; + +static inline bool file_eof(const FileDescriptor *fp) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Check whether end of file was encountered +/// +/// @param[in] fp File to check. +/// +/// @return true if it was, false if it was not or read operation was never +/// performed. +static inline bool file_eof(const FileDescriptor *const fp) +{ + return fp->eof && rbuffer_size(fp->rv) == 0; +} + +static inline int file_fd(const FileDescriptor *fp) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Return the file descriptor associated with the FileDescriptor structure +/// +/// @param[in] fp File to check. +/// +/// @return File descriptor. +static inline int file_fd(const FileDescriptor *const fp) +{ + return fp->fd; +} diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 8f939c3b40..ade745df2c 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -17,8 +17,8 @@ #endif #include "auto/config.h" -#include "nvim/func_attr.h" #include "nvim/os/fs.h" +#include "nvim/os/os_defs.h" #if defined(HAVE_ACL) # ifdef HAVE_SYS_ACL_H @@ -33,8 +33,9 @@ # include <sys/xattr.h> #endif +#include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/macros_defs.h" @@ -44,6 +45,7 @@ #include "nvim/os/os.h" #include "nvim/path.h" #include "nvim/types_defs.h" +#include "nvim/ui.h" #include "nvim/vim_defs.h" #ifdef HAVE_SYS_UIO_H @@ -53,6 +55,7 @@ #ifdef MSWIN # include "nvim/mbyte.h" # include "nvim/option.h" +# include "nvim/os/os_win_console.h" # include "nvim/strings.h" #endif @@ -90,7 +93,11 @@ int os_chdir(const char *path) smsg(0, "chdir(%s)", path); verbose_leave(); } - return uv_chdir(path); + int err = uv_chdir(path); + if (err == 0) { + ui_call_chdir(cstr_as_string(path)); + } + return err; } /// Get the name of current directory. @@ -535,6 +542,22 @@ os_dup_dup: return ret; } +/// Open the file descriptor for stdin. +int os_open_stdin_fd(void) +{ + int stdin_dup_fd; + if (stdin_fd > 0) { + stdin_dup_fd = stdin_fd; + } else { + stdin_dup_fd = os_dup(STDIN_FILENO); +#ifdef MSWIN + // Replace the original stdin with the console input handle. + os_replace_stdin_to_conin(); +#endif + } + return stdin_dup_fd; +} + /// Read from a file /// /// Handles EINTR and ENOMEM, but not other errors. diff --git a/src/nvim/os/fs.h b/src/nvim/os/fs.h index 56dd657f70..19cdb09c99 100644 --- a/src/nvim/os/fs.h +++ b/src/nvim/os/fs.h @@ -5,7 +5,7 @@ #include <stdio.h> // IWYU pragma: keep #include <uv.h> // IWYU pragma: keep -#include "nvim/os/fs_defs.h" // IWYU pragma: export +#include "nvim/os/fs_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index f3bd1c7ed9..fab360c9af 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -8,13 +8,14 @@ #include "nvim/api/private/defs.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/rstream.h" #include "nvim/event/stream.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/keycodes.h" #include "nvim/log.h" @@ -27,7 +28,9 @@ #include "nvim/os/time.h" #include "nvim/profile.h" #include "nvim/rbuffer.h" +#include "nvim/rbuffer_defs.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #define READ_BUFFER_SIZE 0xfff #define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4) @@ -75,6 +78,13 @@ void input_stop(void) stream_close(&read_stream, NULL, NULL); } +#ifdef EXITFREE +void input_free_all_mem(void) +{ + rbuffer_free(input_buffer); +} +#endif + static void cursorhold_event(void **argv) { event_T event = State & MODE_INSERT ? EVENT_CURSORHOLDI : EVENT_CURSORHOLD; @@ -85,11 +95,11 @@ static void cursorhold_event(void **argv) static void create_cursorhold_event(bool events_enabled) { // If events are enabled and the queue has any items, this function should not - // have been called(inbuf_poll would return kInputAvail) + // have been called (inbuf_poll would return kInputAvail). // TODO(tarruda): Cursorhold should be implemented as a timer set during the // `state_check` callback for the states where it can be triggered. assert(!events_enabled || multiqueue_empty(main_loop.events)); - multiqueue_put(main_loop.events, cursorhold_event, 0); + multiqueue_put(main_loop.events, cursorhold_event, NULL); } static void restart_cursorhold_wait(int tb_change_cnt) @@ -100,7 +110,7 @@ static void restart_cursorhold_wait(int tb_change_cnt) /// Low level input function /// -/// wait until either the input buffer is non-empty or, if `events` is not NULL +/// Wait until either the input buffer is non-empty or, if `events` is not NULL /// until `events` is non-empty. int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *events) { @@ -304,7 +314,8 @@ static uint8_t check_multiclick(int code, int grid, int row, int col) } // For click events the number of clicks is updated. - if (code == KE_LEFTMOUSE || code == KE_RIGHTMOUSE || code == KE_MIDDLEMOUSE) { + if (code == KE_LEFTMOUSE || code == KE_RIGHTMOUSE || code == KE_MIDDLEMOUSE + || code == KE_X1MOUSE || code == KE_X2MOUSE) { uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns) // compute the time elapsed since the previous mouse click and // convert p_mouse from ms to ns @@ -408,7 +419,8 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs size_t input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int col) { modifier |= check_multiclick(code, grid, row, col); - uint8_t buf[7], *p = buf; + uint8_t buf[7]; + uint8_t *p = buf; if (modifier) { p[0] = K_SPECIAL; p[1] = KS_MODIFIER; @@ -531,7 +543,7 @@ bool os_input_ready(MultiQueue *events) { return (typebuf_was_filled // API call filled typeahead || rbuffer_size(input_buffer) // Input buffer filled - || pending_events(events)); // Events must be processed + || pending_events(events)); // Events must be processed } // Exit because of an input read error. diff --git a/src/nvim/os/input.h b/src/nvim/os/input.h index 4b104b0b50..abef46072b 100644 --- a/src/nvim/os/input.h +++ b/src/nvim/os/input.h @@ -4,7 +4,7 @@ #include <stdint.h> // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep -#include "nvim/event/multiqueue.h" +#include "nvim/event/defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" EXTERN bool used_stdin INIT( = false); diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 17d179a56a..fb534ab2f4 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -19,7 +19,8 @@ #include "nvim/eval.h" #include "nvim/ex_cmds_defs.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" +#include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -79,17 +80,21 @@ static char *get_mess_env(void) return get_locale_val(LC_MESSAGES); #else char *p = (char *)os_getenv("LC_ALL"); + if (p != NULL) { + return p; + } + + p = (char *)os_getenv("LC_MESSAGES"); + if (p != NULL) { + return p; + } + + p = (char *)os_getenv("LANG"); + if (p != NULL && ascii_isdigit(*p)) { + p = NULL; // ignore something like "1043" + } if (p == NULL) { - p = (char *)os_getenv("LC_MESSAGES"); - if (p == NULL) { - p = (char *)os_getenv("LANG"); - if (p != NULL && ascii_isdigit(*p)) { - p = NULL; // ignore something like "1043" - } - if (p == NULL) { - p = get_locale_val(LC_CTYPE); - } - } + p = get_locale_val(LC_CTYPE); } return p; #endif @@ -99,9 +104,7 @@ static char *get_mess_env(void) /// Also do "v:lc_time"and "v:ctype". void set_lang_var(void) { - const char *loc; - - loc = get_locale_val(LC_CTYPE); + const char *loc = get_locale_val(LC_CTYPE); set_vim_var_string(VV_CTYPE, loc, -1); loc = get_mess_env(); @@ -142,8 +145,6 @@ void init_locale(void) void ex_language(exarg_T *eap) { char *loc; - char *p; - char *name; int what = LC_ALL; char *whatstr = ""; #ifdef LC_MESSAGES @@ -152,12 +153,12 @@ void ex_language(exarg_T *eap) # define VIM_LC_MESSAGES 6789 #endif - name = eap->arg; + char *name = eap->arg; // Check for "messages {name}", "ctype {name}" or "time {name}" argument. // Allow abbreviation, but require at least 3 characters to avoid // confusion with a two letter language name "me" or "ct". - p = skiptowhite(eap->arg); + char *p = skiptowhite(eap->arg); if ((*p == NUL || ascii_iswhite(*p)) && p - eap->arg >= 3) { if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0) { what = VIM_LC_MESSAGES; @@ -248,7 +249,6 @@ static bool did_init_locales = false; static char **find_locales(void) { garray_T locales_ga; - char *loc; char *saveptr = NULL; // Find all available locales by running command "locale -a". If this @@ -261,7 +261,7 @@ static char **find_locales(void) // Transform locale_a string where each locale is separated by "\n" // into an array of locale strings. - loc = os_strtok(locale_a, "\n", &saveptr); + char *loc = os_strtok(locale_a, "\n", &saveptr); while (loc != NULL) { loc = xstrdup(loc); diff --git a/src/nvim/os/os.h b/src/nvim/os/os.h index 302d84d066..1a942d062b 100644 --- a/src/nvim/os/os.h +++ b/src/nvim/os/os.h @@ -1,24 +1,18 @@ #pragma once -#include <stddef.h> // IWYU pragma: keep -#include <stdint.h> // IWYU pragma: keep -#include <uv.h> // IWYU pragma: keep +#include <stddef.h> +#include <stdint.h> +#include <uv.h> -#include "nvim/buffer_defs.h" // IWYU pragma: keep -#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep -#include "nvim/garray_defs.h" // IWYU pragma: keep -#include "nvim/os/os_defs.h" // IWYU pragma: export -#include "nvim/os/stdpaths_defs.h" // IWYU pragma: keep +#include "nvim/cmdexpand_defs.h" +#include "nvim/garray_defs.h" +#include "nvim/os/os_defs.h" +#include "nvim/os/stdpaths_defs.h" +#include "nvim/types_defs.h" -#define HAVE_PATHDEF - -// Some file names are stored in pathdef.c, which is generated from the -// Makefile to make their value depend on the Makefile. -#ifdef HAVE_PATHDEF extern char *default_vim_dir; extern char *default_vimruntime_dir; extern char *default_lib_dir; -#endif #ifdef INCLUDE_GENERATED_DECLARATIONS // IWYU pragma: begin_exports diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 12de55a227..db575e005a 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -113,3 +113,31 @@ && (defined(S_ISCHR) || defined(S_IFCHR)) # define OPEN_CHR_FILES #endif + +// We use 64-bit file functions here, if available. E.g. ftello() returns +// off_t instead of long, which helps if long is 32 bit and off_t is 64 bit. +// We assume that when fseeko() is available then ftello() is too. +// Note that Windows has different function names. +#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__MINGW32__) +typedef __int64 off_T; +# ifdef __MINGW32__ +# define vim_lseek lseek64 +# define vim_fseek fseeko64 +# define vim_ftell ftello64 +# else +# define vim_lseek _lseeki64 +# define vim_fseek _fseeki64 +# define vim_ftell _ftelli64 +# endif +#else +typedef off_t off_T; +# ifdef HAVE_FSEEKO +# define vim_lseek lseek +# define vim_ftell ftello +# define vim_fseek fseeko +# else +# define vim_lseek lseek +# define vim_ftell ftell +# define vim_fseek(a, b, c) fseek(a, (long)b, c) +# endif +#endif diff --git a/src/nvim/os/os_win_console.c b/src/nvim/os/os_win_console.c index 816e81e997..953d291290 100644 --- a/src/nvim/os/os_win_console.c +++ b/src/nvim/os/os_win_console.c @@ -1,6 +1,7 @@ #include <string.h> #include "nvim/globals.h" +#include "nvim/memory.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" @@ -105,3 +106,40 @@ void os_title_reset(void) { SetConsoleTitle(origTitle); } + +#if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) +# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif +/// Guesses the terminal-type. Calls SetConsoleMode() and uv_set_vterm_state() +/// if appropriate. +/// +/// @param[in,out] term Name of the guessed terminal, statically-allocated +/// @param out_fd stdout file descriptor +void os_tty_guess_term(const char **term, int out_fd) +{ + bool conemu_ansi = strequal(os_getenv("ConEmuANSI"), "ON"); + bool vtp = false; + + HANDLE handle = (HANDLE)_get_osfhandle(out_fd); + DWORD dwMode; + if (handle != INVALID_HANDLE_VALUE && GetConsoleMode(handle, &dwMode)) { + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (SetConsoleMode(handle, dwMode)) { + vtp = true; + } + } + + if (*term == NULL) { + if (vtp) { + *term = "vtpcon"; + } else if (conemu_ansi) { + *term = "conemu"; + } else { + *term = "win32con"; + } + } + + if (conemu_ansi) { + uv_tty_set_vterm_state(UV_TTY_SUPPORTED); + } +} diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index d9ec3a7a8a..e8d38d5b8a 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -44,7 +44,7 @@ #endif #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/process.c.generated.h" // IWYU pragma: export +# include "os/process.c.generated.h" #endif #ifdef MSWIN @@ -114,6 +114,7 @@ bool os_proc_tree_kill(int pid, int sig) /// @param[out] proc_count Number of child processes. /// @return 0 on success, 1 if process not found, 2 on other error. int os_proc_children(int ppid, int **proc_list, size_t *proc_count) + FUNC_ATTR_NONNULL_ALL { if (ppid < 0) { return 2; @@ -229,7 +230,7 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) /// /// @param pid Process to inspect. /// @return Map of process properties, empty on error. -Dictionary os_proc_info(int pid) +Dictionary os_proc_info(int pid, Arena *arena) { Dictionary pinfo = ARRAY_DICT_INIT; PROCESSENTRY32 pe; @@ -257,9 +258,10 @@ Dictionary os_proc_info(int pid) CloseHandle(h); if (pe.th32ProcessID == (DWORD)pid) { - PUT(pinfo, "pid", INTEGER_OBJ(pid)); - PUT(pinfo, "ppid", INTEGER_OBJ((int)pe.th32ParentProcessID)); - PUT(pinfo, "name", CSTR_TO_OBJ(pe.szExeFile)); + pinfo = arena_dict(arena, 3); + PUT_C(pinfo, "pid", INTEGER_OBJ(pid)); + PUT_C(pinfo, "ppid", INTEGER_OBJ((int)pe.th32ParentProcessID)); + PUT_C(pinfo, "name", CSTR_TO_ARENA_OBJ(arena, pe.szExeFile)); } return pinfo; diff --git a/src/nvim/os/pty_conpty_win.c b/src/nvim/os/pty_conpty_win.c index 53169c0ef8..e7697880af 100644 --- a/src/nvim/os/pty_conpty_win.c +++ b/src/nvim/os/pty_conpty_win.c @@ -1,5 +1,6 @@ #include <uv.h> +#include "nvim/log.h" #include "nvim/os/os.h" #include "nvim/os/pty_conpty_win.h" #include "nvim/vim_defs.h" diff --git a/src/nvim/os/pty_conpty_win.h b/src/nvim/os/pty_conpty_win.h index aa04cd1e84..a0c6a06cda 100644 --- a/src/nvim/os/pty_conpty_win.h +++ b/src/nvim/os/pty_conpty_win.h @@ -7,7 +7,7 @@ # define HPCON VOID * #endif -extern HRESULT(WINAPI *pCreatePseudoConsole) // NOLINT(whitespace/parens) +extern HRESULT(WINAPI *pCreatePseudoConsole) (COORD, HANDLE, HANDLE, DWORD, HPCON *); extern HRESULT(WINAPI *pResizePseudoConsole)(HPCON, COORD); extern void(WINAPI *pClosePseudoConsole)(HPCON); diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index f801646967..4d34e8fac4 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -32,15 +32,15 @@ #include "auto/config.h" #include "klib/klist.h" #include "nvim/eval/typval.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/process.h" -#include "nvim/event/stream.h" -#include "nvim/func_attr.h" #include "nvim/log.h" #include "nvim/os/fs.h" #include "nvim/os/os_defs.h" #include "nvim/os/pty_process.h" #include "nvim/os/pty_process_unix.h" +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/pty_process_unix.c.generated.h" @@ -405,3 +405,13 @@ static void chld_handler(uv_signal_t *handle, int signum) proc->internal_exit_cb(proc); } } + +PtyProcess pty_process_init(Loop *loop, void *data) +{ + PtyProcess rv; + rv.process = process_init(loop, kProcessTypePty, data); + rv.width = 80; + rv.height = 24; + rv.tty_fd = -1; + return rv; +} diff --git a/src/nvim/os/pty_process_unix.h b/src/nvim/os/pty_process_unix.h index 92cc582832..1a77ae5fd5 100644 --- a/src/nvim/os/pty_process_unix.h +++ b/src/nvim/os/pty_process_unix.h @@ -4,26 +4,15 @@ #include <stdint.h> #include <sys/ioctl.h> -#include "nvim/event/loop.h" -#include "nvim/event/process.h" +#include "nvim/event/defs.h" -typedef struct pty_process { +typedef struct { Process process; uint16_t width, height; struct winsize winsize; int tty_fd; } PtyProcess; -static inline PtyProcess pty_process_init(Loop *loop, void *data) -{ - PtyProcess rv; - rv.process = process_init(loop, kProcessTypePty, data); - rv.width = 80; - rv.height = 24; - rv.tty_fd = -1; - return rv; -} - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/pty_process_unix.h.generated.h" #endif diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index ca2dce36ea..12831ff05f 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -4,6 +4,8 @@ #include "nvim/ascii_defs.h" #include "nvim/eval/typval.h" +#include "nvim/event/loop.h" +#include "nvim/log.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/os/os.h" @@ -21,9 +23,19 @@ static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) Process *proc = (Process *)ptyproc; os_conpty_free(ptyproc->conpty); - uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer); - ptyproc->wait_eof_timer.data = (void *)ptyproc; - uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); + // NB: pty_process_finish1() is called on a separate thread, + // but the timer only works properly if it's started by the main thread. + loop_schedule_fast(proc->loop, event_create(start_wait_eof_timer, ptyproc)); +} + +static void start_wait_eof_timer(void **argv) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = (PtyProcess *)argv[0]; + + if (ptyproc->finish_wait != NULL) { + uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); + } } /// @returns zero on success, or negative error code. @@ -104,6 +116,8 @@ int pty_process_spawn(PtyProcess *ptyproc) } proc->pid = (int)GetProcessId(process_handle); + uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer); + ptyproc->wait_eof_timer.data = (void *)ptyproc; if (!RegisterWaitForSingleObject(&ptyproc->finish_wait, process_handle, pty_process_finish1, @@ -163,6 +177,16 @@ void pty_process_close(PtyProcess *ptyproc) pty_process_close_master(ptyproc); + if (ptyproc->finish_wait != NULL) { + UnregisterWaitEx(ptyproc->finish_wait, NULL); + ptyproc->finish_wait = NULL; + uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL); + } + if (ptyproc->process_handle != NULL) { + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; + } + if (proc->internal_close_cb) { proc->internal_close_cb(proc); } @@ -191,6 +215,7 @@ static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) PtyProcess *ptyproc = wait_eof_timer->data; Process *proc = (Process *)ptyproc; + assert(ptyproc->finish_wait != NULL); if (proc->out.closed || proc->out.did_eof || !uv_is_readable(proc->out.uvstream)) { uv_timer_stop(&ptyproc->wait_eof_timer); pty_process_finish2(ptyproc); @@ -202,16 +227,10 @@ static void pty_process_finish2(PtyProcess *ptyproc) { Process *proc = (Process *)ptyproc; - UnregisterWaitEx(ptyproc->finish_wait, NULL); - uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL); - DWORD exit_code = 0; GetExitCodeProcess(ptyproc->process_handle, &exit_code); proc->status = proc->exit_signal ? 128 + proc->exit_signal : (int)exit_code; - CloseHandle(ptyproc->process_handle); - ptyproc->process_handle = NULL; - proc->internal_exit_cb(proc); } @@ -407,3 +426,15 @@ cleanup: return rc; } + +PtyProcess pty_process_init(Loop *loop, void *data) +{ + PtyProcess rv; + rv.process = process_init(loop, kProcessTypePty, data); + rv.width = 80; + rv.height = 24; + rv.conpty = NULL; + rv.finish_wait = NULL; + rv.process_handle = NULL; + return rv; +} diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 26cf387e54..3528f6bfe5 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -4,7 +4,7 @@ #include <uv.h> #include "nvim/event/process.h" -#include "nvim/lib/queue.h" +#include "nvim/lib/queue_defs.h" #include "nvim/os/pty_conpty_win.h" typedef struct pty_process { @@ -22,18 +22,6 @@ typedef struct arg_node { QUEUE node; // QUEUE structure. } ArgNode; -static inline PtyProcess pty_process_init(Loop *loop, void *data) -{ - PtyProcess rv; - rv.process = process_init(loop, kProcessTypePty, data); - rv.width = 80; - rv.height = 24; - rv.conpty = NULL; - rv.finish_wait = NULL; - rv.process_handle = NULL; - return rv; -} - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/pty_process_win.h.generated.h" #endif diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 191be784e8..46ba13c4cd 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -8,9 +8,11 @@ #include "auto/config.h" #include "klib/kvec.h" #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" +#include "nvim/event/defs.h" #include "nvim/event/libuv_process.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" @@ -20,8 +22,7 @@ #include "nvim/event/wstream.h" #include "nvim/ex_cmds.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/main.h" @@ -31,6 +32,7 @@ #include "nvim/message.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" +#include "nvim/os/os_defs.h" #include "nvim/os/shell.h" #include "nvim/os/signal.h" #include "nvim/os/time.h" @@ -38,6 +40,7 @@ #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/rbuffer.h" +#include "nvim/rbuffer_defs.h" #include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/tag.h" @@ -118,14 +121,10 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in int i; size_t len; char *p; - bool dir; char *extra_shell_arg = NULL; ShellOpts shellopts = kShellOptExpand | kShellOptSilent; int j; char *tempname; - char *command; - FILE *fd; - char *buffer; #define STYLE_ECHO 0 // use "echo", the default #define STYLE_GLOB 1 // use "glob", for csh #define STYLE_VIMGLOB 2 // use "vimglob", for Posix sh @@ -241,7 +240,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in len += sizeof("egin;" " end") - 1; } - command = xmalloc(len); + char *command = xmalloc(len); // Build the shell command: // - Set $nonomatch depending on EW_NOTFOUND (hopefully the shell @@ -389,7 +388,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in } // read the names from the file into memory - fd = fopen(tempname, READBIN); + FILE *fd = fopen(tempname, READBIN); if (fd == NULL) { // Something went wrong, perhaps a file name with a special char. if (!(flags & EW_SILENT)) { @@ -416,7 +415,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in #endif len = (size_t)templen; fseek(fd, 0, SEEK_SET); - buffer = xmalloc(len + 1); + char *buffer = xmalloc(len + 1); // fread() doesn't terminate buffer with NUL; // appropriate termination (not always NUL) is done below. size_t readlen = fread(buffer, 1, len, fd); @@ -537,7 +536,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in } // check if this entry should be included - dir = (os_isdir((*file)[i])); + bool dir = (os_isdir((*file)[i])); if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) { continue; } @@ -670,7 +669,8 @@ char *shell_argv_to_str(char **const argv) int os_call_shell(char *cmd, ShellOpts opts, char *extra_args) { DynamicBuffer input = DYNAMIC_BUFFER_INIT; - char *output = NULL, **output_ptr = NULL; + char *output = NULL; + char **output_ptr = NULL; int current_state = State; bool forward_output = true; @@ -702,7 +702,7 @@ int os_call_shell(char *cmd, ShellOpts opts, char *extra_args) xfree(input.data); if (output) { - (void)write_output(output, nread, true); + write_output(output, nread, true); xfree(output); } @@ -1122,7 +1122,8 @@ static void out_data_ring(char *output, size_t size) static void out_data_append_to_screen(char *output, size_t *count, bool eof) FUNC_ATTR_NONNULL_ALL { - char *p = output, *end = output + *count; + char *p = output; + char *end = output + *count; while (p < end) { if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) { msg_putchar_attr((uint8_t)(*p), 0); @@ -1141,7 +1142,7 @@ static void out_data_append_to_screen(char *output, size_t *count, bool eof) goto end; } - (void)msg_outtrans_len(p, i, 0); + msg_outtrans_len(p, i, 0); p += i; } } @@ -1235,7 +1236,8 @@ static size_t word_length(const char *str) /// before we finish writing. static void read_input(DynamicBuffer *buf) { - size_t written = 0, len = 0; + size_t written = 0; + size_t len = 0; linenr_T lnum = curbuf->b_op_start.lnum; char *lp = ml_get(lnum); diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index c920cb655e..ecedf144e5 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -7,9 +7,11 @@ #endif #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/eval.h" +#include "nvim/event/defs.h" #include "nvim/event/signal.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/main.h" diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index 7691aa5122..ede17bc7c8 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -4,13 +4,16 @@ #include "nvim/ascii_defs.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/os/os.h" #include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/stdpaths.c.generated.h" +#endif + /// Names of the environment variables, mapped to XDGVarType values static const char *xdg_env_vars[] = { [kXDGConfigHome] = "XDG_CONFIG_HOME", diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 49b43af6c0..16118028b4 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -8,8 +8,8 @@ #include "auto/config.h" #include "nvim/event/loop.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/event/multiqueue.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/main.h" @@ -19,7 +19,7 @@ #include "nvim/os/time.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/time.c.generated.h" // IWYU pragma: export +# include "os/time.c.generated.h" #endif /// Gets a high-resolution (nanosecond), monotonically-increasing time relative diff --git a/src/nvim/os/time.h b/src/nvim/os/time.h index 2748ba6953..b4d82d8002 100644 --- a/src/nvim/os/time.h +++ b/src/nvim/os/time.h @@ -3,7 +3,7 @@ #include <stddef.h> // IWYU pragma: keep #include <time.h> // IWYU pragma: keep -#include "nvim/os/time_defs.h" // IWYU pragma: export +#include "nvim/os/time_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/time.h.generated.h" diff --git a/src/nvim/os/tty.c b/src/nvim/os/tty.c deleted file mode 100644 index e683b9383f..0000000000 --- a/src/nvim/os/tty.c +++ /dev/null @@ -1,49 +0,0 @@ -// -// Terminal/console utils -// - -#include "nvim/os/os.h" // IWYU pragma: keep (Windows) -#include "nvim/os/tty.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/tty.c.generated.h" // IWYU pragma: export -#endif - -#ifdef MSWIN -# if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) -# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 -# endif -/// Guesses the terminal-type. Calls SetConsoleMode() and uv_set_vterm_state() -/// if appropriate. -/// -/// @param[in,out] term Name of the guessed terminal, statically-allocated -/// @param out_fd stdout file descriptor -void os_tty_guess_term(const char **term, int out_fd) -{ - bool conemu_ansi = strequal(os_getenv("ConEmuANSI"), "ON"); - bool vtp = false; - - HANDLE handle = (HANDLE)_get_osfhandle(out_fd); - DWORD dwMode; - if (handle != INVALID_HANDLE_VALUE && GetConsoleMode(handle, &dwMode)) { - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if (SetConsoleMode(handle, dwMode)) { - vtp = true; - } - } - - if (*term == NULL) { - if (vtp) { - *term = "vtpcon"; - } else if (conemu_ansi) { - *term = "conemu"; - } else { - *term = "win32con"; - } - } - - if (conemu_ansi) { - uv_tty_set_vterm_state(UV_TTY_SUPPORTED); - } -} -#endif diff --git a/src/nvim/os/tty.h b/src/nvim/os/tty.h deleted file mode 100644 index a24d875c05..0000000000 --- a/src/nvim/os/tty.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/tty.h.generated.h" // IWYU pragma: export -#endif diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index ae0994a73f..8886d6068d 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -9,8 +9,10 @@ #include "nvim/ascii_defs.h" #include "nvim/cmdexpand_defs.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/memory.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/vim_defs.h" #ifdef HAVE_PWD_FUNCS # include <pwd.h> @@ -22,6 +24,10 @@ # include "nvim/message.h" #endif +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/users.c.generated.h" +#endif + // All user names (for ~user completion as done by shell). static garray_T ga_users = GA_EMPTY_INIT_VALUE; @@ -186,7 +192,7 @@ void free_users(void) /// Done only once and then cached. static void init_users(void) { - static int lazy_init_done = false; + static bool lazy_init_done = false; if (lazy_init_done) { return; diff --git a/src/nvim/path.c b/src/nvim/path.c index c7212c7ade..4de18c7530 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -8,6 +8,7 @@ #include "auto/config.h" #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/eval.h" @@ -15,23 +16,25 @@ #include "nvim/ex_docmd.h" #include "nvim/file_search.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/garray_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/shell.h" #include "nvim/path.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/strings.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -153,7 +156,7 @@ char *path_tail_with_sep(char *fname) /// /// @return The position of the last path separator + 1. const char *invocation_path_tail(const char *invocation, size_t *len) - FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1) { const char *tail = get_past_head(invocation); const char *p = tail; @@ -238,9 +241,9 @@ char *get_past_head(const char *path) return (char *)retval; } -/// Return true if 'c' is a path separator. +/// @return true if 'c' is a path separator. /// Note that for MS-Windows this includes the colon. -int vim_ispathsep(int c) +bool vim_ispathsep(int c) { #ifdef UNIX return c == '/'; // Unix has ':' inside file names @@ -254,7 +257,7 @@ int vim_ispathsep(int c) } // Like vim_ispathsep(c), but exclude the colon for MS-Windows. -int vim_ispathsep_nocolon(int c) +bool vim_ispathsep_nocolon(int c) { return vim_ispathsep(c) #ifdef BACKSLASH_IN_FILENAME @@ -263,8 +266,8 @@ int vim_ispathsep_nocolon(int c) ; } -/// return true if 'c' is a path list separator. -int vim_ispathlistsep(int c) +/// @return true if 'c' is a path list separator. +bool vim_ispathlistsep(int c) { #ifdef UNIX return c == ':'; @@ -652,7 +655,7 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in && (vim_strchr("*?[{~$", (uint8_t)(*path_end)) != NULL || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char(path_end)))) #endif - ) { // NOLINT(whitespace/parens) + ) { e = p; } len = (size_t)(utfc_ptr2len(path_end)); @@ -718,7 +721,7 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in && *path_end == '/') { STRCPY(s, path_end + 1); stardepth++; - (void)do_path_expand(gap, buf, (size_t)(s - buf), flags, true); + do_path_expand(gap, buf, (size_t)(s - buf), flags, true); stardepth--; } *s = NUL; @@ -747,7 +750,7 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in STRCPY(buf + len, "/**"); // NOLINT STRCPY(buf + len + 3, path_end); stardepth++; - (void)do_path_expand(gap, buf, len + 1, flags, true); + do_path_expand(gap, buf, len + 1, flags, true); stardepth--; } @@ -755,7 +758,7 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in if (path_has_exp_wildcard(path_end)) { // handle more wildcards // need to expand another component of the path // remove backslashes for the remaining components only - (void)do_path_expand(gap, buf, len + 1, flags, false); + do_path_expand(gap, buf, len + 1, flags, false); } else { FileInfo file_info; @@ -908,7 +911,7 @@ static char *get_path_cutoff(char *fname, garray_T *gap) #ifdef MSWIN || (vim_ispathsep(fname[j]) && vim_ispathsep(path_part[i][j])) #endif - ) // NOLINT(whitespace/parens) + ) && fname[j] != NUL && path_part[i][j] != NUL) { j++; } @@ -972,20 +975,17 @@ static void uniquefy_paths(garray_T *gap, char *pattern) for (int i = 0; i < gap->ga_len && !got_int; i++) { char *path = fnames[i]; - int is_in_curdir; const char *dir_end = gettail_dir(path); - char *pathsep_p; - char *path_cutoff; len = strlen(path); - is_in_curdir = path_fnamencmp(curdir, path, (size_t)(dir_end - path)) == 0 - && curdir[dir_end - path] == NUL; + bool is_in_curdir = path_fnamencmp(curdir, path, (size_t)(dir_end - path)) == 0 + && curdir[dir_end - path] == NUL; if (is_in_curdir) { in_curdir[i] = xstrdup(path); } // Shorten the filename while maintaining its uniqueness - path_cutoff = get_path_cutoff(path, &path_ga); + char *path_cutoff = get_path_cutoff(path, &path_ga); // Don't assume all files can be reached without path when search // pattern starts with **/, so only remove path_cutoff @@ -1000,7 +1000,7 @@ static void uniquefy_paths(garray_T *gap, char *pattern) } else { // Here all files can be reached without path, so get shortest // unique path. We start at the end of the path. */ - pathsep_p = path + len - 1; + char *pathsep_p = path + len - 1; while (find_previous_pathsep(path, &pathsep_p)) { if (vim_regexec(®match, pathsep_p + 1, 0) && is_unique(pathsep_p + 1, gap, i) @@ -1355,7 +1355,7 @@ void FreeWild(int count, char **files) } /// @return true if we can expand this backtick thing here. -static int vim_backtick(char *p) +static bool vim_backtick(char *p) { return *p == '`' && *(p + 1) != NUL && *(p + strlen(p) - 1) == '`'; } @@ -1668,7 +1668,7 @@ static char *eval_includeexpr(const char *const ptr, const size_t len) current_sctx = curbuf->b_p_script_ctx[BV_INEX].script_ctx; char *res = eval_to_string_safe(curbuf->b_p_inex, - was_set_insecurely(curwin, "includeexpr", OPT_LOCAL)); + was_set_insecurely(curwin, kOptIncludeexpr, OPT_LOCAL)); set_vim_var_string(VV_FNAME, NULL, 0); current_sctx = save_sctx; @@ -1969,7 +1969,7 @@ bool same_directory(char *f1, char *f2) return false; } - (void)vim_FullName(f1, ffname, MAXPATHL, false); + vim_FullName(f1, ffname, MAXPATHL, false); t1 = path_tail_with_sep(ffname); t2 = path_tail_with_sep(f2); return t1 - ffname == t2 - f2 @@ -2239,7 +2239,7 @@ int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int } /// @return true if "fname" matches with an entry in 'suffixes'. -int match_suffix(char *fname) +bool match_suffix(char *fname) { #define MAXSUFLEN 30 // maximum length of a file suffix char suf_buf[MAXSUFLEN]; @@ -2394,7 +2394,7 @@ static int path_to_absolute(const char *fname, char *buf, size_t len, int force) /// Check if file `fname` is a full (absolute) path. /// /// @return `true` if "fname" is absolute. -int path_is_absolute(const char *fname) +bool path_is_absolute(const char *fname) { #ifdef MSWIN if (*fname == NUL) { diff --git a/src/nvim/path.h b/src/nvim/path.h index 89f939dd02..a8eb893bb3 100644 --- a/src/nvim/path.h +++ b/src/nvim/path.h @@ -2,32 +2,33 @@ #include <stddef.h> // IWYU pragma: keep -#include "nvim/func_attr.h" #include "nvim/garray_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep -// Flags for expand_wildcards() -#define EW_DIR 0x01 // include directory names -#define EW_FILE 0x02 // include file names -#define EW_NOTFOUND 0x04 // include not found names -#define EW_ADDSLASH 0x08 // append slash to directory name -#define EW_KEEPALL 0x10 // keep all matches -#define EW_SILENT 0x20 // don't print "1 returned" from shell -#define EW_EXEC 0x40 // executable files -#define EW_PATH 0x80 // search in 'path' too -#define EW_ICASE 0x100 // ignore case -#define EW_NOERROR 0x200 // no error for bad regexp -#define EW_NOTWILD 0x400 // add match with literal name if exists -#define EW_KEEPDOLLAR 0x800 // do not escape $, $var is expanded +/// Flags for expand_wildcards() +enum { + EW_DIR = 0x01, ///< include directory names + EW_FILE = 0x02, ///< include file names + EW_NOTFOUND = 0x04, ///< include not found names + EW_ADDSLASH = 0x08, ///< append slash to directory name + EW_KEEPALL = 0x10, ///< keep all matches + EW_SILENT = 0x20, ///< don't print "1 returned" from shell + EW_EXEC = 0x40, ///< executable files + EW_PATH = 0x80, ///< search in 'path' too + EW_ICASE = 0x100, ///< ignore case + EW_NOERROR = 0x200, ///< no error for bad regexp + EW_NOTWILD = 0x400, ///< add match with literal name if exists + EW_KEEPDOLLAR = 0x800, ///< do not escape $, $var is expanded + EW_ALLLINKS = 0x1000, ///< also links not pointing to existing file + EW_SHELLCMD = 0x2000, ///< called from expand_shellcmd(), don't check + ///< if executable is in $PATH + EW_DODOT = 0x4000, ///< also files starting with a dot + EW_EMPTYOK = 0x8000, ///< no matches is not an error + EW_NOTENV = 0x10000, ///< do not expand environment variables + EW_NOBREAK = 0x20000, ///< do not invoke breakcheck +}; // Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND // is used when executing commands and EW_SILENT for interactive expanding. -#define EW_ALLLINKS 0x1000 // also links not pointing to existing file -#define EW_SHELLCMD 0x2000 // called from expand_shellcmd(), don't check - // if executable is in $PATH -#define EW_DODOT 0x4000 // also files starting with a dot -#define EW_EMPTYOK 0x8000 // no matches is not an error -#define EW_NOTENV 0x10000 // do not expand environment variables -#define EW_NOBREAK 0x20000 // do not invoke breakcheck /// Return value for the comparison of two files. Also @see path_full_compare. typedef enum file_comparison { diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 6e9f92c193..eca07f0144 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -6,16 +6,20 @@ #include <string.h> #include "nvim/ascii_defs.h" +#include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/diff.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/indent.h" #include "nvim/macros_defs.h" -#include "nvim/mark.h" +#include "nvim/mark_defs.h" +#include "nvim/marktree.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/move.h" #include "nvim/option.h" @@ -23,6 +27,7 @@ #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -31,14 +36,14 @@ /// Functions calculating horizontal size of text, when displayed in a window. -/// Return the number of characters 'c' will take on the screen, taking -/// into account the size of a tab. +/// Return the number of cells the first char in "p" will take on the screen, +/// taking into account the size of a tab. /// Also see getvcol() /// /// @param p /// @param col /// -/// @return Number of characters. +/// @return Number of cells. int win_chartabsize(win_T *wp, char *p, colnr_T col) { buf_T *buf = wp->w_buffer; @@ -48,48 +53,21 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col) return ptr2cells(p); } -/// Return the number of characters the string 's' will take on the screen, -/// taking into account the size of a tab. -/// -/// @param s -/// -/// @return Number of characters the string will take on the screen. -int linetabsize_str(char *s) -{ - return linetabsize_col(0, s); -} - -/// Like linetabsize_str(), but "s" starts at column "startcol". +/// Like linetabsize_str(), but "s" starts at virtual column "startvcol". /// /// @param startcol /// @param s /// -/// @return Number of characters the string will take on the screen. -int linetabsize_col(int startcol, char *s) +/// @return Number of cells the string will take on the screen. +int linetabsize_col(int startvcol, char *s) { - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, 0, startcol, s, s); - while (*cts.cts_ptr != NUL) { - cts.cts_vcol += lbr_chartabsize_adv(&cts); + CharsizeArg csarg; + CSType const cstype = init_charsize_arg(&csarg, curwin, 0, s); + if (cstype == kCharsizeFast) { + return linesize_fast(&csarg, startvcol, MAXCOL); + } else { + return linesize_regular(&csarg, startvcol, MAXCOL); } - clear_chartabsize_arg(&cts); - return cts.cts_vcol; -} - -/// Like linetabsize_str(), but for a given window instead of the current one. -/// -/// @param wp -/// @param line -/// @param len -/// -/// @return Number of characters the string will take on the screen. -int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) -{ - chartabsize_T cts; - init_chartabsize_arg(&cts, wp, lnum, 0, line, line); - win_linetabsize_cts(&cts, len); - clear_chartabsize_arg(&cts); - return cts.cts_vcol; } /// Return the number of cells line "lnum" of window "wp" will take on the @@ -99,144 +77,100 @@ int linetabsize(win_T *wp, linenr_T lnum) return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), (colnr_T)MAXCOL); } -void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) -{ - for (; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len); - MB_PTR_ADV(cts->cts_ptr)) { - cts->cts_vcol += win_lbr_chartabsize(cts, NULL); - } - // check for inline virtual text after the end of the line - if (len == MAXCOL && cts->cts_has_virt_text && *cts->cts_ptr == NUL) { - (void)win_lbr_chartabsize(cts, NULL); - cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right; - } -} +static const uint32_t inline_filter[4] = {[kMTMetaInline] = kMTFilterSelect }; -/// Prepare the structure passed to chartabsize functions. +/// Prepare the structure passed to charsize functions. /// -/// "line" is the start of the line, "ptr" is the first relevant character. +/// "line" is the start of the line. /// When "lnum" is zero do not use inline virtual text. -void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char *line, - char *ptr) +CSType init_charsize_arg(CharsizeArg *csarg, win_T *wp, linenr_T lnum, char *line) { - cts->cts_win = wp; - cts->cts_vcol = col; - cts->cts_line = line; - cts->cts_ptr = ptr; - cts->cts_max_head_vcol = 0; - cts->cts_cur_text_width_left = 0; - cts->cts_cur_text_width_right = 0; - cts->cts_has_virt_text = false; - cts->cts_row = lnum - 1; - - if (cts->cts_row >= 0 && wp->w_buffer->b_virt_text_inline > 0) { - marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter); - MTKey mark = marktree_itr_current(cts->cts_iter); - if (mark.pos.row == cts->cts_row) { - cts->cts_has_virt_text = true; + csarg->win = wp; + csarg->line = line; + csarg->max_head_vcol = 0; + csarg->cur_text_width_left = 0; + csarg->cur_text_width_right = 0; + csarg->virt_row = -1; + csarg->indent_width = INT_MIN; + csarg->use_tabstop = !wp->w_p_list || wp->w_p_lcs_chars.tab1; + + if (lnum > 0) { + if (marktree_itr_get_filter(wp->w_buffer->b_marktree, lnum - 1, 0, lnum, 0, + inline_filter, csarg->iter)) { + csarg->virt_row = lnum - 1; } } -} - -/// Free any allocated item in "cts". -void clear_chartabsize_arg(chartabsize_T *cts) -{ -} -/// like win_chartabsize(), but also check for line breaks on the screen -/// -/// @param cts -/// -/// @return The number of characters taken up on the screen. -int lbr_chartabsize(chartabsize_T *cts) -{ - if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL - && !curwin->w_p_bri && !cts->cts_has_virt_text) { - if (curwin->w_p_wrap) { - return win_nolbr_chartabsize(cts, NULL); - } - return win_chartabsize(curwin, cts->cts_ptr, cts->cts_vcol); + if (csarg->virt_row >= 0 + || (wp->w_p_wrap && (wp->w_p_lbr || wp->w_p_bri || *get_showbreak_value(wp) != NUL))) { + return kCharsizeRegular; + } else { + return kCharsizeFast; } - return win_lbr_chartabsize(cts, NULL); } -/// Call lbr_chartabsize() and advance the pointer. -/// -/// @param cts -/// -/// @return The number of characters take up on the screen. -int lbr_chartabsize_adv(chartabsize_T *cts) -{ - int retval = lbr_chartabsize(cts); - MB_PTR_ADV(cts->cts_ptr); - return retval; -} - -/// Get the number of characters taken up on the screen indicated by "cts". -/// "cts->cts_cur_text_width_left" and "cts->cts_cur_text_width_right" are set +/// Get the number of cells taken up on the screen for the given arguments. +/// "csarg->cur_text_width_left" and "csarg->cur_text_width_right" are set /// to the extra size for inline virtual text. -/// This function is used very often, keep it fast!!!! /// -/// If "headp" not NULL, set "*headp" to the size of 'showbreak'/'breakindent' -/// included in the return value. -/// When "cts->cts_max_head_vcol" is positive, only count in "*headp" the size -/// of 'showbreak'/'breakindent' before "cts->cts_max_head_vcol". -/// When "cts->cts_max_head_vcol" is negative, only count in "*headp" the size +/// When "csarg->max_head_vcol" is positive, only count in "head" the size +/// of 'showbreak'/'breakindent' before "csarg->max_head_vcol". +/// When "csarg->max_head_vcol" is negative, only count in "head" the size /// of 'showbreak'/'breakindent' before where cursor should be placed. -/// -/// Warning: "*headp" may not be set if it's 0, init to 0 before calling. -int win_lbr_chartabsize(chartabsize_T *cts, int *headp) +CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vcol, + int32_t const cur_char) { - win_T *wp = cts->cts_win; - char *line = cts->cts_line; // start of the line - char *s = cts->cts_ptr; - colnr_T vcol = cts->cts_vcol; - int mb_added = 0; - - cts->cts_cur_text_width_left = 0; - cts->cts_cur_text_width_right = 0; + csarg->cur_text_width_left = 0; + csarg->cur_text_width_right = 0; - // No 'linebreak', 'showbreak' and 'breakindent': return quickly. - if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL - && !cts->cts_has_virt_text) { - if (wp->w_p_wrap) { - return win_nolbr_chartabsize(cts, headp); - } - return win_chartabsize(wp, s, vcol); - } + win_T *wp = csarg->win; + buf_T *buf = wp->w_buffer; + char *line = csarg->line; + bool const use_tabstop = cur_char == TAB && csarg->use_tabstop; + int mb_added = 0; bool has_lcs_eol = wp->w_p_list && wp->w_p_lcs_chars.eol != NUL; // First get normal size, without 'linebreak' or inline virtual text - int size = win_chartabsize(wp, s, vcol); - if (*s == NUL && !has_lcs_eol) { - size = 0; // NUL is not displayed + int size; + int is_doublewidth = false; + if (use_tabstop) { + size = tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array); + } else if (*cur == NUL) { + // 1 cell for EOL list char (if present), as opposed to the two cell ^@ + // for a NUL character in the text. + size = has_lcs_eol ? 1 : 0; + } else if (cur_char < 0) { + size = kInvalidByteCells; + } else { + size = char2cells(cur_char); + is_doublewidth = size == 2 && cur_char > 0x80; } - bool is_doublewidth = size == 2 && MB_BYTE2LEN((uint8_t)(*s)) > 1; - if (cts->cts_has_virt_text) { + if (csarg->virt_row >= 0) { int tab_size = size; - int col = (int)(s - line); + int col = (int)(cur - line); while (true) { - MTKey mark = marktree_itr_current(cts->cts_iter); - if (mark.pos.row != cts->cts_row || mark.pos.col > col) { + MTKey mark = marktree_itr_current(csarg->iter); + if (mark.pos.row != csarg->virt_row || mark.pos.col > col) { break; } else if (mark.pos.col == col) { - if (!mt_end(mark) && mark.flags & (MT_FLAG_DECOR_VIRT_TEXT_INLINE)) { + if (!mt_end(mark) && (mark.flags & MT_FLAG_DECOR_VIRT_TEXT_INLINE) + && mt_scoped_in_win(mark, wp)) { DecorInline decor = mt_decor(mark); DecorVirtText *vt = decor.ext ? decor.data.ext.vt : NULL; while (vt) { if (!(vt->flags & kVTIsLines) && vt->pos == kVPosInline) { if (mt_right(mark)) { - cts->cts_cur_text_width_right += vt->width; + csarg->cur_text_width_right += vt->width; } else { - cts->cts_cur_text_width_left += vt->width; + csarg->cur_text_width_left += vt->width; } size += vt->width; - if (*s == TAB) { + if (use_tabstop) { // tab size changes because of the inserted text size -= tab_size; - tab_size = win_chartabsize(wp, s, vcol + size); + tab_size = tabstop_padding(vcol + size, buf->b_p_ts, buf->b_p_vts_array); size += tab_size; } } @@ -244,7 +178,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) } } } - marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter); + marktree_itr_next_filter(wp->w_buffer->b_marktree, csarg->iter, csarg->virt_row + 1, 0, + inline_filter); } } @@ -254,16 +189,17 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) mb_added = 1; } + char *const sbr = get_showbreak_value(wp); + // May have to add something for 'breakindent' and/or 'showbreak' // string at the start of a screen line. int head = mb_added; - char *const sbr = get_showbreak_value(wp); // When "size" is 0, no new screen line is started. if (size > 0 && wp->w_p_wrap && (*sbr != NUL || wp->w_p_bri)) { int col_off_prev = win_col_off(wp); int width2 = wp->w_width_inner - col_off_prev + win_col_off2(wp); colnr_T wcol = vcol + col_off_prev; - colnr_T max_head_vcol = cts->cts_max_head_vcol; + colnr_T max_head_vcol = csarg->max_head_vcol; int added = 0; // cells taken by 'showbreak'/'breakindent' before current char @@ -274,11 +210,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (wcol >= width2 && width2 > 0) { wcol %= width2; } - if (*sbr != NUL) { - head_prev += vim_strsize(sbr); - } - if (wp->w_p_bri) { - head_prev += get_breakindent_win(wp, line); + head_prev = csarg->indent_width; + if (head_prev == INT_MIN) { + head_prev = 0; + if (*sbr != NUL) { + head_prev += vim_strsize(sbr); + } + if (wp->w_p_bri) { + head_prev += get_breakindent_win(wp, line); + } + csarg->indent_width = head_prev; } if (wcol < head_prev) { head_prev -= wcol; @@ -295,12 +236,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (wcol + size > wp->w_width) { // cells taken by 'showbreak'/'breakindent' halfway current char - int head_mid = 0; - if (*sbr != NUL) { - head_mid += vim_strsize(sbr); - } - if (wp->w_p_bri) { - head_mid += get_breakindent_win(wp, line); + int head_mid = csarg->indent_width; + if (head_mid == INT_MIN) { + head_mid = 0; + if (*sbr != NUL) { + head_mid += vim_strsize(sbr); + } + if (wp->w_p_bri) { + head_mid += get_breakindent_win(wp, line); + } + csarg->indent_width = head_mid; } if (head_mid > 0 && wcol + size > wp->w_width_inner) { // Calculate effective window width. @@ -320,7 +265,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) head += (max_head_vcol - (vcol + head_prev + prev_rem) + width2 - 1) / width2 * head_mid; } else if (max_head_vcol < 0) { - int off = virt_text_cursor_off(cts, *s == NUL); + int off = virt_text_cursor_off(csarg, *cur == NUL); if (off >= prev_rem) { if (size > off) { head += (1 + (off - prev_rem) / width) * head_mid; @@ -335,25 +280,20 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) size += added; } - if (headp != NULL) { - *headp = head; - } - - colnr_T vcol_start = 0; // start from where to consider linebreak + bool need_lbr = false; // If 'linebreak' set check at a blank before a non-blank if the line - // needs a break here - if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width_inner != 0) { - char *t = cts->cts_line; + // needs a break here. + if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width_inner != 0 + && vim_isbreak((uint8_t)cur[0]) && !vim_isbreak((uint8_t)cur[1])) { + char *t = csarg->line; while (vim_isbreak((uint8_t)t[0])) { t++; } - vcol_start = (colnr_T)(t - cts->cts_line); + // 'linebreak' is only needed when not in leading whitespace. + need_lbr = cur >= t; } - if (wp->w_p_lbr && vcol_start <= vcol - && vim_isbreak((uint8_t)s[0]) - && !vim_isbreak((uint8_t)s[1]) - && wp->w_p_wrap - && wp->w_width_inner != 0) { + if (need_lbr) { + char *s = cur; // Count all characters from first non-blank after a blank up to next // non-blank after a blank. int numberextra = win_col_off(wp); @@ -385,39 +325,54 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) } } - return size; + return (CharSize){ .width = size, .head = head }; } -/// Like win_lbr_chartabsize(), except that we know 'linebreak' is off and -/// 'wrap' is on. This means we need to check for a double-byte character that -/// doesn't fit at the end of the screen line. -/// -/// @param cts -/// @param headp +/// Like charsize_regular(), except it doesn't handle inline virtual text, +/// 'linebreak', 'breakindent' or 'showbreak'. +/// Handles normal characters, tabs and wrapping. +/// This function is always inlined. /// -/// @return The number of characters take up on the screen. -static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp) +/// @see charsize_regular +/// @see charsize_fast +static inline CharSize charsize_fast_impl(win_T *const wp, bool use_tabstop, colnr_T const vcol, + int32_t const cur_char) + FUNC_ATTR_PURE FUNC_ATTR_ALWAYS_INLINE { - win_T *wp = cts->cts_win; - char *s = cts->cts_ptr; - colnr_T col = cts->cts_vcol; - - if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { - return tabstop_padding(col, - wp->w_buffer->b_p_ts, - wp->w_buffer->b_p_vts_array); - } - int n = ptr2cells(s); - - // Add one cell for a double-width character in the last column of the - // window, displayed with a ">". - if ((n == 2) && (MB_BYTE2LEN((uint8_t)(*s)) > 1) && in_win_border(wp, col)) { - if (headp != NULL) { - *headp = 1; + // A tab gets expanded, depending on the current column + if (cur_char == TAB && use_tabstop) { + return (CharSize){ + .width = tabstop_padding(vcol, wp->w_buffer->b_p_ts, + wp->w_buffer->b_p_vts_array) + }; + } else { + int width; + if (cur_char < 0) { + width = kInvalidByteCells; + } else { + width = char2cells(cur_char); + } + + // If a double-width char doesn't fit at the end of a line, it wraps to the next line, + // and the last column displays a '>'. + if (width == 2 && cur_char >= 0x80 && wp->w_p_wrap && in_win_border(wp, vcol)) { + return (CharSize){ .width = 3, .head = 1 }; + } else { + return (CharSize){ .width = width }; } - return 3; } - return n; +} + +/// Like charsize_regular(), except it doesn't handle inline virtual text, +/// 'linebreak', 'breakindent' or 'showbreak'. +/// Handles normal characters, tabs and wrapping. +/// Can be used if CSType is kCharsizeFast. +/// +/// @see charsize_regular +CharSize charsize_fast(CharsizeArg *csarg, colnr_T const vcol, int32_t const cur_char) + FUNC_ATTR_PURE +{ + return charsize_fast_impl(csarg->win, csarg->use_tabstop, vcol, cur_char); } /// Check that virtual column "vcol" is in the rightmost column of window "wp". @@ -448,19 +403,65 @@ static bool in_win_border(win_T *wp, colnr_T vcol) return (vcol - width1) % width2 == width2 - 1; } +/// Calculate virtual column until the given "len". +/// +/// @param arg Argument to charsize functions. +/// @param vcol Starting virtual column. +/// @param len First byte of the end character, or MAXCOL. +/// +/// @return virtual column before the character at "len", +/// or full size of the line if "len" is MAXCOL. +int linesize_regular(CharsizeArg *const csarg, int vcol, colnr_T const len) +{ + char *const line = csarg->line; + + StrCharInfo ci = utf_ptr2StrCharInfo(line); + while (ci.ptr - line < len && *ci.ptr != NUL) { + vcol += charsize_regular(csarg, ci.ptr, vcol, ci.chr.value).width; + ci = utfc_next(ci); + } + + // Check for inline virtual text after the end of the line. + if (len == MAXCOL && csarg->virt_row >= 0) { + (void)charsize_regular(csarg, ci.ptr, vcol, ci.chr.value); + vcol += csarg->cur_text_width_left + csarg->cur_text_width_right; + } + + return vcol; +} + +/// Like linesize_regular(), but can be used when CStype is kCharsizeFast. +/// +/// @see linesize_regular +int linesize_fast(CharsizeArg const *const csarg, int vcol, colnr_T const len) +{ + win_T *const wp = csarg->win; + bool const use_tabstop = csarg->use_tabstop; + + char *const line = csarg->line; + + StrCharInfo ci = utf_ptr2StrCharInfo(line); + while (ci.ptr - line < len && *ci.ptr != NUL) { + vcol += charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value).width; + ci = utfc_next(ci); + } + + return vcol; +} + /// Get how many virtual columns inline virtual text should offset the cursor. /// -/// @param cts should contain information stored by win_lbr_chartabsize() +/// @param csarg should contain information stored by charsize_regular() /// about widths of left and right gravity virtual text /// @param on_NUL whether this is the end of the line -static int virt_text_cursor_off(chartabsize_T *cts, bool on_NUL) +static int virt_text_cursor_off(const CharsizeArg *csarg, bool on_NUL) { int off = 0; if (!on_NUL || !(State & MODE_NORMAL)) { - off += cts->cts_cur_text_width_left; + off += csarg->cur_text_width_left; } if (!on_NUL && (State & MODE_NORMAL)) { - off += cts->cts_cur_text_width_right; + off += csarg->cur_text_width_right; } return off; } @@ -479,115 +480,53 @@ static int virt_text_cursor_off(chartabsize_T *cts, bool on_NUL) /// @param end void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end) { - char *ptr; // points to current char - char *posptr; // points to char at pos->col - int incr; - int head; - colnr_T *vts = wp->w_buffer->b_p_vts_array; - int ts = (int)wp->w_buffer->b_p_ts; - - colnr_T vcol = 0; - char *line = ptr = ml_get_buf(wp->w_buffer, pos->lnum); // start of the line + char *const line = ml_get_buf(wp->w_buffer, pos->lnum); // start of the line + int const end_col = pos->col; - if (pos->col == MAXCOL) { - // continue until the NUL - posptr = NULL; - } else { - // In a few cases the position can be beyond the end of the line. - for (colnr_T i = 0; i < pos->col; i++) { - if (ptr[i] == NUL) { - pos->col = i; - break; - } - } - posptr = ptr + pos->col; - posptr -= utf_head_off(line, posptr); - } - - chartabsize_T cts; + CharsizeArg csarg; bool on_NUL = false; - init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line); - cts.cts_max_head_vcol = -1; - - // This function is used very often, do some speed optimizations. - // When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set - // and there are no virtual text use a simple loop. - // Also use this when 'list' is set but tabs take their normal size. - if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL)) - && !wp->w_p_lbr - && *get_showbreak_value(wp) == NUL - && !wp->w_p_bri - && !cts.cts_has_virt_text) { - while (true) { - head = 0; - int c = (uint8_t)(*ptr); + CSType const cstype = init_charsize_arg(&csarg, wp, pos->lnum, line); + csarg.max_head_vcol = -1; - // make sure we don't go past the end of the line - if (c == NUL) { - // NUL at end of line only takes one column - incr = 1; + colnr_T vcol = 0; + CharSize char_size; + StrCharInfo ci = utf_ptr2StrCharInfo(line); + if (cstype == kCharsizeFast) { + bool const use_tabstop = csarg.use_tabstop; + while (true) { + if (*ci.ptr == NUL) { + // if cursor is at NUL, it is treated like 1 cell char + char_size = (CharSize){ .width = 1 }; break; } - - // A tab gets expanded, depending on the current column - if (c == TAB) { - incr = tabstop_padding(vcol, ts, vts); - } else { - // For utf-8, if the byte is >= 0x80, need to look at - // further bytes to find the cell width. - if (c >= 0x80) { - incr = utf_ptr2cells(ptr); - } else { - incr = byte2cells(c); - } - - // If a double-cell char doesn't fit at the end of a line - // it wraps to the next line, it's like this char is three - // cells wide. - if ((incr == 2) - && wp->w_p_wrap - && (MB_BYTE2LEN((uint8_t)(*ptr)) > 1) - && in_win_border(wp, vcol)) { - incr++; - head = 1; - } - } - - if ((posptr != NULL) && (ptr >= posptr)) { - // character at pos->col + char_size = charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value); + StrCharInfo const next = utfc_next(ci); + if (next.ptr - line > end_col) { break; } - - vcol += incr; - MB_PTR_ADV(ptr); + ci = next; + vcol += char_size.width; } } else { while (true) { - // A tab gets expanded, depending on the current column - // Other things also take up space. - head = 0; - incr = win_lbr_chartabsize(&cts, &head); - - // make sure we don't go past the end of the line - if (*cts.cts_ptr == NUL) { - // NUL at end of line only takes one column, unless there is virtual text - incr = MAX(1, cts.cts_cur_text_width_left + cts.cts_cur_text_width_right); + char_size = charsize_regular(&csarg, ci.ptr, vcol, ci.chr.value); + if (*ci.ptr == NUL) { + // if cursor is at NUL, it is treated like 1 cell char unless there is virtual text + char_size.width = MAX(1, csarg.cur_text_width_left + csarg.cur_text_width_right); on_NUL = true; break; } - - if ((posptr != NULL) && (cts.cts_ptr >= posptr)) { - // character at pos->col + StrCharInfo const next = utfc_next(ci); + if (next.ptr - line > end_col) { break; } - - cts.cts_vcol += incr; - MB_PTR_ADV(cts.cts_ptr); + ci = next; + vcol += char_size.width; } - vcol = cts.cts_vcol; - ptr = cts.cts_ptr; } - clear_chartabsize_arg(&cts); + + int head = char_size.head; + int incr = char_size.width; if (start != NULL) { *start = vcol + head; @@ -598,7 +537,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } if (cursor != NULL) { - if ((*ptr == TAB) + if (ci.chr.value == TAB && (State & MODE_NORMAL) && !wp->w_p_list && !virtual_active() @@ -606,7 +545,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // cursor at end *cursor = vcol + incr - 1; } else { - vcol += virt_text_cursor_off(&cts, on_NUL); + vcol += virt_text_cursor_off(&csarg, on_NUL); // cursor at start *cursor = vcol + head; } @@ -730,7 +669,8 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right /// Check if there may be filler lines anywhere in window "wp". bool win_may_fill(win_T *wp) { - return (wp->w_p_diff && diffopt_filler()) || wp->w_buffer->b_virt_line_blocks; + return ((wp->w_p_diff && diffopt_filler()) + || buf_meta_total(wp->w_buffer, kMTMetaLines)); } /// Return the number of filler lines above "lnum". @@ -795,14 +735,18 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool limit_winheight) int plines_win_nofold(win_T *wp, linenr_T lnum) { char *s = ml_get_buf(wp->w_buffer, lnum); - chartabsize_T cts; - init_chartabsize_arg(&cts, wp, lnum, 0, s, s); - if (*s == NUL && !cts.cts_has_virt_text) { + CharsizeArg csarg; + CSType const cstype = init_charsize_arg(&csarg, wp, lnum, s); + if (*s == NUL && csarg.virt_row < 0) { return 1; // be quick for an empty line } - win_linetabsize_cts(&cts, (colnr_T)MAXCOL); - clear_chartabsize_arg(&cts); - int64_t col = cts.cts_vcol; + + int64_t col; + if (cstype == kCharsizeFast) { + col = linesize_fast(&csarg, 0, MAXCOL); + } else { + col = linesize_regular(&csarg, 0, MAXCOL); + } // If list mode is on, then the '$' at the end of the line may take up one // extra column. @@ -841,26 +785,33 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) char *line = ml_get_buf(wp->w_buffer, lnum); - colnr_T col = 0; - chartabsize_T cts; + CharsizeArg csarg; + CSType const cstype = init_charsize_arg(&csarg, wp, lnum, line); - init_chartabsize_arg(&cts, wp, lnum, 0, line, line); - while (*cts.cts_ptr != NUL && --column >= 0) { - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); - MB_PTR_ADV(cts.cts_ptr); + colnr_T vcol = 0; + StrCharInfo ci = utf_ptr2StrCharInfo(line); + if (cstype == kCharsizeFast) { + bool const use_tabstop = csarg.use_tabstop; + while (*ci.ptr != NUL && --column >= 0) { + vcol += charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value).width; + ci = utfc_next(ci); + } + } else { + while (*ci.ptr != NUL && --column >= 0) { + vcol += charsize_regular(&csarg, ci.ptr, vcol, ci.chr.value).width; + ci = utfc_next(ci); + } } - // If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're not + // If current char is a TAB, and the TAB is not displayed as ^I, and we're not // in MODE_INSERT state, then col must be adjusted so that it represents the // last screen position of the TAB. This only fixes an error when the TAB // wraps from one screen line to the next (when 'columns' is not a multiple // of 'ts') -- webb. - col = cts.cts_vcol; - if (*cts.cts_ptr == TAB && (State & MODE_NORMAL) - && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { - col += win_lbr_chartabsize(&cts, NULL) - 1; + colnr_T col = vcol; + if (ci.chr.value == TAB && (State & MODE_NORMAL) && csarg.use_tabstop) { + col += win_charsize(cstype, col, ci.ptr, ci.chr.value, &csarg).width - 1; } - clear_chartabsize_arg(&cts); // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. int width = wp->w_width_inner - win_col_off(wp); diff --git a/src/nvim/plines.h b/src/nvim/plines.h index 6aede88c8b..658206e1be 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -3,26 +3,97 @@ #include <stdbool.h> #include <stdint.h> -#include "nvim/buffer_defs.h" -#include "nvim/marktree.h" -#include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/func_attr.h" +#include "nvim/marktree_defs.h" +#include "nvim/pos_defs.h" +#include "nvim/types_defs.h" -/// Argument for lbr_chartabsize(). +typedef bool CSType; + +enum { + kCharsizeRegular, + kCharsizeFast, +}; + +/// Argument for char size functions. typedef struct { - win_T *cts_win; - char *cts_line; ///< start of the line - char *cts_ptr; ///< current position in line - int cts_row; + win_T *win; + char *line; ///< Start of the line. + + bool use_tabstop; ///< Use 'tabstop' instead of char2cells() for a TAB. + int indent_width; ///< Width of 'showbreak' and 'breakindent' on wrapped + ///< parts of lines, INT_MIN if not yet calculated. - bool cts_has_virt_text; ///< true if if there is inline virtual text - int cts_cur_text_width_left; ///< width of virtual text left of cursor - int cts_cur_text_width_right; ///< width of virtual text right of cursor - MarkTreeIter cts_iter[1]; + int virt_row; ///< Row for virtual text, -1 if no virtual text. + int cur_text_width_left; ///< Width of virtual text left of cursor. + int cur_text_width_right; ///< Width of virtual text right of cursor. - int cts_vcol; ///< virtual column at current position - int cts_max_head_vcol; ///< see win_lbr_chartabsize() -} chartabsize_T; + int max_head_vcol; ///< See charsize_regular(). + MarkTreeIter iter[1]; +} CharsizeArg; + +typedef struct { + int width; + int head; ///< Size of 'breakindent' etc. before the character (included in width). +} CharSize; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "plines.h.generated.h" #endif + +static inline CharSize win_charsize(CSType cstype, int vcol, char *ptr, int32_t chr, + CharsizeArg *csarg) + REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; + +/// Get the number of cells taken up on the screen by the given character at vcol. +/// "csarg->cur_text_width_left" and "csarg->cur_text_width_right" are set +/// to the extra size for inline virtual text. +/// +/// When "csarg->max_head_vcol" is positive, only count in "head" the size +/// of 'showbreak'/'breakindent' before "csarg->max_head_vcol". +/// When "csarg->max_head_vcol" is negative, only count in "head" the size +/// of 'showbreak'/'breakindent' before where cursor should be placed. +static inline CharSize win_charsize(CSType cstype, int vcol, char *ptr, int32_t chr, + CharsizeArg *csarg) +{ + if (cstype == kCharsizeFast) { + return charsize_fast(csarg, vcol, chr); + } else { + return charsize_regular(csarg, ptr, vcol, chr); + } +} + +static inline int linetabsize_str(char *s) + REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; + +/// Return the number of cells the string "s" will take on the screen, +/// taking into account the size of a tab. +/// +/// @param s +/// +/// @return Number of cells the string will take on the screen. +static inline int linetabsize_str(char *s) +{ + return linetabsize_col(0, s); +} + +static inline int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) + REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; + +/// Like linetabsize_str(), but for a given window instead of the current one. +/// +/// @param wp +/// @param line +/// @param len +/// +/// @return Number of cells the string will take on the screen. +static inline int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) +{ + CharsizeArg csarg; + CSType const cstype = init_charsize_arg(&csarg, wp, lnum, line); + if (cstype == kCharsizeFast) { + return linesize_fast(&csarg, 0, len); + } else { + return linesize_regular(&csarg, 0, len); + } +} diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index 68e572911c..6c2008926a 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -53,16 +53,16 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) list(SORT NVIM_RELATIVE_SOURCES) add_custom_command( OUTPUT ${NVIM_POT} - COMMAND $<TARGET_FILE:nvim> -u NONE -i NONE -n --headless --cmd "set cpo+=+" + COMMAND $<TARGET_FILE:nvim_bin> -u NONE -i NONE -n --headless --cmd "set cpo+=+" -S ${CMAKE_CURRENT_SOURCE_DIR}/tojavascript.vim ${NVIM_POT} ${PROJECT_SOURCE_DIR}/runtime/optwin.vim COMMAND ${XGETTEXT_PRG} -o ${NVIM_POT} --default-domain=nvim --add-comments --keyword=_ --keyword=N_ --keyword=NGETTEXT:1,2 -D ${CMAKE_CURRENT_SOURCE_DIR} -D ${CMAKE_CURRENT_BINARY_DIR} ${NVIM_RELATIVE_SOURCES} optwin.js - COMMAND $<TARGET_FILE:nvim> -u NONE -i NONE -n --headless --cmd "set cpo+=+" + COMMAND $<TARGET_FILE:nvim_bin> -u NONE -i NONE -n --headless --cmd "set cpo+=+" -S ${CMAKE_CURRENT_SOURCE_DIR}/fixfilenames.vim ${NVIM_POT} ../../../runtime/optwin.vim VERBATIM - DEPENDS ${NVIM_SOURCES} nvim nvim_runtime_deps) + DEPENDS ${NVIM_SOURCES} nvim_bin nvim_runtime_deps) set(LANGUAGE_MO_FILES) set(UPDATE_PO_TARGETS) @@ -88,7 +88,7 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) set(poFile ${CMAKE_CURRENT_SOURCE_DIR}/${name}.po) add_custom_target(check-po-${name} - COMMAND $<TARGET_FILE:nvim> -u NONE -n -e + COMMAND $<TARGET_FILE:nvim_bin> -u NONE -n -e -S ${CMAKE_CURRENT_SOURCE_DIR}/check.vim -c "if error == 0 | q | endif" -c cq ${poFile} || ${CMAKE_COMMAND} -E echo "${name}.po failed the check." @@ -182,6 +182,7 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) BuildMo(${LANGUAGE}) endforeach() - add_custom_target(translations ALL DEPENDS ${LANGUAGE_MO_FILES}) + add_custom_target(nvim_translations DEPENDS ${LANGUAGE_MO_FILES}) add_custom_target(update-po DEPENDS ${UPDATE_PO_TARGETS}) + add_dependencies(nvim nvim_translations) endif() diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po index 0345d3e243..ba0301589c 100644 --- a/src/nvim/po/da.po +++ b/src/nvim/po/da.po @@ -4049,7 +4049,7 @@ msgid "freeing %ld lines" msgstr "frigør %ld linjer" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " i \"%c" #, c-format diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po index d9058326d5..fae6fe2297 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -4313,7 +4313,7 @@ msgid "freeing %ld lines" msgstr "libration de %ld lignes" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " dans \"%c" #, c-format diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po index f3c55fe9ab..08b97ec180 100644 --- a/src/nvim/po/tr.po +++ b/src/nvim/po/tr.po @@ -4208,7 +4208,7 @@ msgstr[0] "%<PRId64> satır değiştirildi" msgstr[1] "%<PRId64> satır değiştirildi" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " \"%c" #, c-format diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index 83898cda12..e62de86b09 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -14,8 +14,8 @@ msgid "" msgstr "" "Project-Id-Version: Neovim Ukrainian\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-08-26 20:36+0300\n" -"PO-Revision-Date: 2023-08-26 21:00+0300\n" +"POT-Creation-Date: 2023-12-31 12:04+0200\n" +"PO-Revision-Date: 2023-12-31 12:35+0200\n" "Last-Translator: Анатолій Сахнік <sakhnik@gmail.com>\n" "Language-Team: Ukrainian\n" "Language: uk\n" @@ -1434,6 +1434,9 @@ msgstr " [Змінено]" msgid "[Not edited]" msgstr "[Не редаговано]" +msgid "[New]" +msgstr "[Новий]" + msgid "[Read errors]" msgstr "[Помилки читання]" @@ -1481,14 +1484,6 @@ msgstr " (%d з %d)" msgid " ((%d) of %d)" msgstr " ((%d) з %d)" -#, c-format -msgid " (file %d of %d)" -msgstr " (файл %d з %d)" - -#, c-format -msgid " (file (%d) of %d)" -msgstr " (файл (%d) з %d)" - msgid "E382: Cannot write, 'buftype' option is set" msgstr "E382: Не можу записати, вказана опція 'buftype'" @@ -1584,6 +1579,9 @@ msgstr "[конвертовано]" msgid "[Device]" msgstr "[Пристрій]" +msgid "[noeol]" +msgstr "[noeol]" + msgid " [a]" msgstr "[д]" @@ -1636,9 +1634,6 @@ msgstr " тип файлу\n" msgid "'history' option is zero" msgstr "Опція 'history' порожня" -msgid "E474: Failed to convert list to msgpack string buffer" -msgstr "E474: Не вдалося перетворити список у текстовий буфер msgpack" - msgid "E548: Digit expected" msgstr "E548: Очікується цифра" @@ -2062,16 +2057,6 @@ msgstr "" "\n" "\tВостаннє змінена у " -msgid "E5009: $VIMRUNTIME is empty or unset" -msgstr "E5009: $VIMRUNTIME порожня чи не встановлена" - -#, c-format -msgid "E5009: Invalid $VIMRUNTIME: %s" -msgstr "E5009: Некоректна $VIMRUNTIME: %s" - -msgid "E5009: Invalid 'runtimepath'" -msgstr "E5009: Некоректний 'runtimepath'" - msgid "E977: Can only compare Blob with Blob" msgstr "E977: Blob можна порівняти тільки з Blob" @@ -2366,10 +2351,6 @@ msgstr "E998: Скорочення порожнього %s без початко msgid "E1132: Missing function argument" msgstr "E1132: Бракує аргументу функції" -#, c-format -msgid "Error converting the call result: %s" -msgstr "Не вдалося перетворити результат виклику: %s" - msgid "add() argument" msgstr "аргумент add()" @@ -2839,8 +2820,8 @@ msgid "E940: Cannot lock or unlock variable %s" msgstr "E940: Неможливо заблокувати чи розблокувати змінну %s" #, c-format -msgid "E963: Setting %s to value with wrong type" -msgstr "E963: Встановлення значення з неправильним типом у %s" +msgid "E963: Setting v:%s to value with wrong type" +msgstr "E963: Встановлення значення v:%s з неправильним типом" msgid "E991: Cannot use =<< here" msgstr "E991: Тут не можна застосувати =<<" @@ -3249,6 +3230,16 @@ msgstr "E500: Результат — порожній рядок" msgid "Untitled" msgstr "Неназваний" +msgid "E5009: $VIMRUNTIME is empty or unset" +msgstr "E5009: $VIMRUNTIME порожня чи не встановлена" + +#, c-format +msgid "E5009: Invalid $VIMRUNTIME: %s" +msgstr "E5009: Некоректна $VIMRUNTIME: %s" + +msgid "E5009: Invalid 'runtimepath'" +msgstr "E5009: Некоректний 'runtimepath'" + msgid "E583: Multiple :else" msgstr "E583: Не одне :else" @@ -3476,27 +3467,12 @@ msgstr "Конвертація з 'charconvert' не вдалася" msgid "can't read output of 'charconvert'" msgstr "не вдалося прочитати вивід 'charconvert'" -msgid "[New File]" -msgstr "[Новий файл]" - -msgid "[New]" -msgstr "[Новий]" - -msgid "[dos format]" -msgstr "[формат dos]" - msgid "[dos]" msgstr "[dos]" -msgid "[mac format]" -msgstr "[формат mac]" - msgid "[mac]" msgstr "[mac]" -msgid "[unix format]" -msgstr "[формат unix]" - msgid "[unix]" msgstr "[unix]" @@ -3514,12 +3490,6 @@ msgstr[0] "%<PRId64> байт" msgstr[1] "%<PRId64> байти" msgstr[2] "%<PRId64> байтів" -msgid "[Incomplete last line]" -msgstr "[Неповний останній рядок]" - -msgid "[noeol]" -msgstr "[noeol]" - #, c-format msgid "E208: Error writing to \"%s\"" msgstr "E208: Помилка запису у «%s»" @@ -3602,18 +3572,18 @@ msgid "E351: Cannot delete fold with current 'foldmethod'" msgstr "E351: Не вдалося знищити згортку методом 'foldmethod'" #, c-format -msgid "+--%3ld line folded" -msgid_plural "+--%3ld lines folded " -msgstr[0] "+--%3ld рядок згорнуто " +msgid "+--%3d line folded" +msgid_plural "+--%3d lines folded " +msgstr[0] "+--%3ld рядок згорнуто" msgstr[1] "+--%3ld рядки згорнуто " msgstr[2] "+--%3ld рядків згорнуто " #, c-format -msgid "+-%s%3ld line: " -msgid_plural "+-%s%3ld lines: " -msgstr[0] "+-%s%3ld рядок: " -msgstr[1] "+-%s%3ld рядки: " -msgstr[2] "+-%s%3ld рядків: " +msgid "+-%s%3d line: " +msgid_plural "+-%s%3d lines: " +msgstr[0] "+-%s%3-о рядок: " +msgstr[1] "+-%s%3-о рядки: " +msgstr[2] "+-%s%3-о рядків: " msgid "E223: Recursive mapping" msgstr "E223: Заміна клавіш рекурсивна" @@ -3925,6 +3895,10 @@ msgstr "E119: Замало аргументів для функції %s" msgid "E716: Key not present in Dictionary: \"%s\"" msgstr "E716: Немає такого ключа у словнику: «%s»" +#, c-format +msgid "E716: Key not present in Dictionary: \"%.*s\"" +msgstr "E716: Немає такого ключа у словнику: «%.*s»" + msgid "E714: List required" msgstr "E714: Потрібен List" @@ -4112,6 +4086,10 @@ msgstr "E1278: Заблудла '}' без відповідної '{': %s" msgid "E1279: Missing '}': %s" msgstr "E1279: Пропущено '}': %s" +#, c-format +msgid "E1510: Value too large: %s" +msgstr "E1510: Величина завелика: %s" + msgid "E5767: Cannot use :undo! to redo or move to a different undo branch" msgstr "" "E5767: Неможливо застосувати :undo! щоб повторити або перейти до іншої гілки " @@ -4345,11 +4323,11 @@ msgid "E1502: Lua failed to grow stack to %i" msgstr "E1502: Lua не вдалося збільшити стек до %i" msgid "" -"E5100: Cannot convert given lua table: table should either have a sequence " -"of positive integer keys or contain only string keys" +"E5100: Cannot convert given lua table: table should contain either only " +"integer keys or only string keys" msgstr "" -"E5100: Не вдалося перетворити таблицю lua: таблиця повинна мати " -"послідовність додатних чисел як ключі або текстові ключі" +"E5100: Не вдалося перетворити таблицю lua: таблиця повинна мати або тільки " +"цілочисельні ключі або тільки текстові ключі" msgid "E5101: Cannot convert given lua type" msgstr "E5101: Не вдалося перетворити тип lua" @@ -4362,9 +4340,11 @@ msgstr "E5102: Lua не вдалося збільшити стек до %i" msgid "Error executing vim.schedule lua callback: %.*s" msgstr "Помилка виконання обробника lua vim.schedule: %.*s" +#, c-format msgid "E970: Failed to initialize lua interpreter\n" msgstr "E970: Не вдалося ініціалізувати інтерпретатор lua\n" +#, c-format msgid "E970: Failed to initialize builtin lua modules\n" msgstr "E970: Не вдалося ініціалізувати інтерпретатор lua\n" @@ -4469,6 +4449,7 @@ msgstr "--embed конфліктує з -es/-Es/-l" msgid "Cannot open for reading: \"%s\": %s\n" msgstr "Не вдалося відкрити для читання: \"%s\": %s\n" +#, c-format msgid "Cannot open for script output: \"" msgstr "Не вдалося відкрити як вихідний файл: \"" @@ -4483,6 +4464,7 @@ msgstr "E5422: Суперечливі конфігурації: «%s» «%s»" msgid "E282: Cannot read from \"%s\"" msgstr "E282: Не вдалося прочитати з «%s»" +#, c-format msgid "" "\n" "More info with \"" @@ -4490,12 +4472,15 @@ msgstr "" "\n" "Дізнайтеся більше: \"" +#, c-format msgid "Usage:\n" msgstr "Вжиток:\n" +#, c-format msgid " nvim [options] [file ...]\n" msgstr " nvim [опції] [файл ...]\n" +#, c-format msgid "" "\n" "Options:\n" @@ -4503,75 +4488,95 @@ msgstr "" "\n" "Опції:\n" +#, c-format msgid " --cmd <cmd> Execute <cmd> before any config\n" msgstr "" " --cmd <команда> Виконати <команду> перед будь-якою конфігурацією\n" +#, c-format msgid " +<cmd>, -c <cmd> Execute <cmd> after config and first file\n" msgstr "" " +<cmd>, -c <команда> Виконати <команду> після завантаження першого файлу\n" +#, c-format msgid " -l <script> [args...] Execute Lua <script> (with optional args)\n" msgstr " -l <скрипт> [args...] Виконати <скрипт> Lua (з арг-тами, якщо є)\n" +#, c-format msgid " -S <session> Source <session> after loading the first file\n" msgstr "" " -S <сеанс> Виконати <сеанс> після першого завантаженого файлу\n" +#, c-format msgid " -s <scriptin> Read Normal mode commands from <scriptin>\n" msgstr "" " -s <скрипт> Зчитати команди нормального режиму з файлу <скрипт>\n" +#, c-format msgid " -u <config> Use this config file\n" msgstr " -u <config> Вжити цей файл конфігурації\n" +#, c-format msgid " -d Diff mode\n" msgstr " -d Режим порівняння\n" +#, c-format msgid " -es, -Es Silent (batch) mode\n" msgstr " -es, -Es Мовчазний (пакетний) режим\n" +#, c-format msgid " -h, --help Print this help message\n" msgstr " -h, --help Надрукувати це повідомлення\n" +#, c-format msgid " -i <shada> Use this shada file\n" msgstr " -i <shada> Вжити цей файл shada\n" +#, c-format msgid " -n No swap file, use memory only\n" msgstr "" " -n Не використовувати файл обміну, тримати усе в " "пам'яті\n" +#, c-format msgid " -o[N] Open N windows (default: one per file)\n" msgstr " -o[N] Відкрити N вікон (стандартно: одне на файл)\n" +#, c-format msgid "" " -O[N] Open N vertical windows (default: one per file)\n" msgstr "" " -o[N] Відкрити N вертикальних вікон (стандартно: одне на " "файл)\n" +#, c-format msgid " -p[N] Open N tab pages (default: one per file)\n" msgstr "" " -p[N] Відкрити N вкладок (стандартно: одна на файл)\n" +#, c-format msgid " -R Read-only (view) mode\n" msgstr " -R Режим тільки для читання\n" +#, c-format msgid " -v, --version Print version information\n" msgstr " -v, --version Надрукувати інформацію про версію програми\n" +#, c-format msgid " -V[N][file] Verbose [level][file]\n" msgstr " -V[N][файл] Більше повідомлень [рівень][файл]\n" +#, c-format msgid " -- Only file names after this\n" msgstr " -- Лише назви файлів після цього\n" +#, c-format msgid " --api-info Write msgpack-encoded API metadata to stdout\n" msgstr "" " --api-info Записати метадані API, серіалізовані у msgpack, у " "stdout\n" +#, c-format msgid "" " --clean \"Factory defaults\" (skip user config and plugins, " "shada)\n" @@ -4579,25 +4584,32 @@ msgstr "" " --clean «з нуля» (пропустити конфігурацію користувача і " "плагіни, shada)\n" +#, c-format msgid " --embed Use stdin/stdout as a msgpack-rpc channel\n" msgstr "" " --embed Використати stdin/stdout, як канал msgpack-rpc\n" +#, c-format msgid " --headless Don't start a user interface\n" msgstr " --headless Не запускати інтерфейс користувача\n" +#, c-format msgid " --listen <address> Serve RPC API from this address\n" msgstr " --listen <адреса> Обслуговувати RPC API за цією адресою\n" +#, c-format msgid " --remote[-subcommand] Execute commands remotely on a server\n" msgstr " --remote[-subcommand] Виконати команди на віддаленому сервері\n" +#, c-format msgid " --server <address> Specify RPC server to send commands to\n" msgstr " --server <адреса> Задати сервер RPC, куди слати команди\n" +#, c-format msgid " --startuptime <file> Write startup timing messages to <file>\n" msgstr " --startuptime <файл> Записати профіль запуску до <файлу>\n" +#, c-format msgid "" "\n" "See \":help startup-options\" for all options.\n" @@ -4624,6 +4636,10 @@ msgstr "E227: Вже є заміна клавіш для %s" msgid "E460: Entries missing in mapset() dict argument" msgstr "E460: Бракує записів у словниковому аргументі mapset()" +#, c-format +msgid "E1276: Illegal map mode string: '%s'" +msgstr "E1276: Недозволений режим заміни клавіш: '%s'" + msgid "No abbreviation found" msgstr "Скорочення не знайдено" @@ -5217,9 +5233,8 @@ msgstr "E337: Меню не знайдено — перевірте назви msgid "Error detected while processing %s:" msgstr "Виявлено помилку під час виконання %s:" -#, c-format -msgid "line %4ld:" -msgstr "рядок %4ld:" +msgid "line %4" +msgstr "рядок %4" #, c-format msgid "E354: Invalid register name: '%s'" @@ -5232,18 +5247,18 @@ msgid "Press ENTER or type command to continue" msgstr "Натисніть ENTER або введіть команду для продовження" #, c-format -msgid "%ld more line" -msgid_plural "%ld more lines" -msgstr[0] "ще %ld рядок" -msgstr[1] "ще %ld рядки" -msgstr[2] "ще %ld рядків" +msgid "%d more line" +msgid_plural "%d more lines" +msgstr[0] "ще %d рядок" +msgstr[1] "ще %d рядки" +msgstr[2] "ще %d рядків" #, c-format -msgid "%ld line less" -msgid_plural "%ld fewer lines" -msgstr[0] "на %ld рядок менше" -msgstr[1] "на %ld рядки менше" -msgstr[2] "на %ld рядків менше" +msgid "%d line less" +msgid_plural "%d fewer lines" +msgstr[0] "на %d рядок менше" +msgstr[1] "на %d рядки менше" +msgstr[2] "на %d рядків менше" msgid " (Interrupted)" msgstr " (Перервано)" @@ -5360,7 +5375,7 @@ msgstr[1] "Змінено %<PRId64> рядки" msgstr[2] "Змінено %<PRId64> рядків" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " у \"%c" #, c-format @@ -5447,9 +5462,6 @@ msgstr "E520: Не дозволено у modeline" msgid "E992: Not allowed in a modeline when 'modelineexpr' is off" msgstr "E992: Не дозволено у modeline, коли 'modelineexpr' вимкнено" -msgid "E846: Key code not set" -msgstr "E846: Код ключа не встановлено" - msgid "E521: Number required after =" msgstr "E521: Після = потрібно вказати число" @@ -5468,6 +5480,9 @@ msgstr "E593: Потрібно щонайменше %d рядків" msgid "E594: Need at least %d columns" msgstr "E594: Потрібно щонайменше %d стовпців" +msgid "Cannot unset global option value" +msgstr "Неможливо прибрати значення глобальної опції" + #, c-format msgid "Invalid value for option '%s': expected %s, got %s %s" msgstr "Некоректне значення опції '%s': очікується %s, отримано %s %s" @@ -5511,20 +5526,10 @@ msgstr "E589: Опції 'backupext' і 'patchmode' однакові" msgid "E595: 'showbreak' contains unprintable or wide character" msgstr "E595: 'showbreak' містить недруковні або розширені символи" -msgid "E1336: Internal error: shortmess too long" -msgstr "E1336: Внутрішня помилка: занадто довгий shortmess" - #, c-format msgid "E539: Illegal character <%s>" msgstr "E539: Недозволений символ <%s>" -#, c-format -msgid "For option %s" -msgstr "Для опції %s" - -msgid "E5080: Digit expected" -msgstr "E5080: Очікується цифра" - msgid "E524: Missing colon" msgstr "E524: Бракує двокрапки" @@ -5532,6 +5537,17 @@ msgid "E525: Zero length string" msgstr "E525: Рядок порожній" #, c-format +msgid "E537: 'commentstring' must be empty or contain %s" +msgstr "E537: 'commentstring' має бути порожньою чи містити %s" + +#, c-format +msgid "E535: Illegal character after <%c>" +msgstr "E535: Недозволений символ після <%c>" + +msgid "E5080: Digit expected" +msgstr "E5080: Очікується цифра" + +#, c-format msgid "E526: Missing number after <%s>" msgstr "E526: Після <%s> бракує числа" @@ -5541,14 +5557,6 @@ msgstr "E527: Бракує коми" msgid "E528: Must specify a ' value" msgstr "E528: Потрібно вказати значення '" -#, c-format -msgid "E535: Illegal character after <%c>" -msgstr "E535: Недозволений символ після <%c>" - -#, c-format -msgid "E537: 'commentstring' must be empty or contain %s" -msgstr "E537: 'commentstring' має бути порожньою чи містити %s" - msgid "E834: Conflicts with value of 'listchars'" msgstr "E834: Конфліктує із значенням 'listchars'" @@ -5563,6 +5571,19 @@ msgstr "dlerror = «%s»" msgid "E5420: Failed to write to file: %s" msgstr "E5420: Не вдалося записати у файл: %s" +msgid "E1506: Buffer too small to copy xattr value or key" +msgstr "E1506: Буфер замалий для копіювання значення чи ключа xattr" + +msgid "" +"E1508: Size of the extended attribute value is larger than the maximum size " +"allowed" +msgstr "" +"E1508: Розмір величини розширеного атрибуту більший, ніж максимально " +"дозволений розмір" + +msgid "E1509: Error occurred when reading or writing extended attribute" +msgstr "E1509: Сталася помилка при читанні чи записі розширеного атрибуту" + msgid "Vim: Error reading input, exiting...\n" msgstr "Vim: Помилка читання вводу, робота завершується...\n" @@ -5764,6 +5785,110 @@ msgstr "E554: Синтаксична помилка в %s{...}" msgid "E888: (NFA regexp) cannot repeat %s" msgstr "E888: (NFA regexp) неможливо повторити %s" +msgid "E65: Illegal back reference" +msgstr "E65: Некоректне зворотне посилання" + +#, c-format +msgid "E64: %s%c follows nothing" +msgstr "E64: %s%c має йти за чимось" + +msgid "E68: Invalid character after \\z" +msgstr "E68: Некоректний символ після \\z" + +#, c-format +msgid "E678: Invalid character after %s%%[dxouU]" +msgstr "E678: Некоректний символ після %s%%[dxouU]" + +#, c-format +msgid "E71: Invalid character after %s%%" +msgstr "E71: Некоректний символ після %s%%" + +#, c-format +msgid "E60: Too many complex %s{...}s" +msgstr "E60: Забагато складних %s{...}" + +#, c-format +msgid "E61: Nested %s*" +msgstr "E61: Вкладені %s*" + +#, c-format +msgid "E62: Nested %s%c" +msgstr "E62: Вкладені %s%c" + +msgid "E50: Too many \\z(" +msgstr "E50: Забагато \\z(" + +#, c-format +msgid "E51: Too many %s(" +msgstr "E51: Забагато %s(" + +msgid "E52: Unmatched \\z(" +msgstr "E52: Немає пари \\z(" + +msgid "E339: Pattern too long" +msgstr "E339: Задовгий шаблон" + +#, c-format +msgid "External submatches:\n" +msgstr "Зовнішні збіги:\n" + +msgid "E865: (NFA) Regexp end encountered prematurely" +msgstr "E865: (NFA) Обірваний регулярний вираз" + +#, c-format +msgid "E866: (NFA regexp) Misplaced %c" +msgstr "E866: (NFA regexp) Недоречний %c" + +#, c-format +msgid "E877: (NFA regexp) Invalid character class: %<PRId64>" +msgstr "E877: (NFA regexp) Некоректний клас символів: %<PRId64>" + +msgid "E951: \\% value too large" +msgstr "E951: Величина \\% завелика" + +#, c-format +msgid "E867: (NFA) Unknown operator '\\z%c'" +msgstr "E867: (NFA) Невідомий оператор '\\z%c'" + +#, c-format +msgid "E867: (NFA) Unknown operator '\\%%%c'" +msgstr "E867: (NFA) Невідомий оператор '\\%%%c'" + +#, c-format +msgid "E869: (NFA) Unknown operator '\\@%c'" +msgstr "E869: (NFA) Невідомий оператор '\\@%c'" + +msgid "E870: (NFA regexp) Error reading repetition limits" +msgstr "E870: (NFA regexp) Помилка меж повторення" + +msgid "E871: (NFA regexp) Can't have a multi follow a multi" +msgstr "E871: (NFA regexp) Не можна мульти- за мульти-" + +msgid "E872: (NFA regexp) Too many '('" +msgstr "E872: (NFA regexp) Забагато '('" + +msgid "E879: (NFA regexp) Too many \\z(" +msgstr "E879: (NFA regexp) Забагато \\z(" + +msgid "E873: (NFA regexp) proper termination error" +msgstr "E873: (NFA regexp) неправильне завершення" + +msgid "Could not open temporary log file for writing, displaying on stderr... " +msgstr "Не вдалося відкрити тимчасовий файл для запису, показую в stderr…" + +msgid "E874: (NFA) Could not pop the stack!" +msgstr "E874: (NFA) Не вдалося витягнути зі стеку!" + +msgid "" +"E875: (NFA regexp) (While converting from postfix to NFA),too many states " +"left on stack" +msgstr "" +"E875: (NFA regexp) (При перетворенні з постфікс у NFA), забагато станів " +"залишилося у стеку" + +msgid "E876: (NFA regexp) Not enough space to store the whole NFA " +msgstr "E876: (NFA regexp) Недостатньо місця для збереження всього NFA " + msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " "used " @@ -6117,9 +6242,6 @@ msgstr "" "Помилка при читанні файлу ShaDa: список буферів у позиції %<PRIu64> містить " "поле, яке не має назву файлу" -msgid "[Deleted]" -msgstr "[Знищено]" - msgid "" "\n" "--- Signs ---" @@ -6132,15 +6254,15 @@ msgid "Signs for %s:" msgstr "Позначки для %s:" #, c-format -msgid " group=%s" -msgstr " група=%s" +msgid " name=%s" +msgstr " назв=%s" #, c-format -msgid " line=%ld id=%d%s name=%s priority=%d" -msgstr " рядок=%ld id=%d%s назва=%s пріоритет=%d" +msgid " group=%s" +msgstr " група=%s" -msgid "E612: Too many signs defined" -msgstr "E612: Визначено забагато надписів" +msgid " line=%" +msgstr " ряд.=%" #, c-format msgid "E239: Invalid sign text: %s" @@ -6150,20 +6272,23 @@ msgstr "E239: Некоректний надпис: %s" msgid "E155: Unknown sign: %s" msgstr "E155: Невідомий надпис: %s" +msgid " (not supported)" +msgstr " (не підтримується)" + #, c-format msgid "E885: Not possible to change sign %s" msgstr "E885: Неможливо змінити знак %s" -msgid "E159: Missing sign number" -msgstr "E159: Пропущено номер надпису" - #, c-format -msgid "E157: Invalid sign ID: %<PRId64>" -msgstr "E157: Неправильний ID надпису: %<PRId64>" +msgid "E157: Invalid sign ID: %<PRId32>" +msgstr "E157: Неправильний ID надпису: %<PRId32>" msgid "E934: Cannot jump to a buffer that does not have a name" msgstr "E934: Не можна перейти до буфера без назви" +msgid "E159: Missing sign number" +msgstr "E159: Пропущено номер надпису" + #, c-format msgid "E160: Unknown sign command: %s" msgstr "E160: Невідома команда надпису: %s" @@ -6171,9 +6296,6 @@ msgstr "E160: Невідома команда надпису: %s" msgid "E156: Missing sign name" msgstr "E156: Пропущено назву надпису" -msgid " (not supported)" -msgstr " (не підтримується)" - msgid "E759: Format error in spell file" msgstr "E759: Помилка формату у файлі орфографії" @@ -6394,8 +6516,8 @@ msgid "E760: No word count in %s" msgstr "E760: Немає кількості слів у %s" #, c-format -msgid "line %6d, word %6ld - %s" -msgstr "рядок %6d, слово %6ld - %s" +msgid "line %6d, word %6d - %s" +msgstr "рядок %6d, слово %6d - %s" #, c-format msgid "Duplicate word in %s line %d: %s" @@ -6417,45 +6539,37 @@ msgstr "Пропущено %d слів(~) із не-ASCII символами у msgid "Reading word file %s..." msgstr "Читається файл слів %s..." -#, c-format -msgid "Conversion failure for word in %s line %ld: %s" -msgstr "Помилка перетворення слова у %s у рядку %ld: %s" +msgid "Conversion failure for word in %s line %" +msgstr "Помилка перетворення слова у %s рядок %" -#, c-format -msgid "Duplicate /encoding= line ignored in %s line %ld: %s" -msgstr "Повторення рядка /encoding= проігноровано у %s у рядку %ld: %s" +msgid "Duplicate /encoding= line ignored in %s line %" +msgstr "Повторення рядка /encoding= проігноровано у %s рядок %" -#, c-format -msgid "/encoding= line after word ignored in %s line %ld: %s" -msgstr "Рядок /encoding= після слова проігноровано у %s у рядку %ld: %s" +msgid "/encoding= line after word ignored in %s line %" +msgstr "Рядок /encoding= після слова проігноровано у %s рядок %" -#, c-format -msgid "Duplicate /regions= line ignored in %s line %ld: %s" -msgstr "Повторення рядка /regions= проігноровано у %s у рядку %ld: %s" +msgid "Duplicate /regions= line ignored in %s line %" +msgstr "Повторення рядка /regions= проігноровано у %s рядок %" -#, c-format -msgid "Too many regions in %s line %ld: %s" -msgstr "Забагато регіонів у %s у рядку %ld: %s" +msgid "Too many regions in %s line %" +msgstr "Забагато регіонів у %s рядок %" -#, c-format -msgid "/ line ignored in %s line %ld: %s" -msgstr "Рядок / проігноровано у %s у рядку %ld: %s" +msgid "/ line ignored in %s line %" +msgstr "Рядок / проігноровано у %s рядок %" -#, c-format -msgid "Invalid region nr in %s line %ld: %s" -msgstr "Некоректний номер регіону у %s у рядку %ld: %s" +msgid "Invalid region nr in %s line %" +msgstr "Некоректний номер регіону у %s рядок %" -#, c-format -msgid "Unrecognized flags in %s line %ld: %s" -msgstr "Нерозпізнані прапорці у %s у рядку %ld: %s" +msgid "Unrecognized flags in %s line %" +msgstr "Нерозпізнані прапорці у %s рядок %" #, c-format msgid "Ignored %d words with non-ASCII characters" msgstr "Проігноровано %d слів із не-ASCII символами" #, c-format -msgid "Compressed %s of %ld nodes; %ld (%ld%%) remaining" -msgstr "Стиснено %s з %ld вузлів; залишилося %ld (%ld%%)" +msgid "Compressed %s of %d nodes; %d (%ld%%) remaining" +msgstr "Стиснено %s з %d вузлів; залишилося %d (%ld%%)" msgid "Reading back spell file..." msgstr "Перечитується файл орфографії..." @@ -6540,32 +6654,32 @@ msgid "[Preview]" msgstr "[Перегляд]" #, c-format -msgid "E1400: Cannot mix positional and non-positional arguments: %s" -msgstr "E1400: Неможливо змішувати позиційні й непозиційні аргументи: %s" +msgid "E1500: Cannot mix positional and non-positional arguments: %s" +msgstr "E1500: Неможливо змішувати позиційні й непозиційні аргументи: %s" #, c-format -msgid "E1401: format argument %d unused in $-style format: %s" -msgstr "E1401: аргумент %d формату не використано у $-форматуванні: %s" +msgid "E1501: format argument %d unused in $-style format: %s" +msgstr "E1501: аргумент %d формату не використано у $-форматуванні: %s" #, c-format msgid "" -"E1402: Positional argument %d used as field width reused as different type: " +"E1502: Positional argument %d used as field width reused as different type: " "%s/%s" msgstr "" -"E1402: Позиційний аргумент %d, що задає ширину поля, вжито як інший тип: %s/" +"E1502: Позиційний аргумент %d, що задає ширину поля, вжито як інший тип: %s/" "%s" #, c-format -msgid "E1403: Positional argument %d out of bounds: %s" -msgstr "E1403: Позиційний аргумент %d поза межами: %s" +msgid "E1503: Positional argument %d out of bounds: %s" +msgstr "E1503: Позиційний аргумент %d поза межами: %s" #, c-format -msgid "E1404: Positional argument %d type used inconsistently: %s/%s" -msgstr "E1404: Тип позиційного аргументу %d вжито нерегулярно: %s/%s" +msgid "E1504: Positional argument %d type used inconsistently: %s/%s" +msgstr "E1504: Тип позиційного аргументу %d вжито нерегулярно: %s/%s" #, c-format -msgid "E1405: Invalid format specifier: %s" -msgstr "E1405: Некоректний специфікатор формату: %s" +msgid "E1505: Invalid format specifier: %s" +msgstr "E1505: Некоректний специфікатор формату: %s" msgid "unknown" msgstr "невідомо" diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index f009722357..e34d6fd97f 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -8,35 +8,47 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/ascii_defs.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/menu.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" +#include "nvim/optionstr.h" +#include "nvim/plines.h" #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/state_defs.h" #include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" +#include "nvim/ui_defs.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/winfloat.h" static pumitem_T *pum_array = NULL; // items of displayed pum static int pum_size; // nr of items in "pum_array" @@ -53,7 +65,8 @@ static bool pum_rl; // true when popupmenu is drawn 'rightleft' static int pum_anchor_grid; // grid where position is defined static int pum_row; // top row of pum -static int pum_col; // left column of pum +static int pum_col; // left column of pum, right column if 'rightleft' +static int pum_left_col; // left column of pum, before padding or scrollbar static bool pum_above; // pum is drawn above cursor line static bool pum_is_visible = false; @@ -73,21 +86,20 @@ static void pum_compute_size(void) pum_kind_width = 0; pum_extra_width = 0; for (int i = 0; i < pum_size; i++) { - int w; if (pum_array[i].pum_text != NULL) { - w = vim_strsize(pum_array[i].pum_text); + int w = vim_strsize(pum_array[i].pum_text); if (pum_base_width < w) { pum_base_width = w; } } if (pum_array[i].pum_kind != NULL) { - w = vim_strsize(pum_array[i].pum_kind) + 1; + int w = vim_strsize(pum_array[i].pum_kind) + 1; if (pum_kind_width < w) { pum_kind_width = w; } } if (pum_array[i].pum_extra != NULL) { - w = vim_strsize(pum_array[i].pum_extra) + 1; + int w = vim_strsize(pum_array[i].pum_extra) + 1; if (pum_extra_width < w) { pum_extra_width = w; } @@ -460,14 +472,14 @@ void pum_redraw(void) grid_assign_handle(&pum_grid); - bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col - col_off, + pum_left_col = pum_col - col_off; + bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_left_col, pum_height, grid_width, false, true); bool invalid_grid = moved || pum_invalid; pum_invalid = false; must_redraw_pum = false; - if (!pum_grid.chars - || pum_grid.rows != pum_height || pum_grid.cols != grid_width) { + if (!pum_grid.chars || pum_grid.rows != pum_height || pum_grid.cols != grid_width) { grid_alloc(&pum_grid, pum_height, grid_width, !invalid_grid, false); ui_call_grid_resize(pum_grid.handle, pum_grid.cols, pum_grid.rows); } else if (invalid_grid) { @@ -476,9 +488,8 @@ void pum_redraw(void) if (ui_has(kUIMultigrid)) { const char *anchor = pum_above ? "SW" : "NW"; int row_off = pum_above ? -pum_height : 0; - ui_call_win_float_pos(pum_grid.handle, -1, cstr_as_string((char *)anchor), - pum_anchor_grid, pum_row - row_off, pum_col - col_off, - false, pum_grid.zindex); + ui_call_win_float_pos(pum_grid.handle, -1, cstr_as_string(anchor), pum_anchor_grid, + pum_row - row_off, pum_left_col, false, pum_grid.zindex); } // Never display more than we have @@ -625,19 +636,19 @@ void pum_redraw(void) } if (pum_rl) { - grid_line_fill(col_off - pum_base_width - n + 1, grid_col + 1, ' ', attr); + grid_line_fill(col_off - pum_base_width - n + 1, grid_col + 1, schar_from_ascii(' '), attr); grid_col = col_off - pum_base_width - n + 1; } else { - grid_line_fill(grid_col, col_off + pum_base_width + n, ' ', attr); + grid_line_fill(grid_col, col_off + pum_base_width + n, schar_from_ascii(' '), attr); grid_col = col_off + pum_base_width + n; } totwidth = pum_base_width + n; } if (pum_rl) { - grid_line_fill(col_off - pum_width + 1, grid_col + 1, ' ', attr); + grid_line_fill(col_off - pum_width + 1, grid_col + 1, schar_from_ascii(' '), attr); } else { - grid_line_fill(grid_col, col_off + pum_width, ' ', attr); + grid_line_fill(grid_col, col_off + pum_width, schar_from_ascii(' '), attr); } if (pum_scrollbar > 0) { @@ -654,6 +665,142 @@ void pum_redraw(void) } } +/// create a floating preview window for info +/// @return NULL when no enough room to show +static win_T *pum_create_float_preview(bool enter) +{ + WinConfig config = WIN_CONFIG_INIT; + config.relative = kFloatRelativeEditor; + // when pum_above is SW otherwise is NW + config.anchor = pum_above ? kFloatAnchorSouth : 0; + int col = pum_col + pum_width + pum_scrollbar + 1; + // TODO(glepnir): support config align border by using completepopup + // align menu + config.row = pum_row; + int right_extra = Columns - col; + if (right_extra > 0) { + config.width = right_extra; + config.col = col - 1; + } else if (pum_col - 2 > 0) { + config.width = pum_col - 2; + config.col = pum_col - config.width - 1; + } else { + return NULL; + } + config.height = pum_height; + config.style = kWinStyleMinimal; + config.hide = true; + Error err = ERROR_INIT; + win_T *wp = win_new_float(NULL, true, config, &err); + // TODO(glepnir): remove win_enter usage + if (enter) { + win_enter(wp, false); + } + + // create a new buffer + Buffer b = nvim_create_buf(false, true, &err); + if (!b) { + win_free(wp, NULL); + return NULL; + } + buf_T *buf = find_buffer_by_handle(b, &err); + set_string_option_direct_in_buf(buf, kOptBufhidden, "wipe", OPT_LOCAL, 0); + wp->w_float_is_info = true; + wp->w_p_diff = false; + buf->b_p_bl = false; + win_set_buf(wp, buf, true, &err); + return wp; +} + +/// set info text to preview buffer. +static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *max_width) +{ + for (char *p = info; *p != NUL;) { + int text_width = 0; + char *e = vim_strchr(p, '\n'); + if (e == NULL) { + ml_append_buf(buf, (*lnum)++, p, 0, false); + text_width = (int)mb_string2cells(p); + if (text_width > *max_width) { + *max_width = text_width; + } + break; + } + *e = NUL; + ml_append_buf(buf, (*lnum)++, p, (int)(e - p + 1), false); + text_width = (int)mb_string2cells(p); + if (text_width > *max_width) { + *max_width = text_width; + } + *e = '\n'; + p = e + 1; + } + // delete the empty last line + ml_delete_buf(buf, buf->b_ml.ml_line_count, false); +} + +/// adjust floating preview window width and height +static void pum_adjust_float_position(win_T *wp, int height, int width) +{ + // when floating window in right and right no enough room to show + // but left has enough room, adjust floating window to left. + if (wp->w_config.width < width && wp->w_config.col > pum_col) { + if ((pum_col - 2) > width) { + wp->w_config.width = width; + wp->w_config.col = pum_col - width - 1; + } + } + wp->w_config.width = MIN(wp->w_config.width, width); + wp->w_config.height = MIN(Rows, height); + wp->w_config.hide = false; + win_config_float(wp, wp->w_config); +} + +/// used in nvim_complete_set +win_T *pum_set_info(int pum_idx, char *info) +{ + if (!pum_is_visible || pum_idx < 0 || pum_idx > pum_size) { + return NULL; + } + pum_array[pum_idx].pum_info = xstrdup(info); + compl_set_info(pum_idx); + bool use_float = strstr(p_cot, "popup") != NULL ? true : false; + if (pum_idx != pum_selected || !use_float) { + return NULL; + } + + block_autocmds(); + RedrawingDisabled++; + no_u_sync++; + win_T *wp = win_float_find_preview(); + if (wp == NULL) { + wp = pum_create_float_preview(false); + // no enough room to show + if (!wp) { + return NULL; + } + } else { + // clean exist buffer + while (!buf_is_empty(wp->w_buffer)) { + ml_delete_buf(wp->w_buffer, 1, false); + } + } + no_u_sync--; + RedrawingDisabled--; + + linenr_T lnum = 0; + int max_info_width = 0; + pum_preview_set_text(wp->w_buffer, info, &lnum, &max_info_width); + redraw_later(wp, UPD_SOME_VALID); + + if (wp->w_p_wrap) { + lnum += plines_win(wp, lnum, true); + } + pum_adjust_float_position(wp, lnum, max_info_width); + unblock_autocmds(); + return wp; +} + /// Set the index of the currently selected item. The menu will scroll when /// necessary. When "n" is out of range don't scroll. /// This may be repeated when the preview window is used: @@ -668,8 +815,9 @@ void pum_redraw(void) /// menu must be recomputed. static bool pum_set_selected(int n, int repeat) { - int resized = false; + bool resized = false; int context = pum_height / 2; + int prev_selected = pum_selected; pum_selected = n; @@ -718,6 +866,10 @@ static bool pum_set_selected(int n, int repeat) pum_first = pum_selected + context - pum_height + 1; } } + // adjust for the number of lines displayed + if (pum_first > pum_size - pum_height) { + pum_first = pum_size - pum_height; + } // Show extra info in the preview window if there is something and // 'completeopt' contains "preview". @@ -730,6 +882,11 @@ static bool pum_set_selected(int n, int repeat) && (vim_strchr(p_cot, 'p') != NULL)) { win_T *curwin_save = curwin; tabpage_T *curtab_save = curtab; + bool use_float = strstr(p_cot, "popup") != NULL ? true : false; + + if (use_float) { + block_autocmds(); + } // Open a preview window. 3 lines by default. Prefer // 'previewheight' if set and smaller. @@ -742,12 +899,26 @@ static bool pum_set_selected(int n, int repeat) // Prevent undo sync here, if an autocommand syncs undo weird // things can happen to the undo tree. no_u_sync++; - resized = prepare_tagpreview(false); + + if (!use_float) { + resized = prepare_tagpreview(false); + } else { + win_T *wp = win_float_find_preview(); + if (wp) { + win_enter(wp, false); + } else { + wp = pum_create_float_preview(true); + if (wp) { + resized = true; + } + } + } + no_u_sync--; RedrawingDisabled--; g_do_tagpreview = 0; - if (curwin->w_p_pvw) { + if (curwin->w_p_pvw || curwin->w_float_is_info) { int res = OK; if (!resized && (curbuf->b_nwindows == 1) @@ -767,32 +938,21 @@ static bool pum_set_selected(int n, int repeat) if (res == OK) { // Edit a new, empty buffer. Set options for a "wipeout" // buffer. - set_option_value_give_err("swf", BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value_give_err("bl", BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value_give_err("bt", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); - set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL); - set_option_value_give_err("diff", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err(kOptBuflisted, BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); + set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL); + set_option_value_give_err(kOptDiff, BOOLEAN_OPTVAL(false), OPT_LOCAL); } } if (res == OK) { linenr_T lnum = 0; - - for (char *p = pum_array[pum_selected].pum_info; *p != NUL;) { - char *e = vim_strchr(p, '\n'); - if (e == NULL) { - ml_append(lnum++, p, 0, false); - break; - } - *e = NUL; - ml_append(lnum++, p, (int)(e - p + 1), false); - *e = '\n'; - p = e + 1; - } - + int max_info_width = 0; + pum_preview_set_text(curbuf, pum_array[pum_selected].pum_info, &lnum, &max_info_width); // Increase the height of the preview window to show the // text, but no more than 'previewheight' lines. - if (repeat == 0) { + if (repeat == 0 && !use_float) { if (lnum > p_pvh) { lnum = (linenr_T)p_pvh; } @@ -805,9 +965,22 @@ static bool pum_set_selected(int n, int repeat) curbuf->b_changed = false; curbuf->b_p_ma = false; + if (pum_selected != prev_selected) { + curwin->w_topline = 1; + } else if (curwin->w_topline > curbuf->b_ml.ml_line_count) { + curwin->w_topline = curbuf->b_ml.ml_line_count; + } curwin->w_cursor.lnum = 1; curwin->w_cursor.col = 0; + if (use_float) { + if (curwin->w_p_wrap) { + lnum += plines_win(curwin, lnum, true); + } + // adjust floating window by actually height and max info text width + pum_adjust_float_position(curwin, lnum, max_info_width); + } + if ((curwin != curwin_save && win_valid(curwin_save)) || (curtab != curtab_save && valid_tabpage(curtab_save))) { if (curtab != curtab_save && valid_tabpage(curtab_save)) { @@ -829,7 +1002,7 @@ static bool pum_set_selected(int n, int repeat) // update the view on the buffer. Only go back to // the window when needed, otherwise it will always be // redrawn. - if (resized) { + if (resized && win_valid(curwin_save)) { no_u_sync++; win_enter(curwin_save, true); no_u_sync--; @@ -858,6 +1031,10 @@ static bool pum_set_selected(int n, int repeat) } } } + + if (use_float) { + unblock_autocmds(); + } } } @@ -892,6 +1069,10 @@ void pum_check_clear(void) } pum_is_drawn = false; pum_external = false; + win_T *wp = win_float_find_preview(); + if (wp != NULL) { + win_close(wp, false, false); + } } } @@ -964,13 +1145,13 @@ void pum_set_event_info(dict_T *dict) r = (double)pum_row; c = (double)pum_col; } - (void)tv_dict_add_float(dict, S_LEN("height"), h); - (void)tv_dict_add_float(dict, S_LEN("width"), w); - (void)tv_dict_add_float(dict, S_LEN("row"), r); - (void)tv_dict_add_float(dict, S_LEN("col"), c); - (void)tv_dict_add_nr(dict, S_LEN("size"), pum_size); - (void)tv_dict_add_bool(dict, S_LEN("scrollbar"), - pum_scrollbar ? kBoolVarTrue : kBoolVarFalse); + tv_dict_add_float(dict, S_LEN("height"), h); + tv_dict_add_float(dict, S_LEN("width"), w); + tv_dict_add_float(dict, S_LEN("row"), r); + tv_dict_add_float(dict, S_LEN("col"), c); + tv_dict_add_nr(dict, S_LEN("size"), pum_size); + tv_dict_add_bool(dict, S_LEN("scrollbar"), + pum_scrollbar ? kBoolVarTrue : kBoolVarFalse); } static void pum_position_at_mouse(int min_width) @@ -986,7 +1167,14 @@ static void pum_position_at_mouse(int min_width) max_col = MAX(Columns - wp->w_wincol, wp->w_grid.cols); } } - pum_anchor_grid = mouse_grid; + if (pum_grid.handle != 0 && mouse_grid == pum_grid.handle) { + // Repositioning the menu by right-clicking on itself + mouse_grid = pum_anchor_grid; + mouse_row += pum_row; + mouse_col += pum_left_col; + } else { + pum_anchor_grid = mouse_grid; + } if (max_row - mouse_row > pum_size) { // Enough space below the mouse row. pum_above = false; diff --git a/src/nvim/popupmenu.h b/src/nvim/popupmenu.h index 24a3f8713a..20a342b841 100644 --- a/src/nvim/popupmenu.h +++ b/src/nvim/popupmenu.h @@ -6,6 +6,7 @@ #include "nvim/grid_defs.h" #include "nvim/macros_defs.h" #include "nvim/menu_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep /// Used for popup menu items. typedef struct { diff --git a/src/nvim/pos_defs.h b/src/nvim/pos_defs.h index 98a1762a5c..a9816c6a66 100644 --- a/src/nvim/pos_defs.h +++ b/src/nvim/pos_defs.h @@ -12,19 +12,15 @@ typedef int colnr_T; /// Format used to print values which have colnr_T type #define PRIdCOLNR "d" -/// Maximal (invalid) line number -enum { MAXLNUM = 0x7fffffff, }; +enum { MAXLNUM = 0x7fffffff, }; ///< Maximal (invalid) line number -/// Maximal column number -/// MAXCOL used to be INT_MAX, but with 64 bit ints that results in running -/// out of memory when trying to allocate a very long line. -enum { MAXCOL = 0x7fffffff, }; +// MAXCOL used to be INT_MAX, but with 64 bit ints that results in running +// out of memory when trying to allocate a very long line. +enum { MAXCOL = 0x7fffffff, }; ///< Maximal column number -/// Minimum line number -enum { MINLNUM = 1, }; +enum { MINLNUM = 1, }; ///< Minimum line number -/// Minimum column number -enum { MINCOL = 1, }; +enum { MINCOL = 1, }; ///< Minimum column number /// position in file or buffer typedef struct { diff --git a/src/nvim/profile.c b/src/nvim/profile.c index 53ff57dacb..b88b08d3f0 100644 --- a/src/nvim/profile.c +++ b/src/nvim/profile.c @@ -15,11 +15,11 @@ #include "nvim/eval/userfunc.h" #include "nvim/ex_cmds_defs.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/keycodes.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -36,7 +36,7 @@ #endif /// Struct used in sn_prl_ga for every line of a script. -typedef struct sn_prl_S { +typedef struct { int snp_count; ///< nr of times line was executed proftime_T sn_prl_total; ///< time spent in a line + children proftime_T sn_prl_self; ///< time spent in a line itself @@ -45,6 +45,7 @@ typedef struct sn_prl_S { #define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) static proftime_T prof_wait_time; +static char *startuptime_buf = NULL; // --startuptime buffer /// Gets the current time. /// @@ -255,7 +256,7 @@ void profile_reset(void) size_t todo = functbl->ht_used; hashitem_T *hi = functbl->ht_array; - for (; todo > (size_t)0; hi++) { + for (; todo > 0; hi++) { if (!HASHITEM_EMPTY(hi)) { todo--; ufunc_T *uf = HI2UF(hi); @@ -798,7 +799,7 @@ void script_line_start(void) if (si->sn_prof_on && SOURCING_LNUM >= 1) { // Grow the array before starting the timer, so that the time spent // here isn't counted. - (void)ga_grow(&si->sn_prl_ga, SOURCING_LNUM - si->sn_prl_ga.ga_len); + ga_grow(&si->sn_prl_ga, SOURCING_LNUM - si->sn_prl_ga.ga_len); si->sn_prl_idx = SOURCING_LNUM - 1; while (si->sn_prl_ga.ga_len <= si->sn_prl_idx && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) { @@ -907,7 +908,7 @@ void time_start(const char *message) // initialize the global variables g_prev_time = g_start_time = profile_start(); - fprintf(time_fd, "\n\ntimes in msec\n"); + fprintf(time_fd, "\ntimes in msec\n"); fprintf(time_fd, " clock self+sourced self: sourced script\n"); fprintf(time_fd, " clock elapsed: other lines\n\n"); @@ -944,3 +945,47 @@ void time_msg(const char *mesg, const proftime_T *start) g_prev_time = now; fprintf(time_fd, ": %s\n", mesg); } + +/// Initializes the `time_fd` stream for the --startuptime report. +/// +/// @param fname startuptime report file path +/// @param process_name name of the current Nvim process to write in the report. +void time_init(const char *fname, const char *process_name) +{ + const size_t bufsize = 8192; // Big enough for the entire --startuptime report. + time_fd = fopen(fname, "a"); + if (time_fd == NULL) { + semsg(_(e_notopen), fname); + return; + } + startuptime_buf = xmalloc(sizeof(char) * (bufsize + 1)); + // The startuptime file is (potentially) written by multiple Nvim processes concurrently. So each + // report is buffered, and flushed to disk (`time_finish`) once after startup. `_IOFBF` mode + // ensures the buffer is not auto-flushed ("controlled buffering"). + int r = setvbuf(time_fd, startuptime_buf, _IOFBF, bufsize + 1); + if (r != 0) { + XFREE_CLEAR(startuptime_buf); + fclose(time_fd); + time_fd = NULL; + ELOG("time_init: setvbuf failed: %d %s", r, uv_err_name(r)); + semsg("time_init: setvbuf failed: %d %s", r, uv_err_name(r)); + return; + } + fprintf(time_fd, "--- Startup times for process: %s ---\n", process_name); +} + +/// Flushes the startuptimes to disk for the current process +void time_finish(void) +{ + if (time_fd == NULL) { + return; + } + assert(startuptime_buf != NULL); + TIME_MSG("--- NVIM STARTED ---\n"); + + // flush buffer to disk + fclose(time_fd); + time_fd = NULL; + + XFREE_CLEAR(startuptime_buf); +} diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 4e20eb8925..651ebc9f93 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -13,7 +13,9 @@ #include "nvim/arglist.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" @@ -26,20 +28,24 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/ex_eval_defs.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/help.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" @@ -49,16 +55,20 @@ #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -107,7 +117,7 @@ typedef enum { /// Usually the list contains one or more entries. But an empty list can be /// created using setqflist()/setloclist() with a title and/or user context /// information and entries can be added later using setqflist()/setloclist(). -typedef struct qf_list_S { +typedef struct { unsigned qf_id; ///< Unique identifier for this list qfltype_T qfl_type; qfline_T *qf_start; ///< pointer to the first error @@ -232,7 +242,7 @@ typedef struct { } qffields_T; /// :vimgrep command arguments -typedef struct vgr_args_S { +typedef struct { int tomatch; ///< maximum number of matches to find char *spat; ///< search pattern int flags; ///< search modifier @@ -272,7 +282,7 @@ enum { QF_WINHEIGHT = 10, }; ///< default height for quickfix window // Macro to loop through all the items in a quickfix list // Quickfix item index starts from 1, so i below starts at 1 #define FOR_ALL_QFL_ITEMS(qfl, qfp, i) \ - for ((i) = 1, (qfp) = (qfl)->qf_start; /* NOLINT(readability/braces) */ \ + for ((i) = 1, (qfp) = (qfl)->qf_start; \ !got_int && (i) <= (qfl)->qf_count && (qfp) != NULL; \ (i)++, (qfp) = (qfp)->qf_next) @@ -1056,7 +1066,9 @@ static int qf_setup_state(qfstate_T *pstate, char *restrict enc, const char *res } if (efile != NULL - && (pstate->fd = os_fopen(efile, "r")) == NULL) { + && (pstate->fd = (strequal(efile, "-") + ? fdopen(os_open_stdin_fd(), "r") + : os_fopen(efile, "r"))) == NULL) { semsg(_(e_openerrf), efile); return FAIL; } @@ -1603,7 +1615,7 @@ static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qf // Always ignore case when looking for a matching error. regmatch.rm_ic = true; regmatch.regprog = fmt_ptr->prog; - int r = vim_regexec(®match, linebuf, 0); + bool r = vim_regexec(®match, linebuf, 0); fmt_ptr->prog = regmatch.regprog; int status = QF_FAIL; if (r) { @@ -1970,7 +1982,7 @@ static qf_info_T *ll_get_or_alloc_list(win_T *wp) /// For a location list command, returns the stack for the current window. If /// the location list is not found, then returns NULL and prints an error /// message if 'print_emsg' is true. -static qf_info_T *qf_cmd_get_stack(exarg_T *eap, int print_emsg) +static qf_info_T *qf_cmd_get_stack(exarg_T *eap, bool print_emsg) { qf_info_T *qi = &ql_info; @@ -2512,7 +2524,7 @@ static void win_set_loclist(win_T *wp, qf_info_T *qi) /// Find a help window or open one. If 'newwin' is true, then open a new help /// window. -static int jump_to_help_window(qf_info_T *qi, bool newwin, int *opened_window) +static int jump_to_help_window(qf_info_T *qi, bool newwin, bool *opened_window) { win_T *wp = NULL; @@ -2715,7 +2727,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum) // window, jump to it. Otherwise open a new window to display the file. If // 'newwin' is true, then always open a new window. This is called from either // a quickfix or a location list window. -static int qf_jump_to_usable_window(int qf_fnum, bool newwin, int *opened_window) +static int qf_jump_to_usable_window(int qf_fnum, bool newwin, bool *opened_window) { win_T *usable_wp = NULL; bool usable_win = false; @@ -2770,7 +2782,7 @@ static int qf_jump_to_usable_window(int qf_fnum, bool newwin, int *opened_window /// QF_ABORT if the quickfix/location list was freed by an autocmd /// when opening the buffer. static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int prev_winid, - int *opened_window) + bool *opened_window) { qf_list_T *qfl = qf_get_curlist(qi); int old_changetick = qfl->qf_changedtick; @@ -2905,7 +2917,7 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf /// FAIL if not able to jump/open a window. /// NOTDONE if a file is not associated with the entry. /// QF_ABORT if the quickfix/location list was modified by an autocmd. -static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int *opened_window) +static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, bool *opened_window) { qf_list_T *qfl = qf_get_curlist(qi); int old_changetick = qfl->qf_changedtick; @@ -2964,7 +2976,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int /// QF_ABORT if the quickfix/location list is freed by an autocmd when opening /// the file. static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int forceit, - int prev_winid, int *opened_window, int openfold, int print_message) + int prev_winid, bool *opened_window, int openfold, bool print_message) { // If there is a file name, read the wanted file if needed, and check // autowrite etc. @@ -3051,7 +3063,7 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo int prev_winid = curwin->handle; - int opened_window = false; + bool opened_window = false; int retval = qf_jump_open_window(qi, qf_ptr, newwin, &opened_window); if (retval == FAIL) { goto failed; @@ -3646,12 +3658,12 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp static void qf_set_cwindow_options(void) { // switch off 'swapfile' - set_option_value_give_err("swf", BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value_give_err("bt", STATIC_CSTR_AS_OPTVAL("quickfix"), OPT_LOCAL); - set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); + set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("quickfix"), OPT_LOCAL); + set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); RESET_BINDING(curwin); curwin->w_p_diff = false; - set_option_value_give_err("fdm", STATIC_CSTR_AS_OPTVAL("manual"), OPT_LOCAL); + set_option_value_give_err(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("manual"), OPT_LOCAL); } // Open a new quickfix or location list window, load the quickfix buffer and @@ -3988,7 +4000,7 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) buf_inc_changedtick(buf); if (old_last == NULL) { - (void)qf_win_pos_update(qi, 0); + qf_win_pos_update(qi, 0); // restore curwin/curbuf and a few other things aucmd_restbuf(&aco); @@ -4131,6 +4143,12 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q } // delete all existing lines + // + // Note: we cannot store undo information, because + // qf buffer is usually not allowed to be modified. + // + // So we need to clean up undo information + // otherwise autocommands may invalidate the undo stack while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0) { // If deletion fails, this loop may run forever, so // signal error and return. @@ -4139,6 +4157,9 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q return; } } + + // Remove all undo information + u_clearallandblockfree(curbuf); } // Check if there is anything to display @@ -4199,7 +4220,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q } if (old_last == NULL) { // Delete the empty line which is now at the end - (void)ml_delete(lnum + 1, false); + ml_delete(lnum + 1, false); } qfga_clear(); @@ -4213,7 +4234,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // resembles reading a file into a buffer, it's more logical when using // autocommands. curbuf->b_ro_locked++; - set_option_value_give_err("ft", STATIC_CSTR_AS_OPTVAL("qf"), OPT_LOCAL); + set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OPTVAL("qf"), OPT_LOCAL); curbuf->b_p_ma = false; keep_filetype = true; // don't detect 'filetype' @@ -4583,7 +4604,7 @@ int qf_get_cur_valid_idx(exarg_T *eap) /// Used by :cdo, :ldo, :cfdo and :lfdo commands. /// For :cdo and :ldo, returns the 'n'th valid error entry. /// For :cfdo and :lfdo, returns the 'n'th valid file entry. -static size_t qf_get_nth_valid_entry(qf_list_T *qfl, size_t n, int fdo) +static size_t qf_get_nth_valid_entry(qf_list_T *qfl, size_t n, bool fdo) FUNC_ATTR_NONNULL_ALL { // Check if the list has valid errors. @@ -5091,7 +5112,7 @@ void ex_cfile(exarg_T *eap) } } if (*eap->arg != NUL) { - set_string_option_direct("ef", -1, eap->arg, OPT_FREE, 0); + set_string_option_direct(kOptErrorfile, eap->arg, 0, 0); } char *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; @@ -5690,7 +5711,7 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin return NULL; } - int failed = true; + bool failed = true; bufref_T newbufref; set_bufref(&newbufref, newbuf); @@ -5706,7 +5727,7 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin aucmd_prepbuf(&aco, newbuf); // Need to set the filename for autocommands. - (void)setfname(curbuf, fname, NULL, false); + setfname(curbuf, fname, NULL, false); // Create swap file now to avoid the ATTENTION message. check_need_swap(true); @@ -5958,7 +5979,7 @@ static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict) if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat, true, 0, 0, NULL, NULL) > 0) { - (void)get_errorlist(qi, NULL, 0, 0, l); + get_errorlist(qi, NULL, 0, 0, l); qf_free(&qi->qf_lists[0]); } xfree(qi); @@ -6907,7 +6928,7 @@ static int cbuffer_process_args(exarg_T *eap, buf_T **bufp, linenr_T *line1, lin } if (buf->b_ml.ml_mfp == NULL) { - emsg(_("E681: Buffer is not loaded")); + emsg(_(e_buffer_is_not_loaded)); return FAIL; } @@ -7221,7 +7242,7 @@ void ex_helpgrep(exarg_T *eap) bool updated = false; // Make 'cpoptions' empty, the 'l' flag should not be used here. char *const save_cpo = p_cpo; - const bool save_cpo_allocated = is_option_allocated("cpo"); + const bool save_cpo_allocated = (get_option(kOptCpoptions)->flags & P_ALLOCED); p_cpo = empty_string_option; bool new_qi = false; @@ -7259,7 +7280,7 @@ void ex_helpgrep(exarg_T *eap) // Darn, some plugin changed the value. If it's still empty it was // changed and restored, need to restore in the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); + set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0); } if (save_cpo_allocated) { free_string_option(save_cpo); @@ -7319,12 +7340,12 @@ void free_quickfix(void) } #endif -static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) +static void get_qf_loc_list(bool is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { tv_list_alloc_ret(rettv, kListLenMayKnow); if (is_qf || wp != NULL) { - (void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list); + get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list); } } else { tv_dict_alloc_ret(rettv); diff --git a/src/nvim/quickfix.h b/src/nvim/quickfix.h index 9c49564d57..941aa2f991 100644 --- a/src/nvim/quickfix.h +++ b/src/nvim/quickfix.h @@ -1,5 +1,6 @@ #pragma once +#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/option_defs.h" // IWYU pragma: keep diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c index f74f68adb6..ef286b3e22 100644 --- a/src/nvim/rbuffer.c +++ b/src/nvim/rbuffer.c @@ -3,13 +3,12 @@ #include <stddef.h> #include <string.h> -#include "nvim/func_attr.h" #include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/rbuffer.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "rbuffer.c.generated.h" // IWYU pragma: export +# include "rbuffer.c.generated.h" #endif /// Creates a new `RBuffer` instance. @@ -30,27 +29,12 @@ RBuffer *rbuffer_new(size_t capacity) return rv; } -void rbuffer_free(RBuffer *buf) +void rbuffer_free(RBuffer *buf) FUNC_ATTR_NONNULL_ALL { xfree(buf->temp); xfree(buf); } -size_t rbuffer_size(RBuffer *buf) FUNC_ATTR_NONNULL_ALL -{ - return buf->size; -} - -size_t rbuffer_capacity(RBuffer *buf) FUNC_ATTR_NONNULL_ALL -{ - return (size_t)(buf->end_ptr - buf->start_ptr); -} - -size_t rbuffer_space(RBuffer *buf) FUNC_ATTR_NONNULL_ALL -{ - return rbuffer_capacity(buf) - buf->size; -} - /// Return a pointer to a raw buffer containing the first empty slot available /// for writing. The second argument is a pointer to the maximum number of /// bytes that could be written. @@ -214,7 +198,7 @@ size_t rbuffer_read(RBuffer *buf, char *dst, size_t dst_size) } char *rbuffer_get(RBuffer *buf, size_t index) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { assert(index < buf->size); char *rptr = buf->read_ptr + index; @@ -229,7 +213,7 @@ int rbuffer_cmp(RBuffer *buf, const char *str, size_t count) { assert(count <= buf->size); size_t rcnt; - (void)rbuffer_read_ptr(buf, &rcnt); + rbuffer_read_ptr(buf, &rcnt); size_t n = MIN(count, rcnt); int rv = memcmp(str, buf->read_ptr, n); count -= n; diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h index 55e9849d3d..942e1f2365 100644 --- a/src/nvim/rbuffer.h +++ b/src/nvim/rbuffer.h @@ -16,6 +16,8 @@ #include <stddef.h> #include <stdint.h> +#include "nvim/rbuffer_defs.h" // IWYU pragma: keep + // Macros that simplify working with the read/write pointers directly by hiding // ring buffer wrap logic. Some examples: // @@ -36,50 +38,34 @@ // Note that the rbuffer_{produced,consumed} calls are necessary or these macros // create infinite loops #define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \ - for (size_t rcnt = 0, _r = 1; _r; _r = 0) /* NOLINT(readability/braces) */ \ - for (char *rptr = rbuffer_read_ptr(buf, &rcnt); /* NOLINT(readability/braces) */ \ + for (size_t rcnt = 0, _r = 1; _r; _r = 0) \ + for (char *rptr = rbuffer_read_ptr(buf, &rcnt); \ buf->size; \ rptr = rbuffer_read_ptr(buf, &rcnt)) #define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \ - for (size_t wcnt = 0, _r = 1; _r; _r = 0) /* NOLINT(readability/braces) */ \ - for (char *wptr = rbuffer_write_ptr(buf, &wcnt); /* NOLINT(readability/braces) */ \ + for (size_t wcnt = 0, _r = 1; _r; _r = 0) \ + for (char *wptr = rbuffer_write_ptr(buf, &wcnt); \ rbuffer_space(buf); \ wptr = rbuffer_write_ptr(buf, &wcnt)) // Iteration #define RBUFFER_EACH(buf, c, i) \ - for (size_t i = 0; /* NOLINT(readability/braces) */ \ + for (size_t i = 0; \ i < buf->size; \ i = buf->size) \ - for (char c = 0; /* NOLINT(readability/braces) */ \ + for (char c = 0; \ i < buf->size ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ i++) #define RBUFFER_EACH_REVERSE(buf, c, i) \ - for (size_t i = buf->size; /* NOLINT(readability/braces) */ \ + for (size_t i = buf->size; \ i != SIZE_MAX; \ i = SIZE_MAX) \ - for (char c = 0; /* NOLINT(readability/braces) */ \ + for (char c = 0; \ i-- > 0 ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ ) -typedef struct rbuffer RBuffer; -/// Type of function invoked during certain events: -/// - When the RBuffer switches to the full state -/// - When the RBuffer switches to the non-full state -typedef void (*rbuffer_callback)(RBuffer *buf, void *data); - -struct rbuffer { - rbuffer_callback full_cb, nonfull_cb; - void *data; - size_t size; - // helper memory used to by rbuffer_reset if required - char *temp; - char *end_ptr, *read_ptr, *write_ptr; - char start_ptr[]; -}; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "rbuffer.h.generated.h" #endif diff --git a/src/nvim/rbuffer_defs.h b/src/nvim/rbuffer_defs.h new file mode 100644 index 0000000000..51dc349846 --- /dev/null +++ b/src/nvim/rbuffer_defs.h @@ -0,0 +1,45 @@ +#pragma once + +#include <stddef.h> + +#include "nvim/func_attr.h" + +typedef struct rbuffer RBuffer; +/// Type of function invoked during certain events: +/// - When the RBuffer switches to the full state +/// - When the RBuffer switches to the non-full state +typedef void (*rbuffer_callback)(RBuffer *buf, void *data); + +struct rbuffer { + rbuffer_callback full_cb, nonfull_cb; + void *data; + size_t size; + // helper memory used to by rbuffer_reset if required + char *temp; + char *end_ptr, *read_ptr, *write_ptr; + char start_ptr[]; +}; + +static inline size_t rbuffer_size(RBuffer *buf) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; + +static inline size_t rbuffer_size(RBuffer *buf) +{ + return buf->size; +} + +static inline size_t rbuffer_capacity(RBuffer *buf) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; + +static inline size_t rbuffer_capacity(RBuffer *buf) +{ + return (size_t)(buf->end_ptr - buf->start_ptr); +} + +static inline size_t rbuffer_space(RBuffer *buf) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; + +static inline size_t rbuffer_space(RBuffer *buf) +{ + return rbuffer_capacity(buf) - buf->size; +} diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 3536196a3b..86082adbb6 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -13,7 +13,7 @@ #include <stdbool.h> #include <stddef.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" @@ -21,14 +21,16 @@ #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -43,6 +45,106 @@ #include "nvim/types_defs.h" #include "nvim/vim_defs.h" +enum { + /// In the NFA engine: how many braces are allowed. + /// TODO(RE): Use dynamic memory allocation instead of static, like here + NFA_MAX_BRACES = 20, +}; + +enum { + /// In the NFA engine: how many states are allowed. + NFA_MAX_STATES = 100000, + NFA_TOO_EXPENSIVE = -1, +}; + +/// Which regexp engine to use? Needed for vim_regcomp(). +/// Must match with 'regexpengine'. +enum { + AUTOMATIC_ENGINE = 0, + BACKTRACKING_ENGINE = 1, + NFA_ENGINE = 2, +}; + +/// Structure returned by vim_regcomp() to pass on to vim_regexec(). +/// This is the general structure. For the actual matcher, two specific +/// structures are used. See code below. +struct regprog { + regengine_T *engine; + unsigned regflags; + unsigned re_engine; ///< Automatic, backtracking or NFA engine. + unsigned re_flags; ///< Second argument for vim_regcomp(). + bool re_in_use; ///< prog is being executed +}; + +/// Structure used by the back track matcher. +/// These fields are only to be used in regexp.c! +/// See regexp.c for an explanation. +typedef struct { + // These four members implement regprog_T. + regengine_T *engine; + unsigned regflags; + unsigned re_engine; + unsigned re_flags; + bool re_in_use; + + int regstart; + uint8_t reganch; + uint8_t *regmust; + int regmlen; + uint8_t reghasz; + uint8_t program[]; +} bt_regprog_T; + +/// Structure representing a NFA state. +/// An NFA state may have no outgoing edge, when it is a NFA_MATCH state. +typedef struct nfa_state nfa_state_T; +struct nfa_state { + int c; + nfa_state_T *out; + nfa_state_T *out1; + int id; + int lastlist[2]; ///< 0: normal, 1: recursive + int val; +}; + +/// Structure used by the NFA matcher. +typedef struct { + // These four members implement regprog_T. + regengine_T *engine; + unsigned regflags; + unsigned re_engine; + unsigned re_flags; + bool re_in_use; + + nfa_state_T *start; ///< points into state[] + + int reganch; ///< pattern starts with ^ + int regstart; ///< char at start of pattern + uint8_t *match_text; ///< plain text to match with + + int has_zend; ///< pattern contains \ze + int has_backref; ///< pattern contains \1 .. \9 + int reghasz; + char *pattern; + int nsubexp; ///< number of () + int nstate; + nfa_state_T state[]; +} nfa_regprog_T; + +struct regengine { + /// bt_regcomp or nfa_regcomp + regprog_T *(*regcomp)(uint8_t *, int); + /// bt_regfree or nfa_regfree + void (*regfree)(regprog_T *); + /// bt_regexec_nl or nfa_regexec_nl + int (*regexec_nl)(regmatch_T *, uint8_t *, colnr_T, bool); + /// bt_regexec_mult or nfa_regexec_mult + int (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T, proftime_T *, int *); +#ifdef REGEXP_DEBUG + uint8_t *expr; +#endif +}; + // Structure used to save the current input state, when it needs to be // restored after trying a match. Used by reg_save() and reg_restore(). // Also stores the length of "backpos". @@ -1263,6 +1365,10 @@ static bool reg_match_visual(void) top = curbuf->b_visual.vi_end; bot = curbuf->b_visual.vi_start; } + // a substitute command may have removed some lines + if (bot.lnum > curbuf->b_ml.ml_line_count) { + bot.lnum = curbuf->b_ml.ml_line_count; + } mode = curbuf->b_visual.vi_mode; curswant = curbuf->b_visual.vi_curswant; } @@ -5363,9 +5469,9 @@ static bool reg_save_equal(const regsave_T *save) // After a failed match restore the sub-expressions. #define restore_se(savep, posp, pp) { \ - if (REG_MULTI) /* NOLINT(readability/braces) */ \ + if (REG_MULTI) \ *(posp) = (savep)->se_u.pos; \ - else /* NOLINT */ \ + else \ *(pp) = (savep)->se_u.ptr; } // Tentatively set the sub-expression start to the current position (after @@ -5866,8 +5972,8 @@ static bool regmatch(uint8_t *scan, const proftime_T *tm, int *timed_out) #ifdef REGEXP_DEBUG if (scan != NULL && regnarrate) { - os_errmsg((char *)regprop(scan)); - os_errmsg("(\n"); + fprintf(stderr, "%s", (char *)regprop(scan)); + fprintf(stderr, "%s", "(\n"); } #endif @@ -5893,18 +5999,18 @@ static bool regmatch(uint8_t *scan, const proftime_T *tm, int *timed_out) #ifdef REGEXP_DEBUG if (regnarrate) { - os_errmsg((char *)regprop(scan)); - os_errmsg("...\n"); + fprintf(stderr, "%s", (char *)regprop(scan)); + fprintf(stderr, "%s", "...\n"); if (re_extmatch_in != NULL) { int i; - os_errmsg(_("External submatches:\n")); + fprintf(stderr, _("External submatches:\n")); for (i = 0; i < NSUBEXP; i++) { - os_errmsg(" \""); + fprintf(stderr, "%s", " \""); if (re_extmatch_in->matches[i] != NULL) { - os_errmsg((char *)re_extmatch_in->matches[i]); + fprintf(stderr, "%s", (char *)re_extmatch_in->matches[i]); } - os_errmsg("\"\n"); + fprintf(stderr, "%s", "\"\n"); } } } @@ -6326,15 +6432,33 @@ static bool regmatch(uint8_t *scan, const proftime_T *tm, int *timed_out) break; case ANYOF: - case ANYBUT: + case ANYBUT: { + uint8_t *q = OPERAND(scan); + if (c == NUL) { status = RA_NOMATCH; - } else if ((cstrchr((char *)OPERAND(scan), c) == NULL) == (op == ANYOF)) { + } else if ((cstrchr((char *)q, c) == NULL) == (op == ANYOF)) { status = RA_NOMATCH; - } else { - ADVANCE_REGINPUT(); + } else { // Check following combining characters + int len = utfc_ptr2len((char *)q) - utf_ptr2len((char *)q); + + rex.input += utf_ptr2len((char *)rex.input); + q += utf_ptr2len((char *)q); + + if (len == 0) { + break; + } + + for (int i = 0; i < len; i++) { + if (q[i] != rex.input[i]) { + status = RA_NOMATCH; + break; + } + } + rex.input += len; } break; + } case MULTIBYTECODE: { int i, len; @@ -6380,7 +6504,7 @@ static bool regmatch(uint8_t *scan, const proftime_T *tm, int *timed_out) case RE_COMPOSING: // Skip composing characters. while (utf_iscomposing(utf_ptr2char((char *)rex.input))) { - MB_CPTR_ADV(rex.input); + rex.input += utf_ptr2len((char *)rex.input); } break; @@ -8688,9 +8812,9 @@ static void nfa_emit_equi_class(int c) case 0x1eb2: case 0x1eb4: case 0x1eb6: - EMIT2('A') EMIT2(A_grave) EMIT2(A_acute) // NOLINT(whitespace/cast) - EMIT2(A_circumflex) EMIT2(A_virguilla) // NOLINT(whitespace/cast) - EMIT2(A_diaeresis) EMIT2(A_ring) // NOLINT(whitespace/cast) + EMIT2('A') EMIT2(A_grave) EMIT2(A_acute) + EMIT2(A_circumflex) EMIT2(A_virguilla) + EMIT2(A_diaeresis) EMIT2(A_ring) EMIT2(0x100) EMIT2(0x102) EMIT2(0x104) EMIT2(0x1cd) EMIT2(0x1de) EMIT2(0x1e0) EMIT2(0x1fa) EMIT2(0x200) EMIT2(0x202) @@ -8769,8 +8893,8 @@ static void nfa_emit_equi_class(int c) case 0x1ec2: case 0x1ec4: case 0x1ec6: - EMIT2('E') EMIT2(E_grave) EMIT2(E_acute) // NOLINT(whitespace/cast) - EMIT2(E_circumflex) EMIT2(E_diaeresis) // NOLINT(whitespace/cast) + EMIT2('E') EMIT2(E_grave) EMIT2(E_acute) + EMIT2(E_circumflex) EMIT2(E_diaeresis) EMIT2(0x112) EMIT2(0x114) EMIT2(0x116) EMIT2(0x118) EMIT2(0x11a) EMIT2(0x204) EMIT2(0x206) EMIT2(0x228) EMIT2(0x246) @@ -8838,8 +8962,8 @@ static void nfa_emit_equi_class(int c) case 0x1e2e: case 0x1ec8: case 0x1eca: - EMIT2('I') EMIT2(I_grave) EMIT2(I_acute) // NOLINT(whitespace/cast) - EMIT2(I_circumflex) EMIT2(I_diaeresis) // NOLINT(whitespace/cast) + EMIT2('I') EMIT2(I_grave) EMIT2(I_acute) + EMIT2(I_circumflex) EMIT2(I_diaeresis) EMIT2(0x128) EMIT2(0x12a) EMIT2(0x12c) EMIT2(0x12e) EMIT2(0x130) EMIT2(0x197) EMIT2(0x1cf) EMIT2(0x208) EMIT2(0x20a) @@ -8948,9 +9072,9 @@ static void nfa_emit_equi_class(int c) case 0x1ede: case 0x1ee0: case 0x1ee2: - EMIT2('O') EMIT2(O_grave) EMIT2(O_acute) // NOLINT(whitespace/cast) - EMIT2(O_circumflex) EMIT2(O_virguilla) // NOLINT(whitespace/cast) - EMIT2(O_diaeresis) EMIT2(O_slash) // NOLINT(whitespace/cast) + EMIT2('O') EMIT2(O_grave) EMIT2(O_acute) + EMIT2(O_circumflex) EMIT2(O_virguilla) + EMIT2(O_diaeresis) EMIT2(O_slash) EMIT2(0x14c) EMIT2(0x14e) EMIT2(0x150) EMIT2(0x19f) EMIT2(0x1a0) EMIT2(0x1d1) EMIT2(0x1ea) EMIT2(0x1ec) EMIT2(0x1fe) @@ -9065,8 +9189,8 @@ static void nfa_emit_equi_class(int c) case 0x1eec: case 0x1eee: case 0x1ef0: - EMIT2('U') EMIT2(U_grave) EMIT2(U_acute) // NOLINT(whitespace/cast) - EMIT2(U_diaeresis) EMIT2(U_circumflex) // NOLINT(whitespace/cast) + EMIT2('U') EMIT2(U_grave) EMIT2(U_acute) + EMIT2(U_diaeresis) EMIT2(U_circumflex) EMIT2(0x168) EMIT2(0x16a) EMIT2(0x16c) EMIT2(0x16e) EMIT2(0x170) EMIT2(0x172) EMIT2(0x1af) EMIT2(0x1d3) @@ -9169,9 +9293,9 @@ static void nfa_emit_equi_class(int c) case 0x1eb5: case 0x1eb7: case 0x2c65: - EMIT2('a') EMIT2(a_grave) EMIT2(a_acute) // NOLINT(whitespace/cast) - EMIT2(a_circumflex) EMIT2(a_virguilla) // NOLINT(whitespace/cast) - EMIT2(a_diaeresis) EMIT2(a_ring) // NOLINT(whitespace/cast) + EMIT2('a') EMIT2(a_grave) EMIT2(a_acute) + EMIT2(a_circumflex) EMIT2(a_virguilla) + EMIT2(a_diaeresis) EMIT2(a_ring) EMIT2(0x101) EMIT2(0x103) EMIT2(0x105) EMIT2(0x1ce) EMIT2(0x1df) EMIT2(0x1e1) EMIT2(0x1fb) EMIT2(0x201) EMIT2(0x203) @@ -9258,8 +9382,8 @@ static void nfa_emit_equi_class(int c) case 0x1ec3: case 0x1ec5: case 0x1ec7: - EMIT2('e') EMIT2(e_grave) EMIT2(e_acute) // NOLINT(whitespace/cast) - EMIT2(e_circumflex) EMIT2(e_diaeresis) // NOLINT(whitespace/cast) + EMIT2('e') EMIT2(e_grave) EMIT2(e_acute) + EMIT2(e_circumflex) EMIT2(e_diaeresis) EMIT2(0x113) EMIT2(0x115) EMIT2(0x117) EMIT2(0x119) EMIT2(0x11b) EMIT2(0x205) EMIT2(0x207) EMIT2(0x229) @@ -9334,8 +9458,8 @@ static void nfa_emit_equi_class(int c) case 0x1e2f: case 0x1ec9: case 0x1ecb: - EMIT2('i') EMIT2(i_grave) EMIT2(i_acute) // NOLINT(whitespace/cast) - EMIT2(i_circumflex) EMIT2(i_diaeresis) // NOLINT(whitespace/cast) + EMIT2('i') EMIT2(i_grave) EMIT2(i_acute) + EMIT2(i_circumflex) EMIT2(i_diaeresis) EMIT2(0x129) EMIT2(0x12b) EMIT2(0x12d) EMIT2(0x12f) EMIT2(0x1d0) EMIT2(0x209) EMIT2(0x20b) EMIT2(0x268) EMIT2(0x1d96) @@ -9451,9 +9575,9 @@ static void nfa_emit_equi_class(int c) case 0x1edf: case 0x1ee1: case 0x1ee3: - EMIT2('o') EMIT2(o_grave) EMIT2(o_acute) // NOLINT(whitespace/cast) - EMIT2(o_circumflex) EMIT2(o_virguilla) // NOLINT(whitespace/cast) - EMIT2(o_diaeresis) EMIT2(o_slash) // NOLINT(whitespace/cast) + EMIT2('o') EMIT2(o_grave) EMIT2(o_acute) + EMIT2(o_circumflex) EMIT2(o_virguilla) + EMIT2(o_diaeresis) EMIT2(o_slash) EMIT2(0x14d) EMIT2(0x14f) EMIT2(0x151) EMIT2(0x1a1) EMIT2(0x1d2) EMIT2(0x1eb) EMIT2(0x1ed) EMIT2(0x1ff) EMIT2(0x20d) @@ -9582,8 +9706,8 @@ static void nfa_emit_equi_class(int c) case 0x1eed: case 0x1eef: case 0x1ef1: - EMIT2('u') EMIT2(u_grave) EMIT2(u_acute) // NOLINT(whitespace/cast) - EMIT2(u_circumflex) EMIT2(u_diaeresis) // NOLINT(whitespace/cast) + EMIT2('u') EMIT2(u_grave) EMIT2(u_acute) + EMIT2(u_circumflex) EMIT2(u_diaeresis) EMIT2(0x169) EMIT2(0x16b) EMIT2(0x16d) EMIT2(0x16f) EMIT2(0x171) EMIT2(0x173) EMIT2(0x1d6) EMIT2(0x1d8) @@ -9636,7 +9760,7 @@ static void nfa_emit_equi_class(int c) case 0x1ef5: case 0x1ef7: case 0x1ef9: - EMIT2('y') EMIT2(y_acute) EMIT2(y_diaeresis) // NOLINT(whitespace/cast) + EMIT2('y') EMIT2(y_acute) EMIT2(y_diaeresis) EMIT2(0x177) EMIT2(0x1b4) EMIT2(0x233) EMIT2(0x24f) EMIT2(0x1e8f) EMIT2(0x1e99) EMIT2(0x1ef3) EMIT2(0x1ef5) EMIT2(0x1ef7) EMIT2(0x1ef9) @@ -9841,7 +9965,7 @@ static int nfa_regatom(void) emsg(_(e_nopresub)); return FAIL; } - for (lp = (uint8_t *)reg_prev_sub; *lp != NUL; MB_CPTR_ADV(lp)) { + for (lp = (uint8_t *)reg_prev_sub; *lp != NUL; lp += utf_ptr2len((char *)lp)) { EMIT(utf_ptr2char((char *)lp)); if (lp != (uint8_t *)reg_prev_sub) { EMIT(NFA_CONCAT); @@ -10348,13 +10472,39 @@ collection: } else { if (got_coll_char == true && startc == 0) { EMIT(0x0a); + EMIT(NFA_CONCAT); } else { EMIT(startc); + if (utf_ptr2len(regparse) == utfc_ptr2len(regparse)) { + EMIT(NFA_CONCAT); + } } - EMIT(NFA_CONCAT); } } + int plen; + if (utf_ptr2len(regparse) != (plen = utfc_ptr2len(regparse))) { + int i = utf_ptr2len(regparse); + + c = utf_ptr2char(regparse + i); + + // Add composing characters + while (true) { + if (c == 0) { + // \x00 is translated to \x0a, start at \x01. + EMIT(1); + } else { + EMIT(c); + } + EMIT(NFA_CONCAT); + if ((i += utf_char2len(c)) >= plen) { + break; + } + c = utf_ptr2char(regparse + i); + } + EMIT(NFA_COMPOSING); + EMIT(NFA_CONCAT); + } MB_PTR_ADV(regparse); } // while (p < endp) @@ -14403,6 +14553,78 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm state = t->state->out; result_if_matched = (t->state->c == NFA_START_COLL); while (true) { + if (state->c == NFA_COMPOSING) { + int mc = curc; + int len = 0; + nfa_state_T *end; + nfa_state_T *sta; + int cchars[MAX_MCO]; + int ccount = 0; + int j; + + sta = t->state->out->out; + if (utf_iscomposing(sta->c)) { + // Only match composing character(s), ignore base + // character. Used for ".{composing}" and "{composing}" + // (no preceding character). + len += utf_char2len(mc); + } + if (rex.reg_icombine && len == 0) { + // If \Z was present, then ignore composing characters. + // When ignoring the base character this always matches. + if (sta->c != curc) { + result = FAIL; + } else { + result = OK; + } + while (sta->c != NFA_END_COMPOSING) { + sta = sta->out; + } + } + // Check base character matches first, unless ignored. + else if (len > 0 || mc == sta->c) { + if (len == 0) { + len += utf_char2len(mc); + sta = sta->out; + } + + // We don't care about the order of composing characters. + // Get them into cchars[] first. + while (len < clen) { + mc = utf_ptr2char((char *)rex.input + len); + cchars[ccount++] = mc; + len += utf_char2len(mc); + if (ccount == MAX_MCO) { + break; + } + } + + // Check that each composing char in the pattern matches a + // composing char in the text. We do not check if all + // composing chars are matched. + result = OK; + while (sta->c != NFA_END_COMPOSING) { + for (j = 0; j < ccount; j++) { + if (cchars[j] == sta->c) { + break; + } + } + if (j == ccount) { + result = FAIL; + break; + } + sta = sta->out; + } + } else { + result = FAIL; + } + + if (t->state->out->out1->c == NFA_END_COMPOSING) { + end = t->state->out->out1; + ADD_STATE_IF_MATCH(end); + } + break; + } if (state->c == NFA_END_COLL) { result = !result_if_matched; break; @@ -15545,6 +15767,9 @@ static regengine_T bt_regengine = { bt_regfree, bt_regexec_nl, bt_regexec_multi, +#ifdef REGEXP_DEBUG + "", +#endif }; static regengine_T nfa_regengine = { @@ -15552,6 +15777,9 @@ static regengine_T nfa_regengine = { nfa_regfree, nfa_regexec_nl, nfa_regexec_multi, +#ifdef REGEXP_DEBUG + "", +#endif }; // Which regexp engine to use? Needed for vim_regcomp(). diff --git a/src/nvim/regexp.h b/src/nvim/regexp.h index 447f4860a9..de4e5ded9c 100644 --- a/src/nvim/regexp.h +++ b/src/nvim/regexp.h @@ -1,8 +1,8 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep -#include "nvim/regexp_defs.h" // IWYU pragma: export +#include "nvim/regexp_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep // Second argument for vim_regcomp(). diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h index 079f3b6929..b8e87d069d 100644 --- a/src/nvim/regexp_defs.h +++ b/src/nvim/regexp_defs.h @@ -15,51 +15,15 @@ #include "nvim/pos_defs.h" #include "nvim/types_defs.h" -/// Used for "magic_overruled". -typedef enum { - OPTION_MAGIC_NOT_SET, ///< p_magic not overruled - OPTION_MAGIC_ON, ///< magic on inside regexp - OPTION_MAGIC_OFF, ///< magic off inside regexp -} optmagic_T; - -/// Magicness of a pattern, used by regexp code. -/// The order and values matter: -/// magic <= MAGIC_OFF includes MAGIC_NONE -/// magic >= MAGIC_ON includes MAGIC_ALL -typedef enum { - MAGIC_NONE = 1, ///< "\V" very unmagic - MAGIC_OFF = 2, ///< "\M" or 'magic' off - MAGIC_ON = 3, ///< "\m" or 'magic' - MAGIC_ALL = 4, ///< "\v" very magic -} magic_T; - -/// The number of sub-matches is limited to 10. -/// The first one (index 0) is the whole match, referenced with "\0". -/// The second one (index 1) is the first sub-match, referenced with "\1". -/// This goes up to the tenth (index 9), referenced with "\9". -enum { NSUBEXP = 10, }; - -/// In the NFA engine: how many braces are allowed. -/// TODO(RE): Use dynamic memory allocation instead of static, like here -enum { NFA_MAX_BRACES = 20, }; - -/// In the NFA engine: how many states are allowed. enum { - NFA_MAX_STATES = 100000, - NFA_TOO_EXPENSIVE = -1, -}; - -/// Which regexp engine to use? Needed for vim_regcomp(). -/// Must match with 'regexpengine'. -enum { - AUTOMATIC_ENGINE = 0, - BACKTRACKING_ENGINE = 1, - NFA_ENGINE = 2, + /// The number of sub-matches is limited to 10. + /// The first one (index 0) is the whole match, referenced with "\0". + /// The second one (index 1) is the first sub-match, referenced with "\1". + /// This goes up to the tenth (index 9), referenced with "\9". + NSUBEXP = 10, }; typedef struct regengine regengine_T; -typedef struct regprog regprog_T; -typedef struct reg_extmatch reg_extmatch_T; /// Structure to be used for multi-line matching. /// Sub-match "no" starts in line "startpos[no].lnum" column "startpos[no].col" @@ -77,73 +41,23 @@ typedef struct { colnr_T rmm_maxcol; ///< when not zero: maximum column } regmmatch_T; -#include "nvim/buffer_defs.h" - -/// Structure returned by vim_regcomp() to pass on to vim_regexec(). -/// This is the general structure. For the actual matcher, two specific -/// structures are used. See code below. -struct regprog { - regengine_T *engine; - unsigned regflags; - unsigned re_engine; ///< Automatic, backtracking or NFA engine. - unsigned re_flags; ///< Second argument for vim_regcomp(). - bool re_in_use; ///< prog is being executed -}; - -/// Structure used by the back track matcher. -/// These fields are only to be used in regexp.c! -/// See regexp.c for an explanation. -typedef struct { - // These four members implement regprog_T. - regengine_T *engine; - unsigned regflags; - unsigned re_engine; - unsigned re_flags; - bool re_in_use; - - int regstart; - uint8_t reganch; - uint8_t *regmust; - int regmlen; - uint8_t reghasz; - uint8_t program[]; -} bt_regprog_T; - -/// Structure representing a NFA state. -/// An NFA state may have no outgoing edge, when it is a NFA_MATCH state. -typedef struct nfa_state nfa_state_T; -struct nfa_state { - int c; - nfa_state_T *out; - nfa_state_T *out1; - int id; - int lastlist[2]; ///< 0: normal, 1: recursive - int val; -}; - -/// Structure used by the NFA matcher. -typedef struct { - // These four members implement regprog_T. - regengine_T *engine; - unsigned regflags; - unsigned re_engine; - unsigned re_flags; - bool re_in_use; - - nfa_state_T *start; ///< points into state[] - - int reganch; ///< pattern starts with ^ - int regstart; ///< char at start of pattern - uint8_t *match_text; ///< plain text to match with +/// Used for "magic_overruled". +typedef enum { + OPTION_MAGIC_NOT_SET, ///< p_magic not overruled + OPTION_MAGIC_ON, ///< magic on inside regexp + OPTION_MAGIC_OFF, ///< magic off inside regexp +} optmagic_T; - int has_zend; ///< pattern contains \ze - int has_backref; ///< pattern contains \1 .. \9 - int reghasz; - char *pattern; - int nsubexp; ///< number of () - int nstate; - nfa_state_T state[]; -} nfa_regprog_T; +/// Magicness of a pattern, used by regexp code. +/// The order and values matter: +/// magic <= MAGIC_OFF includes MAGIC_NONE +/// magic >= MAGIC_ON includes MAGIC_ALL +typedef enum { + MAGIC_NONE = 1, ///< "\V" very unmagic + MAGIC_OFF = 2, ///< "\M" or 'magic' off + MAGIC_ON = 3, ///< "\m" or 'magic' + MAGIC_ALL = 4, ///< "\v" very magic +} magic_T; /// Structure to be used for single-line matching. /// Sub-match "no" starts at "startp[no]" and ends just before "endp[no]". @@ -160,22 +74,10 @@ typedef struct { /// Structure used to store external references: "\z\(\)" to "\z\1". /// Use a reference count to avoid the need to copy this around. When it goes /// from 1 to zero the matches need to be freed. -struct reg_extmatch { +typedef struct { int16_t refcnt; uint8_t *matches[NSUBEXP]; -}; - -struct regengine { - /// bt_regcomp or nfa_regcomp - regprog_T *(*regcomp)(uint8_t *, int); - /// bt_regfree or nfa_regfree - void (*regfree)(regprog_T *); - /// bt_regexec_nl or nfa_regexec_nl - int (*regexec_nl)(regmatch_T *, uint8_t *, colnr_T, bool); - /// bt_regexec_mult or nfa_regexec_mult - int (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T, proftime_T *, int *); - // uint8_t *expr; -}; +} reg_extmatch_T; /// Flags used by vim_regsub() and vim_regsub_both() enum { diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 38d3942126..76188499e3 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -17,6 +17,8 @@ #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/debugger.h" @@ -26,16 +28,18 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" -#include "nvim/func_attr.h" +#include "nvim/ex_eval_defs.h" #include "nvim/garray.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" #include "nvim/map_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -45,11 +49,13 @@ #include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/strings.h" #include "nvim/types_defs.h" @@ -303,7 +309,7 @@ static bool source_callback_vim_lua(int num_fnames, char **fnames, bool all, voi for (int i = 0; i < num_fnames; i++) { if (path_with_extension(fnames[i], "vim")) { - (void)do_source(fnames[i], false, DOSO_NONE, cookie); + do_source(fnames[i], false, DOSO_NONE, cookie); did_one = true; if (!all) { return true; @@ -313,7 +319,7 @@ static bool source_callback_vim_lua(int num_fnames, char **fnames, bool all, voi for (int i = 0; i < num_fnames; i++) { if (path_with_extension(fnames[i], "lua")) { - (void)do_source(fnames[i], false, DOSO_NONE, cookie); + do_source(fnames[i], false, DOSO_NONE, cookie); did_one = true; if (!all) { return true; @@ -337,7 +343,7 @@ static bool source_callback(int num_fnames, char **fnames, bool all, void *cooki for (int i = 0; i < num_fnames; i++) { if (!path_with_extension(fnames[i], "vim") && !path_with_extension(fnames[i], "lua")) { - (void)do_source(fnames[i], false, DOSO_NONE, cookie); + do_source(fnames[i], false, DOSO_NONE, cookie); did_one = true; if (!all) { return true; @@ -568,31 +574,31 @@ static int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, return did_one ? OK : FAIL; } -Array runtime_inspect(void) +Array runtime_inspect(Arena *arena) { RuntimeSearchPath path = runtime_search_path; - Array rv = ARRAY_DICT_INIT; + Array rv = arena_array(arena, kv_size(path)); for (size_t i = 0; i < kv_size(path); i++) { SearchPathItem *item = &kv_A(path, i); - Array entry = ARRAY_DICT_INIT; - ADD(entry, CSTR_TO_OBJ(item->path)); - ADD(entry, BOOLEAN_OBJ(item->after)); + Array entry = arena_array(arena, 3); + ADD_C(entry, CSTR_AS_OBJ(item->path)); + ADD_C(entry, BOOLEAN_OBJ(item->after)); if (item->has_lua != kNone) { - ADD(entry, BOOLEAN_OBJ(item->has_lua == kTrue)); + ADD_C(entry, BOOLEAN_OBJ(item->has_lua == kTrue)); } - ADD(rv, ARRAY_OBJ(entry)); + ADD_C(rv, ARRAY_OBJ(entry)); } return rv; } -ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all) +ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all, Arena *arena) { int ref; RuntimeSearchPath path = runtime_search_path_get_cached(&ref); static char buf[MAXPATHL]; - ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, path, buf, sizeof buf); + ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, path, buf, sizeof buf, arena); runtime_search_path_unref(path, &ref); return rv; @@ -604,15 +610,16 @@ ArrayOf(String) runtime_get_named_thread(bool lua, Array pat, bool all) uv_mutex_lock(&runtime_search_path_mutex); static char buf[MAXPATHL]; ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, runtime_search_path_thread, - buf, sizeof buf); + buf, sizeof buf, NULL); uv_mutex_unlock(&runtime_search_path_mutex); return rv; } static ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all, - RuntimeSearchPath path, char *buf, size_t buf_len) + RuntimeSearchPath path, char *buf, size_t buf_len, + Arena *arena) { - ArrayOf(String) rv = ARRAY_DICT_INIT; + ArrayOf(String) rv = arena_array(arena, kv_size(path) * pat.size); for (size_t i = 0; i < kv_size(path); i++) { SearchPathItem *item = &kv_A(path, i); if (lua) { @@ -632,7 +639,7 @@ static ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all, item->path, pat_item.data.string.data); if (size < buf_len) { if (os_file_is_readable(buf)) { - ADD(rv, CSTR_TO_OBJ(buf)); + ADD_C(rv, CSTR_TO_ARENA_OBJ(arena, buf)); if (!all) { goto done; } @@ -1062,7 +1069,7 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack) xstrlcat(new_rtp, afterdir, new_rtp_capacity); } - set_option_value_give_err("rtp", CSTR_AS_OPTVAL(new_rtp), 0); + set_option_value_give_err(kOptRuntimepath, CSTR_AS_OPTVAL(new_rtp), 0); xfree(new_rtp); retval = OK; @@ -1085,7 +1092,7 @@ static int load_pack_plugin(bool opt, char *fname) size_t len = strlen(ffname) + sizeof(plugpat); char *pat = xmallocz(len); - vim_snprintf(pat, len, plugpat, ffname); // NOLINT + vim_snprintf(pat, len, plugpat, ffname); gen_expand_wildcards_and_cb(1, &pat, EW_FILE, true, source_callback_vim_lua, NULL); char *cmd = xstrdup("g:did_load_filetypes"); @@ -1773,7 +1780,7 @@ freeall: static void cmd_source(char *fname, exarg_T *eap) { if (eap != NULL && *fname == NUL) { - cmd_source_buffer(eap); + cmd_source_buffer(eap, false); } else if (eap != NULL && eap->forceit) { // ":source!": read Normal mode commands // Need to execute the commands directly. This is required at least @@ -1804,7 +1811,7 @@ void ex_options(exarg_T *eap) bool multi_mods = 0; buf[0] = NUL; - (void)add_win_cmd_modifiers(buf, &cmdmod, &multi_mods); + add_win_cmd_modifiers(buf, &cmdmod, &multi_mods); os_setenv("OPTWIN_CMD", buf, 1); cmd_source(SYS_OPTWIN_FILE, NULL); @@ -1845,7 +1852,7 @@ static FILE *fopen_noinh_readbin(char *filename) return NULL; } - (void)os_set_cloexec(fd_tmp); + os_set_cloexec(fd_tmp); return fdopen(fd_tmp, READBIN); } @@ -1983,7 +1990,7 @@ static int source_using_linegetter(void *cookie, LineGetter fgetline, const char return retval; } -static void cmd_source_buffer(const exarg_T *const eap) +void cmd_source_buffer(const exarg_T *const eap, bool ex_lua) FUNC_ATTR_NONNULL_ALL { if (curbuf == NULL) { @@ -2006,9 +2013,10 @@ static void cmd_source_buffer(const exarg_T *const eap) .buf = ga.ga_data, .offset = 0, }; - if (strequal(curbuf->b_p_ft, "lua") + if (ex_lua || strequal(curbuf->b_p_ft, "lua") || (curbuf->b_fname && path_with_extension(curbuf->b_fname, "lua"))) { - nlua_source_using_linegetter(get_str_line, (void *)&cookie, ":source (no file)"); + char *name = ex_lua ? ":{range}lua" : ":source (no file)"; + nlua_source_using_linegetter(get_str_line, (void *)&cookie, name); } else { source_using_linegetter((void *)&cookie, get_str_line, ":source (no file)"); } @@ -2623,7 +2631,7 @@ static char *get_one_sourceline(struct source_cookie *sp) int c; char *buf; #ifdef USE_CRNL - int has_cr; // CR-LF found + bool has_cr; // CR-LF found #endif bool have_read = false; @@ -2758,7 +2766,7 @@ void ex_finish(exarg_T *eap) /// Mark a sourced file as finished. Possibly makes the ":finish" pending. /// Also called for a pending finish at the ":endtry" or after returning from /// an extra do_cmdline(). "reanimate" is used in the latter case. -void do_finish(exarg_T *eap, int reanimate) +void do_finish(exarg_T *eap, bool reanimate) { if (reanimate) { ((struct source_cookie *)getline_cookie(eap->getline, diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h index 87e4436618..7312b62526 100644 --- a/src/nvim/runtime.h +++ b/src/nvim/runtime.h @@ -9,7 +9,7 @@ #include "nvim/garray_defs.h" #include "nvim/option_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep -#include "nvim/runtime_defs.h" // IWYU pragma: export +#include "nvim/runtime_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep /// Stack of execution contexts. Each entry is an estack_T. diff --git a/src/nvim/runtime_defs.h b/src/nvim/runtime_defs.h index 169cadfb94..7029e8be66 100644 --- a/src/nvim/runtime_defs.h +++ b/src/nvim/runtime_defs.h @@ -3,11 +3,6 @@ #include <stdbool.h> #include "nvim/autocmd_defs.h" -#include "nvim/eval/typval_defs.h" -#include "nvim/ex_eval_defs.h" -#include "nvim/garray_defs.h" -#include "nvim/pos_defs.h" -#include "nvim/types_defs.h" typedef enum { ETYPE_TOP, ///< toplevel diff --git a/src/nvim/search.c b/src/nvim/search.c index 642219c1e0..48e41c290d 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -10,6 +10,7 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" @@ -20,20 +21,23 @@ #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/indent_c.h" #include "nvim/insexpand.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -54,6 +58,7 @@ #include "nvim/strings.h" #include "nvim/tag.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -92,7 +97,7 @@ static const char e_search_hit_bottom_without_match_for_str[] // one for other searches. last_idx points to the one that was used the last // time. -static struct spat spats[2] = { +static SearchPattern spats[2] = { // Last used search pattern [0] = { NULL, true, false, 0, { '/', false, false, 0 }, NULL }, // Last used substitute pattern @@ -103,12 +108,12 @@ static int last_idx = 0; // index in spats[] for RE_LAST static uint8_t lastc[2] = { NUL, NUL }; // last character searched for static Direction lastcdir = FORWARD; // last direction of character search -static int last_t_cmd = true; // last search t_cmd +static bool last_t_cmd = true; // last search t_cmd static char lastc_bytes[MB_MAXBYTES + 1]; static int lastc_bytelen = 1; // >1 for multi-byte char // copy of spats[], for keeping the search patterns while executing autocmds -static struct spat saved_spats[2]; +static SearchPattern saved_spats[2]; static char *saved_mr_pattern = NULL; static int saved_spats_last_idx = 0; static bool saved_spats_no_hlsearch = false; @@ -118,7 +123,7 @@ static char *mr_pattern = NULL; // Type used by find_pattern_in_path() to remember which included files have // been searched already. -typedef struct SearchedFile { +typedef struct { FILE *fp; // File pointer char *name; // Full name of file linenr_T lnum; // Line we were up to in file @@ -272,7 +277,7 @@ void restore_search_patterns(void) set_no_hlsearch(saved_spats_no_hlsearch); } -static inline void free_spat(struct spat *const spat) +static inline void free_spat(SearchPattern *const spat) { xfree(spat->pat); tv_dict_unref(spat->additional_data); @@ -293,7 +298,7 @@ void free_search_patterns(void) // copy of spats[RE_SEARCH], for keeping the search patterns while incremental // searching -static struct spat saved_last_search_spat; +static SearchPattern saved_last_search_spat; static int did_save_last_search_spat = 0; static int saved_last_idx = 0; static bool saved_no_hlsearch = false; @@ -390,7 +395,7 @@ bool pat_has_uppercase(char *pat) magic_T magic_val = MAGIC_ON; // get the magicness of the pattern - (void)skip_regexp_ex(pat, NUL, magic_isset(), NULL, NULL, &magic_val); + skip_regexp_ex(pat, NUL, magic_isset(), NULL, NULL, &magic_val); while (*p != NUL) { const int l = utfc_ptr2len(p); @@ -438,7 +443,7 @@ int last_csearch_forward(void) int last_csearch_until(void) { - return last_t_cmd == true; + return last_t_cmd; } void set_last_csearch(int c, char *s, int len) @@ -476,7 +481,7 @@ void reset_search_dir(void) // Set the last search pattern. For ":let @/ =" and ShaDa file. // Also set the saved search pattern, so that this works in an autocommand. -void set_last_search_pat(const char *s, int idx, int magic, int setlast) +void set_last_search_pat(const char *s, int idx, int magic, bool setlast) { free_spat(&spats[idx]); // An empty string means that nothing should be matched. @@ -523,7 +528,7 @@ void last_pat_prog(regmmatch_T *regmatch) return; } emsg_off++; // So it doesn't beep if bad expr - (void)search_regcomp("", NULL, 0, last_idx, SEARCH_KEEP, regmatch); + search_regcomp("", NULL, 0, last_idx, SEARCH_KEEP, regmatch); emsg_off--; } @@ -1049,7 +1054,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in // Save the values for when (options & SEARCH_KEEP) is used. // (there is no "if ()" around this because gcc wants them initialized) - struct soffset old_off = spats[0].off; + SearchOffset old_off = spats[0].off; pos = curwin->w_cursor; // start searching at the cursor position @@ -1497,7 +1502,7 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char *pat) /// position of the character, otherwise move to just before the char. /// Do this "cap->count1" times. /// Return FAIL or OK. -int searchc(cmdarg_T *cap, int t_cmd) +int searchc(cmdarg_T *cap, bool t_cmd) FUNC_ATTR_NONNULL_ALL { int c = cap->nchar; // char to search for @@ -2520,7 +2525,7 @@ int current_search(int count, bool forward) /// else from position "cur". /// "direction" is FORWARD or BACKWARD. /// Returns true, false or -1 for failure. -static int is_zero_width(char *pattern, int move, pos_T *cur, Direction direction) +static int is_zero_width(char *pattern, bool move, pos_T *cur, Direction direction) { regmmatch_T regmatch; int result = -1; @@ -2576,8 +2581,8 @@ static int is_zero_width(char *pattern, int move, pos_T *cur, Direction directio return result; } -/// return true if line 'lnum' is empty or has white chars only. -int linewhite(linenr_T lnum) +/// @return true if line 'lnum' is empty or has white chars only. +bool linewhite(linenr_T lnum) { char *p = skipwhite(ml_get(lnum)); return *p == NUL; @@ -3192,7 +3197,11 @@ static int fuzzy_match_item_compare(const void *const s1, const void *const s2) const int idx1 = ((const fuzzyItem_T *)s1)->idx; const int idx2 = ((const fuzzyItem_T *)s2)->idx; - return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1; + if (v1 == v2) { + return idx1 == idx2 ? 0 : idx1 > idx2 ? 1 : -1; + } else { + return v1 > v2 ? -1 : 1; + } } /// Fuzzy search the string "str" in a list of "items" and return the matching @@ -3431,7 +3440,11 @@ static int fuzzy_match_str_compare(const void *const s1, const void *const s2) const int idx1 = ((fuzmatch_str_T *)s1)->idx; const int idx2 = ((fuzmatch_str_T *)s2)->idx; - return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1; + if (v1 == v2) { + return idx1 == idx2 ? 0 : idx1 > idx2 ? 1 : -1; + } else { + return v1 > v2 ? -1 : 1; + } } /// Sort fuzzy matches by score @@ -3460,7 +3473,11 @@ static int fuzzy_match_func_compare(const void *const s1, const void *const s2) if (*str1 == '<' && *str2 != '<') { return 1; } - return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1; + if (v1 == v2) { + return idx1 == idx2 ? 0 : idx1 > idx2 ? 1 : -1; + } else { + return v1 > v2 ? -1 : 1; + } } /// Sort fuzzy matches of function names by score. @@ -3559,7 +3576,6 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool char *curr_fname = curbuf->b_fname; char *prev_fname = NULL; int depth_displayed; // For type==CHECK_PATH - int already_searched; char *p; bool define_matched; regmatch_T regmatch; @@ -3645,7 +3661,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool FNAME_EXP|FNAME_INCL|FNAME_REL, 1, p_fname, NULL); } - already_searched = false; + bool already_searched = false; if (new_fname != NULL) { // Check whether we have already searched in this file for (i = 0;; i++) { diff --git a/src/nvim/search.h b/src/nvim/search.h index 48ca555e7f..09af34d87e 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -3,67 +3,78 @@ #include <stdbool.h> #include <stdint.h> -#include "nvim/buffer_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/normal_defs.h" // IWYU pragma: keep #include "nvim/os/time_defs.h" #include "nvim/pos_defs.h" #include "nvim/regexp_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" -#include "nvim/vim_defs.h" +#include "nvim/vim_defs.h" // IWYU pragma: keep -// Values for the find_pattern_in_path() function args 'type' and 'action': -#define FIND_ANY 1 -#define FIND_DEFINE 2 -#define CHECK_PATH 3 +/// Values for the find_pattern_in_path() function args 'type' and 'action': +enum { + FIND_ANY = 1, + FIND_DEFINE = 2, + CHECK_PATH = 3, +}; -#define ACTION_SHOW 1 -#define ACTION_GOTO 2 -#define ACTION_SPLIT 3 -#define ACTION_SHOW_ALL 4 -#define ACTION_EXPAND 5 +enum { + ACTION_SHOW = 1, + ACTION_GOTO = 2, + ACTION_SPLIT = 3, + ACTION_SHOW_ALL = 4, + ACTION_EXPAND = 5, +}; -// Values for 'options' argument in do_search() and searchit() -#define SEARCH_REV 0x01 ///< go in reverse of previous dir. -#define SEARCH_ECHO 0x02 ///< echo the search command and handle options -#define SEARCH_MSG 0x0c ///< give messages (yes, it's not 0x04) -#define SEARCH_NFMSG 0x08 ///< give all messages except not found -#define SEARCH_OPT 0x10 ///< interpret optional flags -#define SEARCH_HIS 0x20 ///< put search pattern in history -#define SEARCH_END 0x40 ///< put cursor at end of match -#define SEARCH_NOOF 0x80 ///< don't add offset to position -#define SEARCH_START 0x100 ///< start search without col offset -#define SEARCH_MARK 0x200 ///< set previous context mark -#define SEARCH_KEEP 0x400 ///< keep previous search pattern -#define SEARCH_PEEK 0x800 ///< peek for typed char, cancel search -#define SEARCH_COL 0x1000 ///< start at specified column instead of zero +/// Values for "options" argument in do_search() and searchit() +enum { + SEARCH_REV = 0x01, ///< go in reverse of previous dir. + SEARCH_ECHO = 0x02, ///< echo the search command and handle options + SEARCH_MSG = 0x0c, ///< give messages (yes, it's not 0x04) + SEARCH_NFMSG = 0x08, ///< give all messages except not found + SEARCH_OPT = 0x10, ///< interpret optional flags + SEARCH_HIS = 0x20, ///< put search pattern in history + SEARCH_END = 0x40, ///< put cursor at end of match + SEARCH_NOOF = 0x80, ///< don't add offset to position + SEARCH_START = 0x100, ///< start search without col offset + SEARCH_MARK = 0x200, ///< set previous context mark + SEARCH_KEEP = 0x400, ///< keep previous search pattern + SEARCH_PEEK = 0x800, ///< peek for typed char, cancel search + SEARCH_COL = 0x1000, ///< start at specified column instead of zero +}; -// Values for flags argument for findmatchlimit() -#define FM_BACKWARD 0x01 // search backwards -#define FM_FORWARD 0x02 // search forwards -#define FM_BLOCKSTOP 0x04 // stop at start/end of block -#define FM_SKIPCOMM 0x08 // skip comments +/// Values for flags argument for findmatchlimit() +enum { + FM_BACKWARD = 0x01, ///< search backwards + FM_FORWARD = 0x02, ///< search forwards + FM_BLOCKSTOP = 0x04, ///< stop at start/end of block + FM_SKIPCOMM = 0x08, ///< skip comments +}; -// Values for sub_cmd and which_pat argument for search_regcomp() -// Also used for which_pat argument for searchit() -#define RE_SEARCH 0 // save/use pat in/from search_pattern -#define RE_SUBST 1 // save/use pat in/from subst_pattern -#define RE_BOTH 2 // save pat in both patterns -#define RE_LAST 2 // use last used pattern if "pat" is NULL +/// Values for sub_cmd and which_pat argument for search_regcomp() +/// Also used for which_pat argument for searchit() +enum { + RE_SEARCH = 0, ///< save/use pat in/from search_pattern + RE_SUBST = 1, ///< save/use pat in/from subst_pattern + RE_BOTH = 2, ///< save pat in both patterns + RE_LAST = 2, ///< use last used pattern if "pat" is NULL +}; // Values for searchcount() -#define SEARCH_STAT_DEF_TIMEOUT 40 -#define SEARCH_STAT_DEF_MAX_COUNT 99 -#define SEARCH_STAT_BUF_LEN 12 +enum { SEARCH_STAT_DEF_TIMEOUT = 40, }; +enum { SEARCH_STAT_DEF_MAX_COUNT = 99, }; +enum { SEARCH_STAT_BUF_LEN = 12, }; -/// Maximum number of characters that can be fuzzy matched -#define MAX_FUZZY_MATCHES 256 +enum { + /// Maximum number of characters that can be fuzzy matched + MAX_FUZZY_MATCHES = 256, +}; /// Structure containing offset definition for the last search pattern /// /// @note Only offset for the last search pattern is used, not for the last /// substitute pattern. -typedef struct soffset { +typedef struct { char dir; ///< Search direction: forward ('/') or backward ('?') bool line; ///< True if search has line offset. bool end; ///< True if search sets cursor at the end. @@ -71,7 +82,7 @@ typedef struct soffset { } SearchOffset; /// Structure containing last search pattern and its attributes. -typedef struct spat { +typedef struct { char *pat; ///< The pattern (in allocated memory) or NULL. bool magic; ///< Magicness of the pattern. bool no_scs; ///< No smartcase for this pattern. @@ -88,7 +99,7 @@ typedef struct { int sa_wrapped; ///< search wrapped around } searchit_arg_T; -typedef struct searchstat { +typedef struct { int cur; // current position of found words int cnt; // total count of found words bool exact_match; // true if matched exactly on specified position diff --git a/src/nvim/shada.c b/src/nvim/shada.c index be898142f0..0b148993f8 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -17,40 +17,49 @@ #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/cmdhist.h" #include "nvim/eval.h" #include "nvim/eval/decode.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/macros_defs.h" #include "nvim/map_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/msgpack_rpc/helpers.h" -#include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/os/fileio.h" +#include "nvim/os/fileio_defs.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/time.h" +#include "nvim/os/time_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" #include "nvim/search.h" #include "nvim/shada.h" #include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/version.h" #include "nvim/vim_defs.h" @@ -347,25 +356,25 @@ typedef struct { PMap(cstr_t) file_marks; ///< All file marks. } WriteMergerState; -struct sd_read_def; +typedef struct sd_read_def ShaDaReadDef; /// Function used to close files defined by ShaDaReadDef -typedef void (*ShaDaReadCloser)(struct sd_read_def *const sd_reader) +typedef void (*ShaDaReadCloser)(ShaDaReadDef *const sd_reader) REAL_FATTR_NONNULL_ALL; /// Function used to read ShaDa files -typedef ptrdiff_t (*ShaDaFileReader)(struct sd_read_def *const sd_reader, +typedef ptrdiff_t (*ShaDaFileReader)(ShaDaReadDef *const sd_reader, void *const dest, const size_t size) REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; /// Function used to skip in ShaDa files -typedef int (*ShaDaFileSkipper)(struct sd_read_def *const sd_reader, +typedef int (*ShaDaFileSkipper)(ShaDaReadDef *const sd_reader, const size_t offset) REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; /// Structure containing necessary pointers for reading ShaDa files -typedef struct sd_read_def { +struct sd_read_def { ShaDaFileReader read; ///< Reader function. ShaDaReadCloser close; ///< Close function. ShaDaFileSkipper skip; ///< Function used to skip some bytes. @@ -374,27 +383,7 @@ typedef struct sd_read_def { const char *error; ///< Error message in case of error. uintmax_t fpos; ///< Current position (amount of bytes read since ///< reader structure initialization). May overflow. -} ShaDaReadDef; - -struct sd_write_def; - -/// Function used to close files defined by ShaDaWriteDef -typedef void (*ShaDaWriteCloser)(struct sd_write_def *const sd_writer) - REAL_FATTR_NONNULL_ALL; - -/// Function used to write ShaDa files -typedef ptrdiff_t (*ShaDaFileWriter)(struct sd_write_def *const sd_writer, - const void *const src, - const size_t size) - REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; - -/// Structure containing necessary pointers for writing ShaDa files -typedef struct sd_write_def { - ShaDaFileWriter write; ///< Writer function. - ShaDaWriteCloser close; ///< Close function. - void *cookie; ///< Data describing object written to. - const char *error; ///< Error message in case of error. -} ShaDaWriteDef; +}; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "shada.c.generated.h" @@ -626,33 +615,12 @@ static int read_char(ShaDaReadDef *const sd_reader) return (int)ret; } -/// Wrapper for writing to file descriptors -/// -/// @return -1 or number of bytes written. -static ptrdiff_t write_file(ShaDaWriteDef *const sd_writer, const void *const dest, - const size_t size) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - const ptrdiff_t ret = file_write(sd_writer->cookie, dest, size); - if (ret < 0) { - sd_writer->error = os_strerror((int)ret); - return -1; - } - return ret; -} - /// Wrapper for closing file descriptors opened for reading static void close_sd_reader(ShaDaReadDef *const sd_reader) FUNC_ATTR_NONNULL_ALL { close_file(sd_reader->cookie); -} - -/// Wrapper for closing file descriptors opened for writing -static void close_sd_writer(ShaDaWriteDef *const sd_writer) - FUNC_ATTR_NONNULL_ALL -{ - close_file(sd_writer->cookie); + xfree(sd_reader->cookie); } /// Wrapper for read that reads to IObuff and ignores bytes read @@ -721,8 +689,6 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, const size_ static int open_shada_file_for_reading(const char *const fname, ShaDaReadDef *sd_reader) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - int error; - *sd_reader = (ShaDaReadDef) { .read = &read_file, .close = &close_sd_reader, @@ -730,9 +696,11 @@ static int open_shada_file_for_reading(const char *const fname, ShaDaReadDef *sd .error = NULL, .eof = false, .fpos = 0, - .cookie = file_open_new(&error, fname, kFileReadOnly, 0), + .cookie = xmalloc(sizeof(FileDescriptor)), }; - if (sd_reader->cookie == NULL) { + int error = file_open(sd_reader->cookie, fname, kFileReadOnly, 0); + if (error) { + XFREE_CLEAR(sd_reader->cookie); return error; } @@ -742,25 +710,26 @@ static int open_shada_file_for_reading(const char *const fname, ShaDaReadDef *sd } /// Wrapper for closing file descriptors -static void close_file(void *cookie) +static void close_file(FileDescriptor *cookie) { - const int error = file_free(cookie, !!p_fs); + const int error = file_close(cookie, !!p_fs); if (error != 0) { semsg(_(SERR "System error while closing ShaDa file: %s"), os_strerror(error)); } } -/// Msgpack callback for writing to ShaDaWriteDef* +/// Msgpack callback for writing to FileDescriptor* static int msgpack_sd_writer_write(void *data, const char *buf, size_t len) { - ShaDaWriteDef *const sd_writer = (ShaDaWriteDef *)data; - ptrdiff_t written_bytes = sd_writer->write(sd_writer, buf, len); - if (written_bytes == -1) { + FileDescriptor *const sd_writer = (FileDescriptor *)data; + const ptrdiff_t ret = file_write(sd_writer, buf, len); + if (ret < 0) { semsg(_(SERR "System error while writing ShaDa file: %s"), - sd_writer->error); + os_strerror((int)ret)); return -1; } + return 0; } @@ -997,6 +966,48 @@ static inline void hms_dealloc(HistoryMergerState *const hms_p) #define HMS_ITER(hms_p, cur_entry, code) \ HMLL_FORALL(&((hms_p)->hmll), cur_entry, code) +/// Iterate over global variables +/// +/// @warning No modifications to global variable dictionary must be performed +/// while iteration is in progress. +/// +/// @param[in] iter Iterator. Pass NULL to start iteration. +/// @param[out] name Variable name. +/// @param[out] rettv Variable value. +/// +/// @return Pointer that needs to be passed to next `var_shada_iter` invocation +/// or NULL to indicate that iteration is over. +static const void *var_shada_iter(const void *const iter, const char **const name, typval_T *rettv, + var_flavour_T flavour) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2, 3) +{ + const hashitem_T *hi; + const hashitem_T *hifirst = globvarht.ht_array; + const size_t hinum = (size_t)globvarht.ht_mask + 1; + *name = NULL; + if (iter == NULL) { + hi = globvarht.ht_array; + while ((size_t)(hi - hifirst) < hinum + && (HASHITEM_EMPTY(hi) + || !(var_flavour(hi->hi_key) & flavour))) { + hi++; + } + if ((size_t)(hi - hifirst) == hinum) { + return NULL; + } + } else { + hi = (const hashitem_T *)iter; + } + *name = TV_DICT_HI2DI(hi)->di_key; + tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); + while ((size_t)(++hi - hifirst) < hinum) { + if (!HASHITEM_EMPTY(hi) && (var_flavour(hi->hi_key) & flavour)) { + return hi; + } + } + return NULL; +} + /// Find buffer for given buffer name (cached) /// /// @param[in,out] fname_bufs Cache containing fname to buffer mapping. @@ -1118,7 +1129,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } HistoryMergerState hms[HIST_COUNT]; if (srni_flags & kSDReadHistory) { - for (HistoryType i = 0; i < HIST_COUNT; i++) { + for (int i = 0; i < HIST_COUNT; i++) { hms_init(&hms[i], (uint8_t)i, (size_t)p_hi, true, true); } } @@ -1211,7 +1222,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) // string is close to useless: you can only use it with :& or :~ and // that’s all because s//~ is not available until the first call to // regtilde. Vim was not calling this for some reason. - (void)regtilde(cur_entry.data.sub_string.sub, magic_isset(), false); + regtilde(cur_entry.data.sub_string.sub, magic_isset(), false); // Do not free shada entry: its allocated memory was saved above. break; case kSDItemHistoryEntry: @@ -1382,7 +1393,7 @@ shada_read_main_cycle_end: // memory for the history string itself and separator character which // may be assigned right away. if (srni_flags & kSDReadHistory) { - for (HistoryType i = 0; i < HIST_COUNT; i++) { + for (int i = 0; i < HIST_COUNT; i++) { hms_insert_whole_neovim_history(&hms[i]); clr_history(i); int *new_hisidx; @@ -1823,9 +1834,7 @@ static int compare_file_marks(const void *a, const void *b) const FileMarks *const *const b_fms = b; return ((*a_fms)->greatest_timestamp == (*b_fms)->greatest_timestamp ? 0 - : ((*a_fms)->greatest_timestamp > (*b_fms)->greatest_timestamp - ? -1 - : 1)); + : ((*a_fms)->greatest_timestamp > (*b_fms)->greatest_timestamp ? -1 : 1)); } /// Parse msgpack object that has given length @@ -2373,7 +2382,7 @@ static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, static inline void shada_initialize_registers(WriteMergerState *const wms, int max_reg_lines) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { - const void *reg_iter = NULL; + iter_register_T reg_iter = ITER_REGISTER_NULL; const bool limit_reg_lines = max_reg_lines >= 0; do { yankreg_T reg; @@ -2404,7 +2413,7 @@ static inline void shada_initialize_registers(WriteMergerState *const wms, int m } } }; - } while (reg_iter != NULL); + } while (reg_iter != ITER_REGISTER_NULL); } /// Replace numbered mark in WriteMergerState @@ -2473,7 +2482,7 @@ static int hist_type2char(const int type) /// @param[in] sd_reader Structure containing file reader definition. If it is /// not NULL then contents of this file will be merged /// with current Neovim runtime. -static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef *const sd_reader) +static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDef *const sd_reader) FUNC_ATTR_NONNULL_ARG(1) { ShaDaWriteResult ret = kSDWriteSuccessful; @@ -2500,7 +2509,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef bool dump_history = false; // Initialize history merger - for (HistoryType i = 0; i < HIST_COUNT; i++) { + for (int i = 0; i < HIST_COUNT; i++) { int num_saved = get_shada_parameter(hist_type2char(i)); if (num_saved == -1) { num_saved = (int)p_hi; @@ -2605,7 +2614,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef case VAR_LIST: { list_T *l = vartv.vval.v_list; int copyID = get_copyID(); - if (!set_ref_in_list(l, copyID, NULL) + if (!set_ref_in_list_items(l, copyID, NULL) && copyID == l->lv_copyID) { tv_clear(&vartv); continue; @@ -2894,7 +2903,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef #undef PACK_WMS_ARRAY if (dump_history) { - for (size_t i = 0; i < HIST_COUNT; i++) { + for (int i = 0; i < HIST_COUNT; i++) { if (dump_one_history[i]) { hms_insert_whole_neovim_history(&wms->hms[i]); HMS_ITER(&wms->hms[i], cur_entry, { @@ -2914,7 +2923,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef } shada_write_exit: - for (size_t i = 0; i < HIST_COUNT; i++) { + for (int i = 0; i < HIST_COUNT; i++) { if (dump_one_history[i]) { hms_dealloc(&wms->hms[i]); } @@ -2947,12 +2956,9 @@ int shada_write_file(const char *const file, bool nomerge) char *const fname = shada_filename(file); char *tempname = NULL; - ShaDaWriteDef sd_writer = { - .write = &write_file, - .close = &close_sd_writer, - .error = NULL, - }; + FileDescriptor sd_writer; ShaDaReadDef sd_reader = { .close = NULL }; + bool did_open_writer = false; if (!nomerge) { int error; @@ -2982,8 +2988,8 @@ int shada_write_file(const char *const file, bool nomerge) // 3: If somebody happened to delete the file after it was opened for // reading use u=rw permissions. shada_write_file_open: {} - sd_writer.cookie = file_open_new(&error, tempname, kFileCreateOnly|kFileNoSymlink, perm); - if (sd_writer.cookie == NULL) { + error = file_open(&sd_writer, tempname, kFileCreateOnly|kFileNoSymlink, perm); + if (error) { if (error == UV_EEXIST || error == UV_ELOOP) { // File already exists, try another name char *const wp = tempname + strlen(tempname) - 1; @@ -3004,6 +3010,8 @@ shada_write_file_open: {} semsg(_(SERR "System error while opening temporary ShaDa file %s " "for writing: %s"), tempname, os_strerror(error)); } + } else { + did_open_writer = true; } } if (nomerge) { @@ -3026,16 +3034,16 @@ shada_write_file_nomerge: {} } *tail = tail_save; } - int error; - sd_writer.cookie = file_open_new(&error, fname, kFileCreate|kFileTruncate, - 0600); - if (sd_writer.cookie == NULL) { + int error = file_open(&sd_writer, fname, kFileCreate|kFileTruncate, 0600); + if (error) { semsg(_(SERR "System error while opening ShaDa file %s for writing: %s"), fname, os_strerror(error)); + } else { + did_open_writer = true; } } - if (sd_writer.cookie == NULL) { + if (!did_open_writer) { xfree(fname); xfree(tempname); if (sd_reader.cookie != NULL) { @@ -3050,9 +3058,7 @@ shada_write_file_nomerge: {} verbose_leave(); } - const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge - ? NULL - : &sd_reader)); + const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge ? NULL : &sd_reader)); assert(sw_ret != kSDWriteIgnError); if (!nomerge) { sd_reader.close(&sd_reader); @@ -3082,7 +3088,7 @@ shada_write_file_nomerge: {} || old_info.stat.st_gid != getgid()) { const uv_uid_t old_uid = (uv_uid_t)old_info.stat.st_uid; const uv_gid_t old_gid = (uv_gid_t)old_info.stat.st_gid; - const int fchown_ret = os_fchown(file_fd(sd_writer.cookie), + const int fchown_ret = os_fchown(file_fd(&sd_writer), old_uid, old_gid); if (fchown_ret != 0) { semsg(_(RNERR "Failed setting uid and gid for file %s: %s"), @@ -3115,7 +3121,7 @@ shada_write_file_did_not_remove: } xfree(tempname); } - sd_writer.close(&sd_writer); + close_file(&sd_writer); xfree(fname); return OK; @@ -3358,7 +3364,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const CLEAR_GA_AND_ERROR_OUT(ad_ga); \ } #define CHECKED_KEY(un, entry_name, name, error_desc, tgt, condition, attr, proc) \ - else if (CHECK_KEY((un).data.via.map.ptr[i].key, name)) /* NOLINT(readability/braces) */ \ + else if (CHECK_KEY((un).data.via.map.ptr[i].key, name)) \ { \ CHECKED_ENTRY(condition, \ "has " name " key value " error_desc, \ @@ -3389,7 +3395,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const #define INTEGER_KEY(un, entry_name, name, tgt) \ INT_KEY(un, entry_name, name, tgt, TOINT) #define ADDITIONAL_KEY(un) \ - else { /* NOLINT(readability/braces) */ \ + else { \ ga_grow(&ad_ga, 1); \ memcpy(((char *)ad_ga.ga_data) + ((size_t)ad_ga.ga_len \ * sizeof(*(un).data.via.map.ptr)), \ @@ -3579,10 +3585,9 @@ shada_read_next_item_start: entry->data = sd_default_values[type_u64].data; switch ((ShadaEntryType)type_u64) { case kSDItemHeader: - if (!msgpack_rpc_to_dictionary(&(unpacked.data), &(entry->data.header))) { - semsg(_(READERR("header", "is not a dictionary")), initial_fpos); - goto shada_read_next_item_error; - } + // TODO(bfredl): header is written to file and provides useful debugging + // info. It is never read by nvim (earlier we parsed it back to a + // Dictionary, but that value was never used) break; case kSDItemSearchPattern: { if (unpacked.data.type != MSGPACK_OBJECT_MAP) { @@ -3983,7 +3988,7 @@ static bool shada_removable(const char *name) char *new_name = home_replace_save(NULL, name); for (char *p = p_shada; *p;) { - (void)copy_option_part(&p, part, ARRAY_SIZE(part), ", "); + copy_option_part(&p, part, ARRAY_SIZE(part), ", "); if (part[0] == 'r') { home_replace(NULL, part + 1, NameBuff, MAXPATHL, true); size_t n = strlen(NameBuff); diff --git a/src/nvim/sign.c b/src/nvim/sign.c index d05c708d2c..4e6672c5dd 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -18,22 +18,27 @@ #include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/extmark.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" +#include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/macros_defs.h" #include "nvim/map_defs.h" #include "nvim/marktree.h" +#include "nvim/marktree_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -46,8 +51,12 @@ #include "nvim/vim_defs.h" #include "nvim/window.h" -static PMap(cstr_t) sign_map INIT( = MAP_INIT); -static kvec_t(Integer) sign_ns INIT( = MAP_INIT); +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "sign.c.generated.h" +#endif + +static PMap(cstr_t) sign_map = MAP_INIT; +static kvec_t(Integer) sign_ns = KV_INITIAL_VALUE; static char *cmds[] = { "define", @@ -75,7 +84,7 @@ static int64_t group_get_ns(const char *group) return UINT32_MAX; // All namespaces } // Specific or non-existing namespace - int ns = map_get(String, int)(&namespace_ids, cstr_as_string((char *)group)); + int ns = map_get(String, int)(&namespace_ids, cstr_as_string(group)); return ns ? ns : -1; } @@ -103,7 +112,7 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT; sign.flags |= kSHIsSign; - sign.text.ptr = sp->sn_text ? xstrdup(sp->sn_text) : NULL; + memcpy(sign.text, sp->sn_text, SIGN_WIDTH * sizeof(schar_T)); sign.sign_name = xstrdup(sp->sn_name); sign.hl_id = sp->sn_text_hl; sign.line_hl_id = sp->sn_line_hl; @@ -112,12 +121,12 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr sign.priority = (DecorPriority)prio; bool has_hl = (sp->sn_line_hl || sp->sn_num_hl || sp->sn_cul_hl); - uint16_t decor_flags = (sp->sn_text ? MT_FLAG_DECOR_SIGNTEXT : 0) + uint16_t decor_flags = (sp->sn_text[0] ? MT_FLAG_DECOR_SIGNTEXT : 0) | (has_hl ? MT_FLAG_DECOR_SIGNHL : 0); DecorInline decor = { .ext = true, .data.ext = { .vt = NULL, .sh_idx = decor_put_sh(sign) } }; extmark_set(buf, ns, id, lnum - 1, 0, -1, -1, decor, decor_flags, true, - false, true, true, NULL); + false, true, true, false, NULL); } /// For an existing, placed sign with "id", modify the sign, group or priority. @@ -159,24 +168,22 @@ static int buf_findsign(buf_T *buf, int id, char *group) } /// qsort() function to sort signs by line number, priority, id and recency. -int sign_cmp(const void *p1, const void *p2) +static int sign_row_cmp(const void *p1, const void *p2) { const MTKey *s1 = (MTKey *)p1; const MTKey *s2 = (MTKey *)p2; - int n = s1->pos.row - s2->pos.row; - if (n) { - return n; + if (s1->pos.row != s2->pos.row) { + return s1->pos.row > s2->pos.row ? 1 : -1; } DecorSignHighlight *sh1 = decor_find_sign(mt_decor(*s1)); DecorSignHighlight *sh2 = decor_find_sign(mt_decor(*s2)); assert(sh1 && sh2); + SignItem si1 = { sh1, s1->id }; + SignItem si2 = { sh2, s2->id }; - n = sh2->priority - sh1->priority; - - return n ? n : (n = (int)(s2->id - s1->id)) - ? n : (sh2->sign_add_id - sh1->sign_add_id); + return sign_item_cmp(&si1, &si2); } /// Delete the specified sign(s) @@ -232,7 +239,7 @@ static int buf_delete_signs(buf_T *buf, char *group, int id, linenr_T atlnum) // Sort to remove the highest priority sign at a specific line number. if (kv_size(signs)) { - qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp); + qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_row_cmp); extmark_del_id(buf, kv_A(signs, 0).ns, kv_A(signs, 0).id); kv_destroy(signs); } else if (atlnum > 0) { @@ -241,13 +248,18 @@ static int buf_delete_signs(buf_T *buf, char *group, int id, linenr_T atlnum) // When deleting the last sign need to redraw the windows to remove the // sign column. Not when curwin is NULL (this means we're exiting). - if (!buf->b_signs_with_text && curwin != NULL) { + if (!buf_meta_total(buf, kMTMetaSignText) && curwin != NULL) { changed_line_abv_curs(); } return OK; } +bool buf_has_signs(const buf_T *buf) +{ + return (buf_meta_total(buf, kMTMetaSignHL) + buf_meta_total(buf, kMTMetaSignText)); +} + /// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. static void sign_list_placed(buf_T *rbuf, char *group) { @@ -261,7 +273,7 @@ static void sign_list_placed(buf_T *rbuf, char *group) msg_putchar('\n'); while (buf != NULL && !got_int) { - if (buf->b_signs) { + if (buf_has_signs(buf)) { vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname); msg_puts_attr(lbuf, HL_ATTR(HLF_D)); msg_putchar('\n'); @@ -282,7 +294,7 @@ static void sign_list_placed(buf_T *rbuf, char *group) } if (kv_size(signs)) { - qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp); + qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_row_cmp); for (size_t i = 0; i < kv_size(signs); i++) { namebuf[0] = '\0'; @@ -332,9 +344,24 @@ static int sign_cmd_idx(char *begin_cmd, char *end_cmd) return idx; } +/// buf must be SIGN_WIDTH * MAX_SCHAR_SIZE (no extra +1 needed) +size_t describe_sign_text(char *buf, schar_T *sign_text) +{ + size_t p = 0; + for (int i = 0; i < SIGN_WIDTH; i++) { + schar_get(buf + p, sign_text[i]); + size_t len = strlen(buf + p); + if (len == 0) { + break; + } + p += len; + } + return p; +} + /// Initialize the "text" for a new sign and store in "sign_text". /// "sp" is NULL for signs added through nvim_buf_set_extmark(). -int init_sign_text(sign_T *sp, char **sign_text, char *text) +int init_sign_text(sign_T *sp, schar_T *sign_text, char *text) { char *s; char *endp = text + (int)strlen(text); @@ -349,34 +376,29 @@ int init_sign_text(sign_T *sp, char **sign_text, char *text) // Count cells and check for non-printable chars int cells = 0; for (s = text; s < endp; s += utfc_ptr2len(s)) { - if (!vim_isprintc(utf_ptr2char(s))) { + int c; + sign_text[cells] = utfc_ptr2schar(s, &c); + if (!vim_isprintc(c)) { break; } - cells += utf_ptr2cells(s); + int width = utf_char2cells(c); + if (width == 2) { + sign_text[cells + 1] = 0; + } + cells += width; } // Currently must be empty, one or two display cells - if (s != endp || cells > 2) { + if (s != endp || cells > SIGN_WIDTH) { if (sp != NULL) { semsg(_("E239: Invalid sign text: %s"), text); } return FAIL; } - if (cells < 1) { - if (sp != NULL) { - sp->sn_text = NULL; - } - return OK; - } - if (sp != NULL) { - xfree(sp->sn_text); - } - // Allocate one byte more if we need to pad up with a space. - size_t len = (size_t)(endp - text + (cells == 1)); - *sign_text = xstrnsave(text, len); - - if (cells == 1) { - STRCPY(*sign_text + len - 1, " "); + if (cells < 1) { + sign_text[0] = 0; + } else if (cells == 1) { + sign_text[1] = schar_from_ascii(' '); } return OK; @@ -396,7 +418,7 @@ static int sign_define_by_name(char *name, char *icon, char *text, char *linehl, } else { // Signs may already exist, a redraw is needed in windows with a non-empty sign list. FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer->b_signs) { + if (buf_has_signs(wp->w_buffer)) { redraw_buf_later(wp->w_buffer, UPD_NOT_VALID); } } @@ -410,7 +432,7 @@ static int sign_define_by_name(char *name, char *icon, char *text, char *linehl, backslash_halve((*sp)->sn_icon); } - if (text != NULL && (init_sign_text(*sp, &(*sp)->sn_text, text) == FAIL)) { + if (text != NULL && (init_sign_text(*sp, (*sp)->sn_text, text) == FAIL)) { return FAIL; } @@ -435,7 +457,6 @@ static int sign_undefine_by_name(const char *name) } xfree(sp->sn_name); - xfree(sp->sn_text); xfree(sp->sn_icon); xfree(sp); return OK; @@ -450,9 +471,11 @@ static void sign_list_defined(sign_T *sp) msg_outtrans(sp->sn_icon, 0); msg_puts(_(" (not supported)")); } - if (sp->sn_text != NULL) { + if (sp->sn_text[0]) { msg_puts(" text="); - msg_outtrans(sp->sn_text, 0); + char buf[SIGN_WIDTH * MAX_SCHAR_SIZE]; + describe_sign_text(buf, sp->sn_text); + msg_outtrans(buf, 0); } static char *arg[] = { " linehl=", " texthl=", " culhl=", " numhl=" }; int hl[] = { sp->sn_line_hl, sp->sn_text_hl, sp->sn_cul_hl, sp->sn_num_hl }; @@ -522,7 +545,7 @@ static int sign_place(uint32_t *id, char *group, char *name, buf_T *buf, linenr_ static int sign_unplace_inner(buf_T *buf, int id, char *group, linenr_T atlnum) { - if (!buf->b_signs) { // No signs in the buffer + if (!buf_has_signs(buf)) { // No signs in the buffer return FAIL; } @@ -542,7 +565,7 @@ static int sign_unplace_inner(buf_T *buf, int id, char *group, linenr_T atlnum) // When all the signs in a buffer are removed, force recomputing the // number column width (if enabled) in all the windows displaying the // buffer if 'signcolumn' is set to 'number' in that window. - if (!buf->b_signs_with_text) { + if (!buf_meta_total(buf, kMTMetaSignText)) { may_force_numberwidth_recompute(buf, true); } @@ -706,7 +729,7 @@ static void sign_jump_cmd(buf_T *buf, linenr_T lnum, const char *name, int id, c return; } - (void)sign_jump(id, group, buf); + sign_jump(id, group, buf); } /// Parse the command line arguments for the ":sign place", ":sign unplace" and @@ -718,7 +741,7 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **name, int *id, char ** { char *arg1 = arg; char *filename = NULL; - int lnum_arg = false; + bool lnum_arg = false; // first arg could be placed sign id if (ascii_isdigit(*arg)) { @@ -787,7 +810,7 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **name, int *id, char ** } if (filename != NULL && *buf == NULL) { - semsg(_("E158: Invalid buffer name: %s"), filename); + semsg(_(e_invalid_buffer_name_str), filename); return FAIL; } @@ -879,8 +902,10 @@ static dict_T *sign_get_info_dict(sign_T *sp) if (sp->sn_icon != NULL) { tv_dict_add_str(d, S_LEN("icon"), sp->sn_icon); } - if (sp->sn_text != NULL) { - tv_dict_add_str(d, S_LEN("text"), sp->sn_text); + if (sp->sn_text[0]) { + char buf[SIGN_WIDTH * MAX_SCHAR_SIZE]; + describe_sign_text(buf, sp->sn_text); + tv_dict_add_str(d, S_LEN("text"), buf); } static char *arg[] = { "linehl", "texthl", "culhl", "numhl" }; int hl[] = { sp->sn_line_hl, sp->sn_text_hl, sp->sn_cul_hl, sp->sn_num_hl }; @@ -910,7 +935,7 @@ static dict_T *sign_get_placed_info_dict(MTKey mark) /// Returns information about signs placed in a buffer as list of dicts. list_T *get_buffer_signs(buf_T *buf) - FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { list_T *const l = tv_list_alloc(kListLenMayKnow); MarkTreeIter itr[1]; @@ -940,7 +965,7 @@ static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const tv_dict_add_list(d, S_LEN("signs"), l); int64_t ns = group_get_ns(group); - if (!buf->b_signs || ns < 0) { + if (!buf_has_signs(buf) || ns < 0) { return; } @@ -967,7 +992,7 @@ static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const } if (kv_size(signs)) { - qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp); + qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_row_cmp); for (size_t i = 0; i < kv_size(signs); i++) { tv_list_append_dict(l, sign_get_placed_info_dict(kv_A(signs, i))); } @@ -984,7 +1009,7 @@ static void sign_get_placed(buf_T *buf, linenr_T lnum, int id, const char *group sign_get_placed_in_buf(buf, lnum, id, group, retlist); } else { FOR_ALL_BUFFERS(cbuf) { - if (cbuf->b_signs) { + if (buf_has_signs(cbuf)) { sign_get_placed_in_buf(cbuf, 0, id, group, retlist); } } @@ -994,9 +1019,14 @@ static void sign_get_placed(buf_T *buf, linenr_T lnum, int id, const char *group void free_signs(void) { cstr_t name; + kvec_t(cstr_t) names = KV_INITIAL_VALUE; map_foreach_key(&sign_map, name, { - sign_undefine_by_name(name); + kv_push(names, name); }); + for (size_t i = 0; i < kv_size(names); i++) { + sign_undefine_by_name(kv_A(names, i)); + } + kv_destroy(names); } static enum { diff --git a/src/nvim/sign.h b/src/nvim/sign.h index 1c607bc7de..c6d9e1002c 100644 --- a/src/nvim/sign.h +++ b/src/nvim/sign.h @@ -1,9 +1,8 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep -#include "nvim/sign_defs.h" // IWYU pragma: export +#include "nvim/sign_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index 79d21585fc..ad2a2b737d 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -1,22 +1,29 @@ #pragma once +#include "nvim/decoration_defs.h" +#include "nvim/types_defs.h" + /// Sign attributes. Used by the screen refresh routines. typedef struct { - char *text; + schar_T text[SIGN_WIDTH]; int hl_id; } SignTextAttrs; /// Struct to hold the sign properties. -typedef struct sign { +typedef struct { char *sn_name; // name of sign char *sn_icon; // name of pixmap - char *sn_text; // text used instead of pixmap + schar_T sn_text[SIGN_WIDTH]; // text used instead of pixmap int sn_line_hl; // highlight ID for line int sn_text_hl; // highlight ID for text int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set int sn_num_hl; // highlight ID for line number } sign_T; -enum { SIGN_WIDTH = 2, }; ///< Number of display cells for a sign in the signcolumn +typedef struct { + DecorSignHighlight *sh; + uint32_t id; +} SignItem; + enum { SIGN_SHOW_MAX = 9, }; ///< Maximum number of signs shown on a single line enum { SIGN_DEF_PRIO = 10, }; ///< Default sign highlight priority diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 905f5c25b4..c2091c6bae 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -63,7 +63,9 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -73,27 +75,32 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/highlight_defs.h" #include "nvim/insexpand.h" #include "nvim/log.h" #include "nvim/macros_defs.h" -#include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/search.h" #include "nvim/spell.h" @@ -124,7 +131,7 @@ slang_T *first_lang = NULL; char *int_wordlist = NULL; // Structure to store info for word matching. -typedef struct matchinf_S { +typedef struct { langp_T *mi_lp; // info for language and region // pointers to original text to be checked @@ -163,20 +170,20 @@ typedef struct matchinf_S { } matchinf_T; // Structure used for the cookie argument of do_in_runtimepath(). -typedef struct spelload_S { +typedef struct { char sl_lang[MAXWLEN + 1]; // language name slang_T *sl_slang; // resulting slang_T struct int sl_nobreak; // NOBREAK language found } spelload_T; #define SY_MAXLEN 30 -typedef struct syl_item_S { +typedef struct { char sy_chars[SY_MAXLEN]; // the sequence of chars int sy_len; } syl_item_T; spelltab_T spelltab; -int did_set_spelltab; +bool did_set_spelltab; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "spell.c.generated.h" @@ -297,8 +304,8 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount MB_PTR_ADV(mi.mi_fend); } - (void)spell_casefold(wp, ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, - MAXWLEN + 1); + spell_casefold(wp, ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, + MAXWLEN + 1); mi.mi_fwordlen = (int)strlen(mi.mi_fword); if (is_camel_case && mi.mi_fwordlen > 0) { @@ -363,7 +370,7 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount // Check for end of sentence. regmatch.regprog = wp->w_s->b_cap_prog; regmatch.rm_ic = false; - int r = vim_regexec(®match, ptr, 0); + bool r = vim_regexec(®match, ptr, 0); wp->w_s->b_cap_prog = regmatch.regprog; if (r) { *capcol = (int)(regmatch.endp[0] - ptr); @@ -802,7 +809,7 @@ static void find_word(matchinf_T *mip, int mode) if (slang->sl_compsylmax < MAXWLEN) { // "fword" is only needed for checking syllables. if (ptr == mip->mi_word) { - (void)spell_casefold(mip->mi_win, ptr, wlen, fword, MAXWLEN); + spell_casefold(mip->mi_win, ptr, wlen, fword, MAXWLEN); } else { xstrlcpy(fword, ptr, (size_t)endlen[endidxcnt] + 1); } @@ -1212,9 +1219,9 @@ static int fold_more(matchinf_T *mip) MB_PTR_ADV(mip->mi_fend); } - (void)spell_casefold(mip->mi_win, p, (int)(mip->mi_fend - p), - mip->mi_fword + mip->mi_fwordlen, - MAXWLEN - mip->mi_fwordlen); + spell_casefold(mip->mi_win, p, (int)(mip->mi_fend - p), + mip->mi_fword + mip->mi_fwordlen, + MAXWLEN - mip->mi_fwordlen); int flen = (int)strlen(mip->mi_fword + mip->mi_fwordlen); mip->mi_fwordlen += flen; return flen; @@ -1272,7 +1279,7 @@ static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_ln static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col) { bool can_spell; - (void)syn_get_id(wp, lnum, col, false, &can_spell, false); + syn_get_id(wp, lnum, col, false, &can_spell, false); return can_spell; } @@ -1293,7 +1300,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att size_t found_len = 0; hlf_T attr = HLF_COUNT; size_t len; - int has_syntax = syntax_present(wp); + bool has_syntax = syntax_present(wp); colnr_T col; char *buf = NULL; size_t buflen = 0; @@ -2029,7 +2036,7 @@ char *parse_spelllang(win_T *wp) // If not found try loading the language now. if (slang == NULL) { if (filename) { - (void)spell_load_file(lang, lang, NULL, false); + spell_load_file(lang, lang, NULL, false); } else { spell_load_lang(lang); // SpellFileMissing autocommands may do anything, including @@ -2374,7 +2381,7 @@ void spell_reload(void) // window for this buffer in which 'spell' is set. if (*wp->w_s->b_p_spl != NUL) { if (wp->w_p_spell) { - (void)parse_spelllang(wp); + parse_spelllang(wp); break; } } @@ -2834,7 +2841,7 @@ void spell_soundfold(slang_T *slang, char *inword, bool folded, char *res) if (folded) { word = inword; } else { - (void)spell_casefold(curwin, inword, (int)strlen(inword), fword, MAXWLEN); + spell_casefold(curwin, inword, (int)strlen(inword), fword, MAXWLEN); word = fword; } @@ -3219,14 +3226,14 @@ void ex_spelldump(exarg_T *eap) if (no_spell_checking(curwin)) { return; } - OptVal spl = get_option_value("spl", NULL, OPT_LOCAL, NULL); + OptVal spl = get_option_value(kOptSpelllang, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value_give_err("spell", BOOLEAN_OPTVAL(true), OPT_LOCAL); - set_option_value_give_err("spl", spl, OPT_LOCAL); + set_option_value_give_err(kOptSpell, BOOLEAN_OPTVAL(true), OPT_LOCAL); + set_option_value_give_err(kOptSpelllang, spl, OPT_LOCAL); optval_free(spl); if (!buf_is_empty(curbuf)) { diff --git a/src/nvim/spell.h b/src/nvim/spell.h index f3977fdaf2..adbdd3705e 100644 --- a/src/nvim/spell.h +++ b/src/nvim/spell.h @@ -3,9 +3,8 @@ #include <stdbool.h> #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep -#include "nvim/globals.h" -#include "nvim/spell_defs.h" // IWYU pragma: export -#include "nvim/vim_defs.h" +#include "nvim/spell_defs.h" // IWYU pragma: keep +#include "nvim/vim_defs.h" // IWYU pragma: keep /// First language that is loaded, start of the linked list of loaded languages. extern slang_T *first_lang; @@ -14,7 +13,7 @@ extern slang_T *first_lang; extern char *int_wordlist; extern spelltab_T spelltab; -extern int did_set_spelltab; +extern bool did_set_spelltab; extern char *e_format; diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h index dfa399750f..005f3aa9e4 100644 --- a/src/nvim/spell_defs.h +++ b/src/nvim/spell_defs.h @@ -4,16 +4,14 @@ #include <stdint.h> #include "nvim/buffer_defs.h" -#include "nvim/garray_defs.h" -#include "nvim/hashtab_defs.h" -#include "nvim/regexp_defs.h" -/// Assume max. word len is this many bytes. -/// Some places assume a word length fits in a byte, thus it can't be above 255. -enum { MAXWLEN = 254, }; +enum { + /// Assume max. word len is this many bytes. + /// Some places assume a word length fits in a byte, thus it can't be above 255. + MAXWLEN = 254, +}; -/// Number of regions supported. -enum { MAXREGIONS = 8, }; +enum { MAXREGIONS = 8, }; ///< Number of regions supported. /// Type used for indexes in the word tree need to be at least 4 bytes. If int /// is 8 bytes we could use something smaller, but what? @@ -77,7 +75,7 @@ enum { /// Info from "REP", "REPSAL" and "SAL" entries in ".aff" file used in si_rep, /// si_repsal, sl_rep, and si_sal. Not for sl_sal! /// One replacement: from "ft_from" to "ft_to". -typedef struct fromto_S { +typedef struct { char *ft_from; char *ft_to; } fromto_T; @@ -85,7 +83,7 @@ typedef struct fromto_S { /// Info from "SAL" entries in ".aff" file used in sl_sal. /// The info is split for quick processing by spell_soundfold(). /// Note that "sm_oneof" and "sm_rules" point into sm_lead. -typedef struct salitem_S { +typedef struct { char *sm_lead; ///< leading letters int sm_leadlen; ///< length of "sm_lead" char *sm_oneof; ///< letters from () or NULL @@ -191,7 +189,7 @@ struct slang_S { }; /// Structure used in "b_langp", filled from 'spelllang'. -typedef struct langp_S { +typedef struct { slang_T *lp_slang; ///< info for this language slang_T *lp_sallang; ///< language used for sound folding or NULL slang_T *lp_replang; ///< language used for REP items or NULL @@ -215,18 +213,6 @@ typedef struct { uint8_t st_upper[256]; ///< chars: upper case } spelltab_T; -// Use our own character-case definitions, because the current locale may -// differ from what the .spl file uses. -// These must not be called with negative number! -// Multi-byte implementation. For Unicode we can call utf_*(), but don't do -// that for ASCII, because we don't want to use 'casemap' here. Otherwise use -// the "w" library function for characters above 255. -#define SPELL_TOFOLD(c) ((c) >= 128 ? utf_fold(c) : (int)spelltab.st_fold[c]) - -#define SPELL_TOUPPER(c) ((c) >= 128 ? mb_toupper(c) : (int)spelltab.st_upper[c]) - -#define SPELL_ISUPPER(c) ((c) >= 128 ? mb_isupper(c) : spelltab.st_isu[c]) - /// Values for "what" argument of spell_add_word() typedef enum { SPELL_ADD_GOOD = 0, @@ -234,7 +220,7 @@ typedef enum { SPELL_ADD_RARE = 2, } SpellAddType; -typedef struct wordcount_S { +typedef struct { uint16_t wc_count; ///< nr of times word was seen char wc_word[]; ///< word } wordcount_T; diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 2607fddc31..1c632d2700 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -237,34 +237,42 @@ #include "nvim/arglist.h" #include "nvim/ascii_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" #include "nvim/ex_cmds_defs.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/time.h" +#include "nvim/os/time_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/spell.h" #include "nvim/spell_defs.h" #include "nvim/spellfile.h" #include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" @@ -334,7 +342,7 @@ static const char *msg_compressing = N_("Compressing word tree..."); #define MAXLINELEN 500 // Maximum length in bytes of a line in a .aff // and .dic file. // Main structure to store the contents of a ".aff" file. -typedef struct afffile_S { +typedef struct { char *af_enc; // "SET", normalized, alloc'ed string or NULL int af_flagtype; // AFT_CHAR, AFT_LONG, AFT_NUM or AFT_CAPLONG unsigned af_rare; // RARE ID for rare word @@ -376,7 +384,7 @@ struct affentry_S { #define AH_KEY_LEN 17 // 2 x 8 bytes + NUL // Affix header from ".aff" file. Used for af_pref and af_suff. -typedef struct affheader_S { +typedef struct { char ah_key[AH_KEY_LEN]; // key for hashtab == name of affix unsigned ah_flag; // affix name as number, uses "af_flagtype" int ah_newID; // prefix ID after renumbering; 0 if not used @@ -388,7 +396,7 @@ typedef struct affheader_S { #define HI2AH(hi) ((affheader_T *)(hi)->hi_key) // Flag used in compound items. -typedef struct compitem_S { +typedef struct { char ci_key[AH_KEY_LEN]; // key for hashtab == name of compound unsigned ci_flag; // affix name as number, uses "af_flagtype" int ci_newID; // affix ID after renumbering. @@ -449,7 +457,7 @@ struct wordnode_S { #define HI2WN(hi) (wordnode_T *)((hi)->hi_key) // Info used while reading the spell files. -typedef struct spellinfo_S { +typedef struct { wordnode_T *si_foldroot; // tree with case-folded words int si_foldwcount; // nr of words in si_foldroot @@ -574,7 +582,7 @@ static inline int spell_check_magic_string(FILE *const fd) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE { char buf[VIMSPELLMAGICL]; - SPELL_READ_BYTES(buf, VIMSPELLMAGICL, fd,; ); // NOLINT(whitespace/parens) + SPELL_READ_BYTES(buf, VIMSPELLMAGICL, fd,; ); if (memcmp(buf, VIMSPELLMAGIC, VIMSPELLMAGICL) != 0) { return SP_FORMERROR; } @@ -1036,7 +1044,7 @@ static int read_region_section(FILE *fd, slang_T *lp, int len) if (len > MAXREGIONS * 2) { return SP_FORMERROR; } - SPELL_READ_NONNUL_BYTES(lp->sl_regions, (size_t)len, fd,; ); // NOLINT(whitespace/parens) + SPELL_READ_NONNUL_BYTES(lp->sl_regions, (size_t)len, fd,; ); lp->sl_regions[len] = NUL; return 0; } @@ -1101,7 +1109,7 @@ static int read_prefcond_section(FILE *fd, slang_T *lp) if (n > 0) { char buf[MAXWLEN + 1]; buf[0] = '^'; // always match at one position only - SPELL_READ_NONNUL_BYTES(buf + 1, (size_t)n, fd,; ); // NOLINT(whitespace/parens) + SPELL_READ_NONNUL_BYTES(buf + 1, (size_t)n, fd,; ); buf[n + 1] = NUL; lp->sl_prefprog[i] = vim_regcomp(buf, RE_MAGIC | RE_STRING); } @@ -1536,7 +1544,7 @@ static int set_sofo(slang_T *lp, const char *from, const char *to) // sl_sal_first[] for this. for (p = from, s = to; *p != NUL && *s != NUL;) { const int c = mb_cptr2char_adv(&p); - MB_CPTR_ADV(s); + s += utf_ptr2len(s); if (c >= 256) { lp->sl_sal_first[c & 0xff]++; } @@ -2015,16 +2023,16 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char *fname) spell_message(spin, IObuff); // Only do REP lines when not done in another .aff file already. - int do_rep = GA_EMPTY(&spin->si_rep); + bool do_rep = GA_EMPTY(&spin->si_rep); // Only do REPSAL lines when not done in another .aff file already. - int do_repsal = GA_EMPTY(&spin->si_repsal); + bool do_repsal = GA_EMPTY(&spin->si_repsal); // Only do SAL lines when not done in another .aff file already. - int do_sal = GA_EMPTY(&spin->si_sal); + bool do_sal = GA_EMPTY(&spin->si_sal); // Only do MAP lines when not done in another .aff file already. - int do_mapline = GA_EMPTY(&spin->si_map); + bool do_mapline = GA_EMPTY(&spin->si_map); // Allocate and init the afffile_T structure. afffile_T *aff = getroom(spin, sizeof(*aff), true); @@ -2967,9 +2975,9 @@ static void add_fromto(spellinfo_T *spin, garray_T *gap, char *from, char *to) char word[MAXWLEN]; fromto_T *ftp = GA_APPEND_VIA_PTR(fromto_T, gap); - (void)spell_casefold(curwin, from, (int)strlen(from), word, MAXWLEN); + spell_casefold(curwin, from, (int)strlen(from), word, MAXWLEN); ftp->ft_from = getroom_save(spin, word); - (void)spell_casefold(curwin, to, (int)strlen(to), word, MAXWLEN); + spell_casefold(curwin, to, (int)strlen(to), word, MAXWLEN); ftp->ft_to = getroom_save(spin, word); } @@ -3614,11 +3622,9 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname) smsg(0, _("/encoding= line after word ignored in %s line %" PRIdLINENR ": %s"), fname, lnum, line - 1); } else { - char *enc; - // Setup for conversion to 'encoding'. line += 9; - enc = enc_canonize(line); + char *enc = enc_canonize(line); if (!spin->si_ascii && convert_setup(&spin->si_conv, enc, p_enc) == FAIL) { smsg(0, _("Conversion in %s not supported: from %s to %s"), @@ -3820,7 +3826,7 @@ static int store_word(spellinfo_T *spin, char *word, int flags, int region, cons return FAIL; } - (void)spell_casefold(curwin, word, len, foldword, MAXWLEN); + spell_casefold(curwin, word, len, foldword, MAXWLEN); for (const char *p = pfxlist; res == OK; p++) { if (!need_affix || (p != NULL && *p != NUL)) { res = tree_add_word(spin, foldword, spin->si_foldroot, ct | flags, @@ -3980,7 +3986,7 @@ static int tree_add_word(spellinfo_T *spin, const char *word, wordnode_T *root, // (si_compress_cnt == 1) and the number of free nodes drops below the // maximum word length. #ifndef SPELL_COMPRESS_ALWAYS - if (spin->si_compress_cnt == 1 // NOLINT(readability/braces) + if (spin->si_compress_cnt == 1 ? spin->si_free_count < MAXWLEN : spin->si_blocks_cnt >= compress_start) #endif @@ -4593,7 +4599,7 @@ static int write_vim_spell(spellinfo_T *spin, char *fname) spin->si_memtot += (int)(nodecount + nodecount * sizeof(int)); // Write the nodes. - (void)put_node(fd, tree, 0, regionmask, round == 3); + put_node(fd, tree, 0, regionmask, round == 3); } // Write another byte to check for errors (file system full). @@ -5098,7 +5104,7 @@ static void sug_write(spellinfo_T *spin, char *fname) spin->si_memtot += (int)(nodecount + nodecount * sizeof(int)); // Write the nodes. - (void)put_node(fd, tree, 0, 0, false); + put_node(fd, tree, 0, 0, false); // <SUGTABLE>: <sugwcount> <sugline> ... linenr_T wcount = spin->si_spellbuf->b_ml.ml_line_count; @@ -5601,7 +5607,7 @@ static void init_spellfile(void) && strstr(path_tail(fname), ".ascii.") != NULL) ? "ascii" : spell_enc())); - set_option_value_give_err("spellfile", CSTR_AS_OPTVAL(buf), OPT_LOCAL); + set_option_value_give_err(kOptSpellfile, CSTR_AS_OPTVAL(buf), OPT_LOCAL); break; } aspath = false; @@ -5638,7 +5644,7 @@ static void set_spell_charflags(const char *flags_in, int cnt, const char *fol) } } - (void)set_spell_finish(&new_st); + set_spell_finish(&new_st); } static int set_spell_finish(spelltab_T *new_st) diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index d9dd28527e..5c0e295f88 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -16,18 +16,20 @@ #include "nvim/cursor.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/highlight_defs.h" #include "nvim/input.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -36,14 +38,17 @@ #include "nvim/option_vars.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" +#include "nvim/os/os_defs.h" #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/spell.h" +#include "nvim/spell_defs.h" #include "nvim/spellfile.h" #include "nvim/spellsuggest.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" @@ -63,7 +68,7 @@ #define WF_MIXCAP 0x20 // mix of upper and lower case: macaRONI /// Information used when looking for suggestions. -typedef struct suginfo_S { +typedef struct { garray_T su_ga; ///< suggestions, contains "suggest_T" int su_maxcount; ///< max. number of suggestions displayed int su_maxscore; ///< maximum score for adding to su_ga @@ -186,7 +191,7 @@ typedef enum { } state_T; /// Struct to keep the state at each level in suggest_try_change(). -typedef struct trystate_S { +typedef struct { state_T ts_state; ///< state at this level, STATE_ int ts_score; ///< score idx_T ts_arridx; ///< index in tree array, start of node @@ -442,7 +447,7 @@ void spell_suggest(int count) char wcopy[MAXWLEN + 2]; suginfo_T sug; suggest_T *stp; - int mouse_used; + bool mouse_used; int limit; int selected = count; int badlen = 0; @@ -730,8 +735,8 @@ static void spell_find_suggest(char *badptr, int badlen, suginfo_T *su, int maxc su->su_badlen = MAXWLEN - 1; // just in case } xstrlcpy(su->su_badword, su->su_badptr, (size_t)su->su_badlen + 1); - (void)spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword, - MAXWLEN); + spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword, + MAXWLEN); // TODO(vim): make this work if the case-folded text is longer than the // original text. Currently an illegal byte causes wrong pointer @@ -845,7 +850,7 @@ static void spell_suggest_expr(suginfo_T *su, char *expr) // Remove bogus suggestions, sort and truncate at "maxcount". check_suggestions(su, &su->su_ga); - (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); + cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); } /// Find suggestions in file "fname". Used for "file:" in 'spellsuggest'. @@ -892,7 +897,7 @@ static void spell_suggest_file(suginfo_T *su, char *fname) // Remove bogus suggestions, sort and truncate at "maxcount". check_suggestions(su, &su->su_ga); - (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); + cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); } /// Find suggestions for the internal method indicated by "sps_flags". @@ -956,7 +961,7 @@ static void spell_suggest_intern(suginfo_T *su, bool interactive) // got_int when using a command, not for spellsuggest(). os_breakcheck(); if (interactive && got_int) { - (void)vgetc(); + vgetc(); got_int = false; } @@ -968,7 +973,7 @@ static void spell_suggest_intern(suginfo_T *su, bool interactive) // Remove bogus suggestions, sort and truncate at "maxcount". check_suggestions(su, &su->su_ga); - (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); + cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); } } @@ -1064,7 +1069,7 @@ static void suggest_try_change(suginfo_T *su) STRCPY(fword, su->su_fbadword); int n = (int)strlen(fword); char *p = su->su_badptr + su->su_badlen; - (void)spell_casefold(curwin, p, (int)strlen(p), fword + n, MAXWLEN - n); + spell_casefold(curwin, p, (int)strlen(p), fword + n, MAXWLEN - n); // Make sure the resulting text is not longer than the original text. n = (int)strlen(su->su_badptr); @@ -1272,10 +1277,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char *fword, bool soun break; } - int fword_ends = (fword[sp->ts_fidx] == NUL - || (soundfold - ? ascii_iswhite(fword[sp->ts_fidx]) - : !spell_iswordp(fword + sp->ts_fidx, curwin))); + bool fword_ends = (fword[sp->ts_fidx] == NUL + || (soundfold + ? ascii_iswhite(fword[sp->ts_fidx]) + : !spell_iswordp(fword + sp->ts_fidx, curwin))); tword[sp->ts_twordlen] = NUL; if (sp->ts_prefixdepth <= PFD_NOTSPECIAL @@ -1942,6 +1947,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char *fword, bool soun // - Skip the byte if it's equal to the byte in the word, // accepting that byte is always better. n += sp->ts_curi++; + + // break out, if we would be accessing byts buffer out of bounds + if (byts == slang->sl_fbyts && n >= slang->sl_fbyts_len) { + got_int = true; + break; + } c = byts[n]; if (soundfold && sp->ts_twordlen == 0 && c == '*') { // Inserting a vowel at the start of a word counts less, @@ -2532,8 +2543,8 @@ static void score_combine(suginfo_T *su) } if (slang == NULL) { // Using "double" without sound folding. - (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, - su->su_maxcount); + cleanup_suggestions(&su->su_ga, su->su_maxscore, + su->su_maxcount); return; } @@ -2552,9 +2563,9 @@ static void score_combine(suginfo_T *su) // Remove bad suggestions, sort the suggestions and truncate at "maxcount" // for both lists. check_suggestions(su, &su->su_ga); - (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); + cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); check_suggestions(su, &su->su_sga); - (void)cleanup_suggestions(&su->su_sga, su->su_maxscore, su->su_maxcount); + cleanup_suggestions(&su->su_sga, su->su_maxscore, su->su_maxcount); ga_init(&ga, (int)sizeof(suginfo_T), 1); ga_grow(&ga, su->su_ga.ga_len + su->su_sga.ga_len); @@ -2615,7 +2626,7 @@ static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char *ba pbad = badsound; } else { // soundfold the bad word with more characters following - (void)spell_casefold(curwin, su->su_badptr, stp->st_orglen, fword, MAXWLEN); + spell_casefold(curwin, su->su_badptr, stp->st_orglen, fword, MAXWLEN); // When joining two words the sound often changes a lot. E.g., "t he" // sounds like "t h" while "the" sounds like "@". Avoid that by @@ -3142,7 +3153,7 @@ static void check_suggestions(suginfo_T *su, garray_T *gap) int len = stp[i].st_wordlen; xstrlcpy(longword + len, su->su_badptr + stp[i].st_orglen, MAXWLEN + 1 - (size_t)len); hlf_T attr = HLF_COUNT; - (void)spell_check(curwin, longword, &attr, NULL, false); + spell_check(curwin, longword, &attr, NULL, false); if (attr != HLF_COUNT) { // Remove this entry. xfree(stp[i].st_word); @@ -3210,10 +3221,10 @@ static int sug_compare(const void *s1, const void *s2) { suggest_T *p1 = (suggest_T *)s1; suggest_T *p2 = (suggest_T *)s2; - int n = p1->st_score - p2->st_score; + int n = p1->st_score == p2->st_score ? 0 : p1->st_score > p2->st_score ? 1 : -1; if (n == 0) { - n = p1->st_altscore - p2->st_altscore; + n = p1->st_altscore == p2->st_altscore ? 0 : p1->st_altscore > p2->st_altscore ? 1 : -1; if (n == 0) { n = STRICMP(p1->st_word, p2->st_word); } diff --git a/src/nvim/state.c b/src/nvim/state.c index 900eac0826..0df060ecf4 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -3,10 +3,14 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/event/defs.h" +#include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/ex_getln.h" #include "nvim/getchar.h" @@ -26,10 +30,11 @@ #include "nvim/ui.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "state.c.generated.h" // IWYU pragma: export +# include "state.c.generated.h" #endif void state_enter(VimState *s) + FUNC_ATTR_NONNULL_ALL { while (true) { int check_result = s->check ? s->check(s) : 1; @@ -72,9 +77,9 @@ getkey: // Call `os_inchar` directly to block for events or user input without // consuming anything from `input_buffer`(os/input.c) or calling the // mapping engine. - (void)os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events); + os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events); // If an event was put into the queue, we send K_EVENT directly. - if (!multiqueue_empty(main_loop.events)) { + if (!input_available() && !multiqueue_empty(main_loop.events)) { key = K_EVENT; } else { goto getkey; @@ -168,6 +173,7 @@ int get_real_state(void) /// The first character represents the major mode, the following ones the minor /// ones. void get_mode(char *buf) + FUNC_ATTR_NONNULL_ALL { int i = 0; diff --git a/src/nvim/state.h b/src/nvim/state.h index 8c5957bf9a..9002f018d2 100644 --- a/src/nvim/state.h +++ b/src/nvim/state.h @@ -1,6 +1,6 @@ #pragma once -#include "nvim/state_defs.h" // IWYU pragma: export +#include "nvim/state_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "state.h.generated.h" diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 4dac1b1451..b403f840ca 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -18,29 +18,37 @@ #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/normal.h" #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/sign.h" +#include "nvim/sign_defs.h" #include "nvim/state_defs.h" #include "nvim/statusline.h" #include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/window.h" @@ -59,7 +67,6 @@ typedef enum { /// If inversion is possible we use it. Else '=' characters are used. void win_redr_status(win_T *wp) { - int fillchar; int attr; bool is_stl_global = global_stl_height() > 0; static bool busy = false; @@ -85,7 +92,7 @@ void win_redr_status(win_T *wp) // redraw custom status line redraw_custom_statusline(wp); } else { - fillchar = fillchar_status(&attr, wp); + schar_T fillchar = fillchar_status(&attr, wp); const int stl_width = is_stl_global ? Columns : wp->w_width; get_trans_bufname(wp->w_buffer); @@ -144,14 +151,14 @@ void win_redr_status(win_T *wp) } grid_line_start(&default_grid, is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp)); - int col = is_stl_global ? 0 : wp->w_wincol; + const int off = is_stl_global ? 0 : wp->w_wincol; - int width = grid_line_puts(col, p, -1, attr); - grid_line_fill(width + col, this_ru_col + col, fillchar, attr); + int width = grid_line_puts(off, p, -1, attr); + grid_line_fill(off + width, off + this_ru_col, fillchar, attr); if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL) - && this_ru_col - len > (int)(strlen(NameBuff) + 1)) { - grid_line_puts((int)((size_t)this_ru_col - strlen(NameBuff) - 1), NameBuff, -1, attr); + && this_ru_col - len > (int)strlen(NameBuff) + 1) { + grid_line_puts(off + this_ru_col - (int)strlen(NameBuff) - 1, NameBuff, -1, attr); } win_redr_ruler(wp); @@ -161,7 +168,7 @@ void win_redr_status(win_T *wp) const int sc_width = MIN(10, this_ru_col - len - 2); if (sc_width > 0) { - grid_line_puts(wp->w_wincol + this_ru_col - sc_width - 1, showcmd_buf, sc_width, attr); + grid_line_puts(off + this_ru_col - sc_width - 1, showcmd_buf, sc_width, attr); } } @@ -170,6 +177,7 @@ void win_redr_status(win_T *wp) // May need to draw the character below the vertical separator. if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) { + schar_T fillchar; if (stl_connected(wp)) { fillchar = fillchar_status(&attr, wp); } else { @@ -177,7 +185,7 @@ void win_redr_status(win_T *wp) fillchar = wp->w_p_fcs_chars.vert; } grid_line_start(&default_grid, W_ENDROW(wp)); - grid_line_put_schar(W_ENDCOL(wp), schar_from_char(fillchar), attr); + grid_line_put_schar(W_ENDCOL(wp), fillchar, attr); grid_line_flush(); } busy = false; @@ -292,11 +300,11 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) int row; int col = 0; int maxwidth; - int fillchar; + schar_T fillchar; char buf[MAXPATHL]; char transbuf[MAXPATHL]; char *stl; - char *opt_name; + OptIndex opt_idx = kOptInvalid; int opt_scope = 0; stl_hlrec_t *hltab; StlClickRecord *tabtab; @@ -317,12 +325,12 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) // Use 'tabline'. Always at the first line of the screen. stl = p_tal; row = 0; - fillchar = ' '; + fillchar = schar_from_ascii(' '); attr = HL_ATTR(HLF_TPF); maxwidth = Columns; - opt_name = "tabline"; + opt_idx = kOptTabline; } else if (draw_winbar) { - opt_name = "winbar"; + opt_idx = kOptWinbar; stl = ((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr); opt_scope = ((*wp->w_p_wbr != NUL) ? OPT_LOCAL : 0); row = -1; // row zero is first row of text @@ -351,7 +359,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) if (draw_ruler) { stl = p_ruf; - opt_name = "rulerformat"; + opt_idx = kOptRulerformat; // advance past any leading group spec - implicit in ru_col if (*stl == '%') { if (*++stl == '-') { @@ -375,11 +383,11 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) grid = &msg_grid_adj; row = Rows - 1; maxwidth--; // writing in last column may cause scrolling - fillchar = ' '; + fillchar = schar_from_ascii(' '); attr = HL_ATTR(HLF_MSG); } } else { - opt_name = "statusline"; + opt_idx = kOptStatusline; stl = ((*wp->w_p_stl != NUL) ? wp->w_p_stl : p_stl); opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0); } @@ -402,8 +410,8 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) // Make a copy, because the statusline may include a function call that // might change the option value and free the memory. stl = xstrdup(stl); - build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope, - fillchar, maxwidth, &hltab, &tabtab, NULL); + build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_idx, opt_scope, + fillchar, maxwidth, &hltab, NULL, &tabtab, NULL); xfree(stl); ewp->w_p_crb = p_crb_save; @@ -514,7 +522,7 @@ void win_redr_ruler(win_T *wp) && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) == NUL; int width; - int fillchar; + schar_T fillchar; int attr; int off; bool part_of_status = false; @@ -530,7 +538,7 @@ void win_redr_ruler(win_T *wp) width = Columns; part_of_status = true; } else { - fillchar = ' '; + fillchar = schar_from_ascii(' '); attr = HL_ATTR(HLF_MSG); width = Columns; off = 0; @@ -578,7 +586,7 @@ void win_redr_ruler(win_T *wp) if (this_ru_col + o < width) { // Need at least 3 chars left for get_rel_pos() + NUL. while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) { - i += utf_char2bytes(fillchar, buffer + i); + i += (int)schar_get(buffer + i, fillchar); o++; } get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i); @@ -607,24 +615,21 @@ void win_redr_ruler(win_T *wp) } } - int w = grid_line_puts(this_ru_col + off, buffer, -1, attr); - grid_line_fill(this_ru_col + off + w, off + width, fillchar, attr); + int w = grid_line_puts(off + this_ru_col, buffer, -1, attr); + grid_line_fill(off + this_ru_col + w, off + width, fillchar, attr); } } /// Get the character to use in a status line. Get its attributes in "*attr". -int fillchar_status(int *attr, win_T *wp) +schar_T fillchar_status(int *attr, win_T *wp) { - int fill; - bool is_curwin = (wp == curwin); - if (is_curwin) { + if (wp == curwin) { *attr = win_hl_attr(wp, HLF_S); - fill = wp->w_p_fcs_chars.stl; + return wp->w_p_fcs_chars.stl; } else { *attr = win_hl_attr(wp, HLF_SNC); - fill = wp->w_p_fcs_chars.stlnc; + return wp->w_p_fcs_chars.stlnc; } - return fill; } /// Redraw the status line according to 'statusline' and take care of any @@ -660,7 +665,7 @@ static void ui_ext_tabline_update(void) win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin; get_trans_bufname(cwp->w_buffer); - PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string(NameBuff)))); + PUT_C(tab_info, "name", CSTR_TO_ARENA_OBJ(&arena, NameBuff)); ADD_C(tabs, DICTIONARY_OBJ(tab_info)); } @@ -681,7 +686,7 @@ static void ui_ext_tabline_update(void) PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); get_trans_bufname(buf); - PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string(NameBuff)))); + PUT_C(buffer_info, "name", CSTR_TO_ARENA_OBJ(&arena, NameBuff)); ADD_C(buffers, DICTIONARY_OBJ(buffer_info)); } @@ -696,7 +701,7 @@ void draw_tabline(void) win_T *wp; int attr_nosel = HL_ATTR(HLF_TP); int attr_fill = HL_ATTR(HLF_TPF); - int use_sep_chars = (t_colors < 8); + bool use_sep_chars = (t_colors < 8); if (default_grid.chars == NULL) { return; @@ -725,9 +730,6 @@ void draw_tabline(void) int col = 0; win_T *cwp; int wincount; - int c; - int len; - char *p; grid_line_start(&default_grid, 0); FOR_ALL_TABS(tp) { tabcount++; @@ -772,7 +774,7 @@ void draw_tabline(void) grid_line_put_schar(col++, schar_from_ascii(' '), attr); - int modified = false; + bool modified = false; for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) { if (bufIsChanged(wp->w_buffer)) { @@ -783,7 +785,7 @@ void draw_tabline(void) if (modified || wincount > 1) { if (wincount > 1) { vim_snprintf(NameBuff, MAXPATHL, "%d", wincount); - len = (int)strlen(NameBuff); + int len = (int)strlen(NameBuff); if (col + len >= Columns - 3) { break; } @@ -802,8 +804,8 @@ void draw_tabline(void) // Get buffer name in NameBuff[] get_trans_bufname(cwp->w_buffer); shorten_dir(NameBuff); - len = vim_strsize(NameBuff); - p = NameBuff; + int len = vim_strsize(NameBuff); + char *p = NameBuff; while (len > room) { len -= ptr2cells(p); MB_PTR_ADV(p); @@ -829,12 +831,8 @@ void draw_tabline(void) } } - if (use_sep_chars) { - c = '_'; - } else { - c = ' '; - } - grid_line_fill(col, Columns, c, attr_fill); + char c = use_sep_chars ? '_' : ' '; + grid_line_fill(col, Columns, schar_from_ascii(c), attr_fill); // Draw the 'showcmd' information if 'showcmdloc' == "tabline". if (p_sc && *p_sloc == 't') { @@ -868,7 +866,7 @@ void draw_tabline(void) /// the v:lnum and v:relnum variables don't have to be updated. /// /// @return The width of the built status column string for line "lnum" -int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, statuscol_T *stcp) +int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, char *buf, statuscol_T *stcp) { // Only update click definitions once per window per redraw. // Don't update when current width is 0, since it will be redrawn again if not empty. @@ -881,15 +879,15 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, statuscol_T * StlClickRecord *clickrec; char *stc = xstrdup(wp->w_p_stc); - int width = build_stl_str_hl(wp, stcp->text, MAXPATHL, stc, "statuscolumn", OPT_LOCAL, ' ', - stcp->width, &stcp->hlrec, fillclick ? &clickrec : NULL, stcp); + int width = build_stl_str_hl(wp, buf, MAXPATHL, stc, kOptStatuscolumn, OPT_LOCAL, 0, + stcp->width, &stcp->hlrec, NULL, fillclick ? &clickrec : NULL, stcp); xfree(stc); if (fillclick) { stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size); - wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, stcp->width, + wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, width, &wp->w_statuscol_click_defs_size); - stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, stcp->text, stcp->width, false); + stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, buf, width, false); } return width; @@ -913,8 +911,8 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, statuscol_T * /// Note: This should not be NameBuff /// @param outlen The length of the output buffer /// @param fmt The statusline format string -/// @param opt_name The option name corresponding to "fmt" -/// @param opt_scope The scope corresponding to "opt_name" +/// @param opt_idx Index of the option corresponding to "fmt" +/// @param opt_scope The scope corresponding to "opt_idx" /// @param fillchar Character to use when filling empty space in the statusline /// @param maxwidth The maximum width to make the statusline /// @param hltab HL attributes (can be NULL) @@ -922,9 +920,9 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, statuscol_T * /// @param stcp Status column attributes (can be NULL) /// /// @return The final width of the statusline -int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_name, int opt_scope, - int fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab, - statuscol_T *stcp) +int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex opt_idx, + int opt_scope, schar_T fillchar, int maxwidth, stl_hlrec_t **hltab, + size_t *hltab_len, StlClickRecord **tabtab, statuscol_T *stcp) { static size_t stl_items_len = 20; // Initial value, grows as needed. static stl_item_t *stl_items = NULL; @@ -937,14 +935,20 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n char buf_tmp[TMPLEN]; char win_tmp[TMPLEN]; char *usefmt = fmt; - const int save_must_redraw = must_redraw; - const int save_redr_type = curwin->w_redr_type; + const bool save_redraw_not_allowed = redraw_not_allowed; const bool save_KeyTyped = KeyTyped; // TODO(Bram): find out why using called_emsg_before makes tests fail, does it // matter? // const int called_emsg_before = called_emsg; const int did_emsg_before = did_emsg; + // When inside update_screen() we do not want redrawing a statusline, + // ruler, title, etc. to trigger another redraw, it may cause an endless + // loop. + if (updating_screen) { + redraw_not_allowed = true; + } + if (stl_items == NULL) { stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len); stl_groupitems = xmalloc(sizeof(int) * stl_items_len); @@ -957,10 +961,10 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n stl_separator_locations = xmalloc(sizeof(int) * stl_items_len); } - // if "fmt" was set insecurely it needs to be evaluated in the sandbox - // "opt_name" will be NULL when caller is nvim_eval_statusline() - const int use_sandbox = opt_name ? was_set_insecurely(wp, opt_name, opt_scope) - : false; + // If "fmt" was set insecurely it needs to be evaluated in the sandbox. + // "opt_idx" will be kOptInvalid when caller is nvim_eval_statusline(). + const bool use_sandbox = (opt_idx != kOptInvalid) ? was_set_insecurely(wp, opt_idx, opt_scope) + : false; // When the format starts with "%!" then evaluate it as an expression and // use the result as the actual format string. @@ -980,7 +984,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } if (fillchar == 0) { - fillchar = ' '; + fillchar = schar_from_ascii(' '); } // The cursor in windows other than the current one isn't always @@ -1178,7 +1182,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n out_p = out_p - n + 1; // Fill up space left over by half a double-wide char. while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) { - MB_CHAR2BYTES(fillchar, out_p); + schar_get_adv(&out_p, fillchar); } // } @@ -1201,13 +1205,13 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n if (min_group_width < 0) { min_group_width = 0 - min_group_width; while (group_len++ < min_group_width && out_p < out_end_p) { - MB_CHAR2BYTES(fillchar, out_p); + schar_get_adv(&out_p, fillchar); } // If the group is right-aligned, shift everything to the right and // prepend with filler characters. } else { // { Move the group to the right - group_len = (min_group_width - group_len) * utf_char2len(fillchar); + group_len = (min_group_width - group_len) * (int)schar_len(fillchar); memmove(t + group_len, t, (size_t)(out_p - t)); if (out_p + group_len >= (out_end_p + 1)) { group_len = out_end_p - out_p; @@ -1222,7 +1226,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // Prepend the fill characters for (; group_len > 0; group_len--) { - MB_CHAR2BYTES(fillchar, t); + schar_get_adv(&t, fillchar); } } } @@ -1400,7 +1404,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n case STL_VIM_EXPR: // '{' { char *block_start = fmt_p - 1; - int reevaluate = (*fmt_p == '%'); + bool reevaluate = (*fmt_p == '%'); itemisflag = true; if (reevaluate) { @@ -1545,7 +1549,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n break; case STL_SHOWCMD: - if (p_sc && (opt_name == NULL || strcmp(opt_name, p_sloc) == 0)) { + if (p_sc && (opt_idx == kOptInvalid || find_option(p_sloc) == opt_idx)) { str = showcmd_buf; } break; @@ -1632,29 +1636,41 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n break; } bool fold = opt == STL_FOLDCOL; - int width = fold ? (compute_foldcolumn(wp, 0) > 0) : wp->w_scwidth; + int fdc = fold ? compute_foldcolumn(wp, 0) : 0; + int width = fold ? fdc > 0 : wp->w_scwidth; - if (width == 0) { + if (width <= 0) { break; } foldsignitem = curitem; char *p = NULL; if (fold) { - size_t n = fill_foldcolumn(out_p, wp, stcp->foldinfo, - (linenr_T)get_vim_var_nr(VV_LNUM), NULL); + schar_T fold_buf[10]; + fill_foldcolumn(wp, stcp->foldinfo, (linenr_T)get_vim_var_nr(VV_LNUM), + 0, fdc, NULL, fold_buf); stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLF : HLF_FC) + 1); + size_t buflen = 0; + // TODO(bfredl): this is very backwards. we must support schar_T + // being used directly in 'statuscolumn' + for (int i = 0; i < fdc; i++) { + buflen += schar_get(out_p + buflen, fold_buf[i]); + } p = out_p; - p[n] = NUL; } + char buf[SIGN_WIDTH * MAX_SCHAR_SIZE]; size_t buflen = 0; varnumber_T virtnum = get_vim_var_nr(VV_VIRTNUM); for (int i = 0; i < width; i++) { if (!fold) { SignTextAttrs *sattr = virtnum ? NULL : &stcp->sattrs[i]; - p = sattr && sattr->text ? sattr->text : " "; - stl_items[curitem].minwid = -(sattr && sattr->text + p = " "; + if (sattr && sattr->text[0]) { + describe_sign_text(buf, sattr->text); + p = buf; + } + stl_items[curitem].minwid = -(sattr && sattr->text[0] ? (stcp->sign_cul_id ? stcp->sign_cul_id : sattr->hl_id) : (stcp->use_cul ? HLF_CLS : HLF_SC) + 1); } @@ -1803,7 +1819,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) { *out_p++ = ' '; } else { - MB_CHAR2BYTES(fillchar, out_p); + schar_get_adv(&out_p, fillchar); } } minwid = 0; @@ -1826,7 +1842,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // digit follows. if (fillable && *t == ' ' && (!ascii_isdigit(*(t + 1)) || fillchar != '-')) { - MB_CHAR2BYTES(fillchar, out_p); + schar_get_adv(&out_p, fillchar); } else { *out_p++ = *t; } @@ -1843,7 +1859,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // For left-aligned items, fill any remaining space with the fillchar for (; l < minwid && out_p < out_end_p; l++) { - MB_CHAR2BYTES(fillchar, out_p); + schar_get_adv(&out_p, fillchar); } // Otherwise if the item is a number, copy that to the output buffer. @@ -1928,16 +1944,16 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n stl_items[curitem].type = Empty; } + if (num >= 0 || (!itemisflag && str && *str)) { + prevchar_isflag = false; // Item not NULL, but not a flag + } + // Only free the string buffer if we allocated it. // Note: This is not needed if `str` is pointing at `tmp` if (opt == STL_VIM_EXPR) { XFREE_CLEAR(str); } - if (num >= 0 || (!itemisflag && str && *str)) { - prevchar_isflag = false; // Item not NULL, but not a flag - } - // Item processed, move to the next curitem++; } @@ -1954,11 +1970,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // What follows is post-processing to handle alignment and highlighting. int width = vim_strsize(out); - // Return truncated width for 'statuscolumn' - if (stcp != NULL && width > stcp->width) { - stcp->truncate = width - stcp->width; - } - if (maxwidth > 0 && width > maxwidth) { + if (maxwidth > 0 && width > maxwidth && (!stcp || width > MAX_STCWIDTH)) { // Result is too long, must truncate somewhere. int item_idx = 0; char *trunc_p; @@ -2064,8 +2076,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // Fill up for half a double-wide character. while (++width < maxwidth) { - MB_CHAR2BYTES(fillchar, trunc_p); - *trunc_p = NUL; + schar_get_adv(&trunc_p, fillchar); } } width = maxwidth; @@ -2094,12 +2105,12 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n for (int l = 0; l < num_separators; l++) { int dislocation = (l == (num_separators - 1)) ? final_spaces : standard_spaces; - dislocation *= utf_char2len(fillchar); + dislocation *= (int)schar_len(fillchar); char *start = stl_items[stl_separator_locations[l]].start; char *seploc = start + dislocation; STRMOVE(seploc, start); for (char *s = start; s < seploc;) { - MB_CHAR2BYTES(fillchar, s); + schar_get_adv(&s, fillchar); } for (int item_idx = stl_separator_locations[l] + 1; @@ -2127,6 +2138,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n sp->start = NULL; sp->userhl = 0; } + if (hltab_len) { + *hltab_len = (size_t)itemcnt; + } // Store the info about tab pages labels. if (tabtab != NULL) { @@ -2164,12 +2178,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n cur_tab_rec->def.func = NULL; } - // When inside update_screen we do not want redrawing a statusline, ruler, - // title, etc. to trigger another redraw, it may cause an endless loop. - if (updating_screen) { - must_redraw = save_must_redraw; - curwin->w_redr_type = save_redr_type; - } + redraw_not_allowed = save_redraw_not_allowed; // Check for an error. If there is one the display will be messed up and // might loop redrawing. Avoid that by making the corresponding option @@ -2177,8 +2186,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // TODO(Bram): find out why using called_emsg_before makes tests fail, does it // matter? // if (called_emsg > called_emsg_before) - if (opt_name && did_emsg > did_emsg_before) { - set_string_option_direct(opt_name, -1, "", OPT_FREE | opt_scope, SID_ERROR); + if (opt_idx != kOptInvalid && did_emsg > did_emsg_before) { + set_string_option_direct(opt_idx, "", opt_scope, SID_ERROR); } // A user function may reset KeyTyped, restore it. diff --git a/src/nvim/statusline.h b/src/nvim/statusline.h index eab7c1aa47..1a5f12dbf4 100644 --- a/src/nvim/statusline.h +++ b/src/nvim/statusline.h @@ -2,9 +2,10 @@ #include <stddef.h> -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" -#include "nvim/statusline_defs.h" // IWYU pragma: export +#include "nvim/option_defs.h" // IWYU pragma: keep +#include "nvim/statusline_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep /// Array defining what should be done when tabline is clicked EXTERN StlClickDefinition *tab_page_click_defs INIT( = NULL); diff --git a/src/nvim/statusline_defs.h b/src/nvim/statusline_defs.h index c1a54f4f54..118f4a257b 100644 --- a/src/nvim/statusline_defs.h +++ b/src/nvim/statusline_defs.h @@ -1,11 +1,8 @@ #pragma once #include <stdbool.h> -#include <stddef.h> #include "nvim/fold_defs.h" -#include "nvim/macros_defs.h" -#include "nvim/os/os_defs.h" #include "nvim/sign_defs.h" /// Status line click definition @@ -57,21 +54,13 @@ struct stl_item { }; /// Struct to hold info for 'statuscolumn' -typedef struct statuscol statuscol_T; - -struct statuscol { +typedef struct { int width; ///< width of the status column - int cur_attr; ///< current attributes in text int num_attr; ///< default highlight attr int sign_cul_id; ///< cursorline sign highlight id - int truncate; ///< truncated width bool draw; ///< whether to draw the statuscolumn bool use_cul; ///< whether to use cursorline attrs - char text[MAXPATHL]; ///< text in status column - char *textp; ///< current position in text - char *text_end; ///< end of text (the NUL byte) stl_hlrec_t *hlrec; ///< highlight groups - stl_hlrec_t *hlrecp; ///< current highlight group foldinfo_T foldinfo; ///< fold information SignTextAttrs *sattrs; ///< sign attributes -}; +} statuscol_T; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index a439d11818..01bd610292 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -17,11 +17,13 @@ #include "nvim/eval/typval_defs.h" #include "nvim/ex_docmd.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/math.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" @@ -30,6 +32,10 @@ #include "nvim/types_defs.h" #include "nvim/vim_defs.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "strings.c.generated.h" +#endif + static const char e_cannot_mix_positional_and_non_positional_str[] = N_("E1500: Cannot mix positional and non-positional arguments: %s"); static const char e_fmt_arg_nr_unused_str[] @@ -179,21 +185,17 @@ char *vim_strnsave_unquoted(const char *const string, const size_t length) char *vim_strsave_shellescape(const char *string, bool do_special, bool do_newline) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char *d; - char *escaped_string; size_t l; - int csh_like; - bool fish_like; // Only csh and similar shells expand '!' within single quotes. For sh and // the like we must not put a backslash before it, it will be taken // literally. If do_special is set the '!' will be escaped twice. // Csh also needs to have "\n" escaped twice when do_special is set. - csh_like = csh_like_shell(); + int csh_like = csh_like_shell(); // Fish shell uses '\' as an escape character within single quotes, so '\' // itself must be escaped to get a literal '\'. - fish_like = fish_like_shell(); + bool fish_like = fish_like_shell(); // First count the number of extra bytes required. size_t length = strlen(string) + 3; // two quotes and a trailing NUL @@ -225,8 +227,8 @@ char *vim_strsave_shellescape(const char *string, bool do_special, bool do_newli } // Allocate memory for the result and fill it. - escaped_string = xmalloc(length); - d = escaped_string; + char *escaped_string = xmalloc(length); + char *d = escaped_string; // add opening quote #ifdef MSWIN @@ -297,9 +299,7 @@ char *vim_strsave_shellescape(const char *string, bool do_special, bool do_newli char *vim_strsave_up(const char *string) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char *p1; - - p1 = xstrdup(string); + char *p1 = xstrdup(string); vim_strup(p1); return p1; } @@ -335,36 +335,41 @@ void vim_strup(char *p) char *strcase_save(const char *const orig, bool upper) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char *res = xstrdup(orig); + // Calculate the initial length and allocate memory for the result + size_t orig_len = strlen(orig); + // +1 for the null terminator + char *res = xmalloc(orig_len + 1); + // Index in the result string + size_t res_index = 0; + // Current position in the original string + const char *p = orig; - char *p = res; while (*p != NUL) { - int c = utf_ptr2char(p); - int l = utf_ptr2len(p); - if (c == 0) { - // overlong sequence, use only the first byte - c = (uint8_t)(*p); - l = 1; - } - int uc = upper ? mb_toupper(c) : mb_tolower(c); - - // Reallocate string when byte count changes. This is rare, - // thus it's OK to do another malloc()/free(). - int newl = utf_char2len(uc); - if (newl != l) { - // TODO(philix): use xrealloc() in strcase_save() - char *s = xmalloc(strlen(res) + (size_t)(1 + newl - l)); - memcpy(s, res, (size_t)(p - res)); - STRCPY(s + (p - res) + newl, p + l); - p = s + (p - res); - xfree(res); - res = s; - } - - utf_char2bytes(uc, p); - p += newl; - } - + CharInfo char_info = utf_ptr2CharInfo(p); + int c = char_info.value < 0 ? (uint8_t)(*p) : char_info.value; + int newc = upper ? mb_toupper(c) : mb_tolower(c); + // Cast to size_t to avoid mixing types in arithmetic + size_t newl = (size_t)utf_char2len(newc); + + // Check if there's enough space in the allocated memory + if (res_index + newl > orig_len) { + // Need more space: allocate extra space for the new character and the null terminator + size_t new_size = res_index + newl + 1; + res = xrealloc(res, new_size); + // Adjust the original length to the new size, minus the null terminator + orig_len = new_size - 1; + } + + // Write the possibly new character into the result string + utf_char2bytes(newc, res + res_index); + // Move the index in the result string + res_index += newl; + // Move to the next character in the original string + p += char_info.len; + } + + // Null-terminate the result string + res[res_index] = NUL; return res; } @@ -372,9 +377,7 @@ char *strcase_save(const char *const orig, bool upper) void del_trailing_spaces(char *ptr) FUNC_ATTR_NONNULL_ALL { - char *q; - - q = ptr + strlen(ptr); + char *q = ptr + strlen(ptr); while (--q > ptr && ascii_iswhite(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V) { *q = NUL; } @@ -461,9 +464,6 @@ char *vim_strchr(const char *const string, const int c) // Sort an array of strings. -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "strings.c.generated.h" -#endif static int sort_compare(const void *s1, const void *s2) FUNC_ATTR_NONNULL_ALL { @@ -1357,9 +1357,13 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st assert(n <= SIZE_MAX - str_l); str_l += n; } else { - size_t min_field_width = 0, precision = 0; - int zero_padding = 0, precision_specified = 0, justify_left = 0; - int alternate_form = 0, force_sign = 0; + size_t min_field_width = 0; + size_t precision = 0; + bool zero_padding = false; + bool precision_specified = false; + bool justify_left = false; + bool alternate_form = false; + bool force_sign = false; // if both ' ' and '+' flags appear, ' ' flag should be ignored int space_for_positive = 1; @@ -1424,17 +1428,17 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st while (true) { switch (*p) { case '0': - zero_padding = 1; p++; continue; + zero_padding = true; p++; continue; case '-': - justify_left = 1; p++; continue; + justify_left = true; p++; continue; // if both '0' and '-' flags appear, '0' should be ignored case '+': - force_sign = 1; space_for_positive = 0; p++; continue; + force_sign = true; space_for_positive = 0; p++; continue; case ' ': - force_sign = 1; p++; continue; + force_sign = true; p++; continue; // if both ' ' and '+' flags appear, ' ' should be ignored case '#': - alternate_form = 1; p++; continue; + alternate_form = true; p++; continue; case '\'': p++; continue; default: @@ -1469,7 +1473,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st min_field_width = (size_t)j; } else { min_field_width = (size_t)-j; - justify_left = 1; + justify_left = true; } } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we treat @@ -1485,7 +1489,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st // parse precision if (*p == '.') { p++; - precision_specified = 1; + precision_specified = true; if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we @@ -1520,7 +1524,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st if (j >= 0) { precision = (size_t)j; } else { - precision_specified = 0; + precision_specified = false; precision = 0; } } @@ -1775,7 +1779,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st // '0' flag should be ignored. This is so with Solaris 2.6, Digital // UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. if (precision_specified) { - zero_padding = 0; + zero_padding = false; } if (fmt_spec == 'd') { @@ -1863,8 +1867,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st && !(zero_padding_insertion_ind < str_arg_l && tmp[zero_padding_insertion_ind] == '0')) { // assure leading zero for alternate-form octal numbers - if (!precision_specified - || precision < num_of_digits + 1) { + if (!precision_specified || precision < num_of_digits + 1) { // precision is increased to force the first character to be // zero, except if a zero value is formatted with an explicit // precision of zero @@ -1895,7 +1898,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st case 'G': { // floating point char format[40]; - int remove_trailing_zeroes = false; + bool remove_trailing_zeroes = false; double f = (tvs ? tv_float(tvs, &arg_idx) @@ -1921,12 +1924,12 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st force_sign, space_for_positive), sizeof(tmp)); str_arg_l = strlen(tmp); - zero_padding = 0; + zero_padding = false; } else if (xisnan(f)) { // Not a number: nan or NAN memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4); str_arg_l = 3; - zero_padding = 0; + zero_padding = false; } else { // Regular float number format[0] = '%'; @@ -2012,8 +2015,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st default: // unrecognized conversion specifier, keep format string as-is - zero_padding = 0; // turn zero padding off for non-numeric conversion - justify_left = 1; + zero_padding = false; // turn zero padding off for non-numeric conversion + justify_left = true; min_field_width = 0; // reset flags // discard the unrecognized conversion, just keep @@ -2163,6 +2166,47 @@ int kv_do_printf(StringBuilder *str, const char *fmt, ...) return printed; } +String arena_printf(Arena *arena, const char *fmt, ...) + FUNC_ATTR_PRINTF(2, 3) +{ + size_t remaining = 0; + char *buf = NULL; + if (arena) { + if (!arena->cur_blk) { + arena_alloc_block(arena); + } + + // happy case, we can fit the printed string in the rest of the current + // block (one pass): + remaining = arena->size - arena->pos; + buf = arena->cur_blk + arena->pos; + } + + va_list ap; + va_start(ap, fmt); + int printed = vsnprintf(buf, remaining, fmt, ap); + va_end(ap); + + if (printed < 0) { + return (String)STRING_INIT; + } + + // printed string didn't fit, allocate and try again + if ((size_t)printed >= remaining) { + buf = arena_alloc(arena, (size_t)printed + 1, false); + va_start(ap, fmt); + printed = vsnprintf(buf, (size_t)printed + 1, fmt, ap); + va_end(ap); + if (printed < 0) { + return (String)STRING_INIT; + } + } else { + arena->pos += (size_t)printed + 1; + } + + return cbuf_as_string(buf, (size_t)printed); +} + /// Reverse text into allocated memory. /// /// @return the allocated string. @@ -2224,7 +2268,7 @@ char *strrep(const char *src, const char *what, const char *rep) } /// Implementation of "byteidx()" and "byteidxcomp()" functions -static void byteidx_common(typval_T *argvars, typval_T *rettv, int comp) +static void byteidx_common(typval_T *argvars, typval_T *rettv, bool comp) { rettv->vval.v_number = -1; diff --git a/src/nvim/strings.h b/src/nvim/strings.h index d717362f87..903559b062 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -5,11 +5,16 @@ #include "auto/config.h" #include "klib/kvec.h" +#include "nvim/api/private/defs.h" #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" #include "nvim/os/os_defs.h" #include "nvim/types_defs.h" // IWYU pragma: keep +static inline char *strappend(char *dst, const char *src) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL + REAL_FATTR_NONNULL_RET REAL_FATTR_WARN_UNUSED_RESULT; + /// Append string to string and return pointer to the next byte /// /// Unlike strcat, this one does *not* add NUL byte and returns pointer to the @@ -20,8 +25,6 @@ /// /// @return pointer to the byte just past the appended byte. static inline char *strappend(char *const dst, const char *const src) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT - FUNC_ATTR_NONNULL_RET { const size_t src_len = strlen(src); return (char *)memmove(dst, src, src_len) + src_len; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 11282ea170..ab8ab62b90 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -9,6 +9,7 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" @@ -20,12 +21,14 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent_c.h" #include "nvim/macros_defs.h" @@ -40,6 +43,7 @@ #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/strings.h" #include "nvim/syntax.h" @@ -77,7 +81,7 @@ static const char e_trailing_char_after_rsb_str_str[] // and for the actually highlighted text (_h_start and _h_end). // // Note that ordering of members is optimized to reduce padding. -typedef struct syn_pattern { +typedef struct { char sp_type; // see SPTYPE_ defines below bool sp_syncing; // this item used for syncing int16_t sp_syn_match_id; // highlight group ID of pattern @@ -97,7 +101,7 @@ typedef struct syn_pattern { syn_time_T sp_time; } synpat_T; -typedef struct syn_cluster_S { +typedef struct { char *scl_name; // syntax cluster name char *scl_name_u; // uppercase of scl_name int16_t *scl_list; // IDs in this syntax cluster @@ -106,7 +110,7 @@ typedef struct syn_cluster_S { // For the current state we need to remember more than just the idx. // When si_m_endpos.lnum is 0, the items other than si_idx are unknown. // (The end positions have the column number of the next char) -typedef struct state_item { +typedef struct { int si_idx; // index of syntax pattern or // KEYWORD_IDX int si_id; // highlight group ID for keywords @@ -342,10 +346,10 @@ void syntax_start(win_T *wp, linenr_T lnum) if (VALID_STATE(¤t_state) && current_lnum < lnum && current_lnum < syn_buf->b_ml.ml_line_count) { - (void)syn_finish_line(false); + syn_finish_line(false); if (!current_state_stored) { current_lnum++; - (void)store_current_state(); + store_current_state(); } // If the current_lnum is now the same as "lnum", keep the current @@ -403,7 +407,7 @@ void syntax_start(win_T *wp, linenr_T lnum) } while (current_lnum < lnum) { syn_start_line(); - (void)syn_finish_line(false); + syn_finish_line(false); current_lnum++; // If we parsed at least "minlines" lines or started at a valid @@ -492,23 +496,16 @@ static void clear_current_state(void) // 3. Simply start on a given number of lines above "lnum". static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) { - buf_T *curbuf_save; - win_T *curwin_save; pos_T cursor_save; - int idx; linenr_T lnum; - linenr_T end_lnum; linenr_T break_lnum; - bool had_sync_point; stateitem_T *cur_si; synpat_T *spp; - char *line; int found_flags = 0; int found_match_idx = 0; linenr_T found_current_lnum = 0; int found_current_col = 0; lpos_T found_m_endpos; - colnr_T prev_current_col; // Clear any current state that might be hanging around. invalidate_current_state(); @@ -545,14 +542,14 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) if (syn_block->b_syn_sync_flags & SF_CCOMMENT) { // Need to make syn_buf the current buffer for a moment, to be able to // use find_start_comment(). - curwin_save = curwin; + win_T *curwin_save = curwin; curwin = wp; - curbuf_save = curbuf; + buf_T *curbuf_save = curbuf; curbuf = syn_buf; // Skip lines that end in a backslash. for (; start_lnum > 1; start_lnum--) { - line = ml_get(start_lnum - 1); + char *line = ml_get(start_lnum - 1); if (*line == NUL || *(line + strlen(line) - 1) != '\\') { break; } @@ -568,7 +565,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) // defines the comment. // Restrict the search for the end of a comment to b_syn_sync_maxlines. if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL) { - for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0;) { + for (int idx = syn_block->b_syn_patterns.ga_len; --idx >= 0;) { if (SYN_ITEMS(syn_block)[idx].sp_syn.id == syn_block->b_syn_sync_id && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START) { @@ -595,7 +592,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) found_m_endpos.lnum = 0; found_m_endpos.col = 0; - end_lnum = start_lnum; + linenr_T end_lnum = start_lnum; lnum = start_lnum; while (--lnum > break_lnum) { // This can take a long time: break when CTRL-C pressed. @@ -623,7 +620,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) for (current_lnum = lnum; current_lnum < end_lnum; current_lnum++) { syn_start_line(); while (true) { - had_sync_point = syn_finish_line(true); + bool had_sync_point = syn_finish_line(true); // When a sync point has been found, remember where, and // continue to look for another one, further on in the line. if (had_sync_point && current_state.ga_len) { @@ -662,7 +659,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) // syn_current_attr() will have skipped the check for // an item that ends here, need to do that now. Be // careful not to go past the NUL. - prev_current_col = current_col; + colnr_T prev_current_col = current_col; if (syn_getcurline()[current_col] != NUL) { current_col++; } @@ -699,7 +696,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) } current_col = found_m_endpos.col; current_lnum = found_m_endpos.lnum; - (void)syn_finish_line(false); + syn_finish_line(false); current_lnum++; } else { current_lnum = start_lnum; @@ -1151,7 +1148,7 @@ static synstate_T *store_current_state(void) // Add a new entry // If no free items, cleanup the array first. if (syn_block->b_sst_freecount == 0) { - (void)syn_stack_cleanup(); + syn_stack_cleanup(); // "sp" may have been moved to the freelist now sp = syn_stack_find_entry(current_lnum); } @@ -1371,7 +1368,7 @@ bool syntax_check_changed(linenr_T lnum) if (sp != NULL && sp->sst_lnum == lnum) { // finish the previous line (needed when not all of the line was // drawn) - (void)syn_finish_line(false); + syn_finish_line(false); // Compare the current state with the previously saved state of // the line. @@ -1381,7 +1378,7 @@ bool syntax_check_changed(linenr_T lnum) // Store the current state in b_sst_array[] for later use. current_lnum++; - (void)store_current_state(); + store_current_state(); } } @@ -1397,7 +1394,7 @@ bool syntax_check_changed(linenr_T lnum) static bool syn_finish_line(const bool syncing) { while (!current_finished) { - (void)syn_current_attr(syncing, false, NULL, false); + syn_current_attr(syncing, false, NULL, false); // When syncing, and found some item, need to check the item. if (syncing && current_state.ga_len) { @@ -1520,7 +1517,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con // If we found a match after the last column, use it. if (next_match_idx >= 0 && next_match_col >= (int)current_col && next_match_col != MAXCOL) { - (void)push_next_match(); + push_next_match(); } current_finished = true; @@ -2418,8 +2415,8 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_ regmatch.rmm_ic = spp->sp_ic; regmatch.regprog = spp->sp_prog; - int r = syn_regexec(®match, startpos->lnum, lc_col, - IF_SYN_TIME(&spp->sp_time)); + bool r = syn_regexec(®match, startpos->lnum, lc_col, + IF_SYN_TIME(&spp->sp_time)); spp->sp_prog = regmatch.regprog; if (r) { if (best_idx == -1 || regmatch.startpos[0].col @@ -2665,7 +2662,7 @@ static char *syn_getcurline(void) // Call vim_regexec() to find a match with "rmp" in "syn_buf". // Returns true when there is a match. -static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st) +static bool syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st) { int timed_out = 0; proftime_T pt; @@ -3369,7 +3366,7 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only) continue; } - (void)syn_list_header(did_header, 0, id, true); + syn_list_header(did_header, 0, id, true); did_header = true; last_matchgroup = 0; if (spp->sp_type == SPTYPE_MATCH) { @@ -3422,7 +3419,7 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only) // list the link, if there is one if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int) { - (void)syn_list_header(did_header, 0, id, true); + syn_list_header(did_header, 0, id, true); msg_puts_attr("links to", attr); msg_putchar(' '); msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1), 0); @@ -3644,23 +3641,17 @@ static bool syn_list_keywords(const int id, const hashtab_T *const ht, bool did_ static void syn_clear_keyword(int id, hashtab_T *ht) { - hashitem_T *hi; - keyentry_T *kp; - keyentry_T *kp_prev; - keyentry_T *kp_next; - int todo; - hash_lock(ht); - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; hi++) { + int todo = (int)ht->ht_used; + for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) { if (HASHITEM_EMPTY(hi)) { continue; } todo--; - kp_prev = NULL; - for (kp = HI2KE(hi); kp != NULL;) { + keyentry_T *kp_prev = NULL; + for (keyentry_T *kp = HI2KE(hi); kp != NULL;) { if (kp->k_syn.id == id) { - kp_next = kp->ke_next; + keyentry_T *kp_next = kp->ke_next; if (kp_prev == NULL) { if (kp_next == NULL) { hash_remove(ht, hi); @@ -3686,16 +3677,13 @@ static void syn_clear_keyword(int id, hashtab_T *ht) // Clear a whole keyword table. static void clear_keywtab(hashtab_T *ht) { - hashitem_T *hi; - int todo; - keyentry_T *kp; keyentry_T *kp_next; - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; hi++) { + int todo = (int)ht->ht_used; + for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) { if (!HASHITEM_EMPTY(hi)) { todo--; - for (kp = HI2KE(hi); kp != NULL; kp = kp_next) { + for (keyentry_T *kp = HI2KE(hi); kp != NULL; kp = kp_next) { kp_next = kp->ke_next; xfree(kp->next_list); xfree(kp->k_syn.cont_in_list); @@ -3791,9 +3779,7 @@ static char *get_group_name(char *arg, char **name_end) /// Return NULL for any error; static char *get_syn_options(char *arg, syn_opt_arg_T *opt, int *conceal_char, int skip) { - int syn_id; int len = 0; - char *p; int fidx; static const struct flag { char *name; @@ -3837,7 +3823,7 @@ static char *get_syn_options(char *arg, syn_opt_arg_T *opt, int *conceal_char, i } for (fidx = ARRAY_SIZE(flagtab); --fidx >= 0;) { - p = flagtab[fidx].name; + char *p = flagtab[fidx].name; int i; for (i = 0, len = 0; p[i] != NUL; i += 2, len++) { if (arg[len] != p[i] && arg[len] != p[i + 1]) { @@ -3906,7 +3892,7 @@ static char *get_syn_options(char *arg, syn_opt_arg_T *opt, int *conceal_char, i if (strcmp(gname, "NONE") == 0) { *opt->sync_idx = NONE_IDX; } else { - syn_id = syn_name2id(gname); + int syn_id = syn_name2id(gname); int i; for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0;) { if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id @@ -3962,10 +3948,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing) char *arg = eap->arg; int sgl_id = 1; char *group_name_end; - char *rest; const char *errormsg = NULL; - int prev_toplvl_grp; - int prev_syn_inc_tag; bool source = false; eap->nextcmd = find_nextcmd(arg); @@ -3975,7 +3958,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing) if (arg[0] == '@') { arg++; - rest = get_group_name(arg, &group_name_end); + char *rest = get_group_name(arg, &group_name_end); if (rest == NULL) { emsg(_("E397: Filename required")); return; @@ -4011,9 +3994,9 @@ static void syn_cmd_include(exarg_T *eap, int syncing) emsg(_("E847: Too many syntax includes")); return; } - prev_syn_inc_tag = current_syn_inc_tag; + int prev_syn_inc_tag = current_syn_inc_tag; current_syn_inc_tag = ++running_syn_inc_tag; - prev_toplvl_grp = curwin->w_s->b_syn_topgrp; + int prev_toplvl_grp = curwin->w_s->b_syn_topgrp; curwin->w_s->b_syn_topgrp = sgl_id; if (source ? do_source(eap->arg, false, DOSO_NONE, NULL) == FAIL @@ -4030,15 +4013,11 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) char *arg = eap->arg; char *group_name_end; int syn_id; - char *rest; char *keyword_copy = NULL; - char *p; - char *kw; syn_opt_arg_T syn_opt_arg; - int cnt; int conceal_char = NUL; - rest = get_group_name(arg, &group_name_end); + char *rest = get_group_name(arg, &group_name_end); if (rest != NULL) { if (eap->skip) { @@ -4061,8 +4040,8 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) // The options given apply to ALL keywords, so all options must be // found before keywords can be created. // 1: collect the options and copy the keywords to keyword_copy. - cnt = 0; - p = keyword_copy; + int cnt = 0; + char *p = keyword_copy; for (; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) { rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); if (rest == NULL || ends_excmd(*rest)) { @@ -4084,7 +4063,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) syn_incl_toplevel(syn_id, &syn_opt_arg.flags); // 2: Add an entry for each keyword. - for (kw = keyword_copy; --cnt >= 0; kw += strlen(kw) + 1) { + for (char *kw = keyword_copy; --cnt >= 0; kw += strlen(kw) + 1) { for (p = vim_strchr(kw, '[');;) { if (p != NULL) { *p = NUL; @@ -4235,7 +4214,6 @@ static void syn_cmd_region(exarg_T *eap, int syncing) char *rest; // next arg, NULL on error char *key_end; char *key = NULL; - char *p; int item; #define ITEM_START 0 #define ITEM_SKIP 1 @@ -4318,7 +4296,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing) } if (item == ITEM_MATCHGROUP) { - p = skiptowhite(rest); + char *p = skiptowhite(rest); if ((p - rest == 4 && strncmp(rest, "NONE", 4) == 0) || eap->skip) { matchgroup_id = 0; } else { @@ -4645,7 +4623,6 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing) { char *arg = eap->arg; char *group_name_end; - char *rest; bool got_clstr = false; int opt_len; int list_op; @@ -4655,7 +4632,7 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing) return; } - rest = get_group_name(arg, &group_name_end); + char *rest = get_group_name(arg, &group_name_end); if (rest != NULL) { int scl_id = syn_check_cluster(arg, (int)(group_name_end - arg)); @@ -4722,7 +4699,6 @@ static void init_syn_patterns(void) /// @return a pointer to the next argument, or NULL in case of an error. static char *get_syn_pattern(char *arg, synpat_T *ci) { - int *p; int idx; // need at least three chars @@ -4759,7 +4735,7 @@ static char *get_syn_pattern(char *arg, synpat_T *ci) } } if (idx >= 0) { - p = &(ci->sp_offsets[idx]); + int *p = &(ci->sp_offsets[idx]); if (idx != SPO_LC_OFF) { switch (end[3]) { case 's': @@ -4812,12 +4788,9 @@ static char *get_syn_pattern(char *arg, synpat_T *ci) static void syn_cmd_sync(exarg_T *eap, int syncing) { char *arg_start = eap->arg; - char *arg_end; char *key = NULL; - char *next_arg; - int illegal = false; - int finished = false; - char *cpo_save; + bool illegal = false; + bool finished = false; if (ends_excmd(*arg_start)) { syn_cmd_list(eap, true); @@ -4825,8 +4798,8 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) } while (!ends_excmd(*arg_start)) { - arg_end = skiptowhite(arg_start); - next_arg = skipwhite(arg_end); + char *arg_end = skiptowhite(arg_start); + char *next_arg = skipwhite(arg_end); xfree(key); key = vim_strnsave_up(arg_start, (size_t)(arg_end - arg_start)); if (strcmp(key, "CCOMMENT") == 0) { @@ -4896,7 +4869,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic; // Make 'cpoptions' empty, to avoid the 'l' flag - cpo_save = p_cpo; + char *cpo_save = p_cpo; p_cpo = empty_string_option; curwin->w_s->b_syn_linecont_prog = vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC); @@ -5128,10 +5101,8 @@ static int16_t *copy_id_list(const int16_t *const list) static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, int contained) { int retval; - int16_t *scl_list; int16_t id = ssp->id; static int depth = 0; - int r; // If ssp has a "containedin" list and "cur_si" is in it, return true. if (cur_si != NULL && ssp->cont_in_list != NULL @@ -5194,12 +5165,12 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in return retval; } if (item >= SYNID_CLUSTER) { - scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list; + int16_t *scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list; // restrict recursiveness to 30 to avoid an endless loop for a // cluster that includes itself (indirectly) if (scl_list != NULL && depth < 30) { depth++; - r = in_id_list(NULL, scl_list, ssp, contained); + int r = in_id_list(NULL, scl_list, ssp, contained); depth--; if (r) { return retval; @@ -5442,7 +5413,7 @@ int syn_get_id(win_T *wp, linenr_T lnum, colnr_T col, int trans, bool *spellp, i next_match_idx = -1; } - (void)get_syntax_attr(col, spellp, keep_state); + get_syntax_attr(col, spellp, keep_state); return trans ? current_trans_id : current_id; } @@ -5464,7 +5435,7 @@ int syn_get_concealed_id(win_T *wp, linenr_T lnum, colnr_T col) { int seqnr; - (void)syn_get_id(wp, lnum, col, false, NULL, false); + syn_get_id(wp, lnum, col, false, NULL, false); int syntax_flags = get_syntax_info(&seqnr); if (syntax_flags & HL_CONCEAL) { @@ -5524,7 +5495,7 @@ int syn_get_foldlevel(win_T *wp, linenr_T lnum) int cur_level = level; int low_level = cur_level; while (!current_finished) { - (void)syn_current_attr(false, false, NULL, false); + syn_current_attr(false, false, NULL, false); cur_level = syn_cur_foldlevel(); if (cur_level < low_level) { low_level = cur_level; diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index 4e9c7a27dc..69fc1a74b8 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -1,33 +1,32 @@ #pragma once -#include <stdbool.h> - -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep -#include "nvim/globals.h" #include "nvim/macros_defs.h" -#include "nvim/syntax_defs.h" // IWYU pragma: export +#include "nvim/syntax_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep -#define HL_CONTAINED 0x01 // not used on toplevel -#define HL_TRANSP 0x02 // has no highlighting -#define HL_ONELINE 0x04 // match within one line only -#define HL_HAS_EOL 0x08 // end pattern that matches with $ -#define HL_SYNC_HERE 0x10 // sync point after this item (syncing only) -#define HL_SYNC_THERE 0x20 // sync point at current line (syncing only) -#define HL_MATCH 0x40 // use match ID instead of item ID -#define HL_SKIPNL 0x80 // nextgroup can skip newlines -#define HL_SKIPWHITE 0x100 // nextgroup can skip white space -#define HL_SKIPEMPTY 0x200 // nextgroup can skip empty lines -#define HL_KEEPEND 0x400 // end match always kept -#define HL_EXCLUDENL 0x800 // exclude NL from match -#define HL_DISPLAY 0x1000 // only used for displaying, not syncing -#define HL_FOLD 0x2000 // define fold -#define HL_EXTEND 0x4000 // ignore a keepend -#define HL_MATCHCONT 0x8000 // match continued from previous line -#define HL_TRANS_CONT 0x10000 // transparent item without contains arg -#define HL_CONCEAL 0x20000 // can be concealed -#define HL_CONCEALENDS 0x40000 // can be concealed +enum { + HL_CONTAINED = 0x01, ///< not used on toplevel + HL_TRANSP = 0x02, ///< has no highlighting + HL_ONELINE = 0x04, ///< match within one line only + HL_HAS_EOL = 0x08, ///< end pattern that matches with $ + HL_SYNC_HERE = 0x10, ///< sync point after this item (syncing only) + HL_SYNC_THERE = 0x20, ///< sync point at current line (syncing only) + HL_MATCH = 0x40, ///< use match ID instead of item ID + HL_SKIPNL = 0x80, ///< nextgroup can skip newlines + HL_SKIPWHITE = 0x100, ///< nextgroup can skip white space + HL_SKIPEMPTY = 0x200, ///< nextgroup can skip empty lines + HL_KEEPEND = 0x400, ///< end match always kept + HL_EXCLUDENL = 0x800, ///< exclude NL from match + HL_DISPLAY = 0x1000, ///< only used for displaying, not syncing + HL_FOLD = 0x2000, ///< define fold + HL_EXTEND = 0x4000, ///< ignore a keepend + HL_MATCHCONT = 0x8000, ///< match continued from previous line + HL_TRANS_CONT = 0x10000, ///< transparent item without contains arg + HL_CONCEAL = 0x20000, ///< can be concealed + HL_CONCEALENDS = 0x40000, ///< can be concealed +}; #define SYN_GROUP_STATIC(s) syn_check_group(S_LEN(s)) diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h index ae4997154e..94ca2c4cba 100644 --- a/src/nvim/syntax_defs.h +++ b/src/nvim/syntax_defs.h @@ -1,6 +1,6 @@ #pragma once -#include "nvim/highlight_defs.h" +#include "nvim/buffer_defs.h" #define SST_MIN_ENTRIES 150 // minimal size for state stack array #define SST_MAX_ENTRIES 1000 // maximal size for state stack array @@ -8,11 +8,6 @@ #define SST_DIST 16 // normal distance between entries #define SST_INVALID ((synstate_T *)-1) // invalid syn_state pointer -typedef struct syn_state synstate_T; - -#include "nvim/buffer_defs.h" -#include "nvim/regexp_defs.h" - // struct passed to in_id_list() struct sp_syn { int inc_tag; // ":syn include" unique tag @@ -33,7 +28,7 @@ struct keyentry { }; // Struct used to store one state of the state stack. -typedef struct buf_state { +typedef struct { int bs_idx; // index of pattern int bs_flags; // flags for pattern int bs_seqnr; // stores si_seqnr diff --git a/src/nvim/tag.c b/src/nvim/tag.c index c6a1a13606..ab5bfc6773 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -10,9 +10,12 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" @@ -23,18 +26,22 @@ #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/help.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/input.h" #include "nvim/insexpand.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" @@ -44,22 +51,25 @@ #include "nvim/optionstr.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" +#include "nvim/os/os_defs.h" #include "nvim/os/time.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/search.h" #include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/tag.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/vim_defs.h" #include "nvim/window.h" // Structure to hold pointers to various items in a tag line. -typedef struct tag_pointers { +typedef struct { // filled in by parse_tag_line(): char *tagname; // start of tag name (skip "file:") char *tagname_end; // char after tag name @@ -207,8 +217,8 @@ static char *tagmatchname = NULL; // name of last used tag // normal tagstack. static taggy_T ptag_entry = { NULL, INIT_FMARK, 0, 0, NULL }; -static int tfu_in_use = false; // disallow recursive call of tagfunc -static Callback tfu_cb; // 'tagfunc' callback function +static bool tfu_in_use = false; // disallow recursive call of tagfunc +static Callback tfu_cb; // 'tagfunc' callback function // Used instead of NUL to separate tag fields in the growarrays. #define TAG_SEP 0x02 @@ -278,7 +288,7 @@ void set_buflocal_tfu_callback(buf_T *buf) /// @param tag tag (pattern) to jump to /// @param forceit :ta with ! /// @param verbose print "tag not found" message -void do_tag(char *tag, int type, int count, int forceit, int verbose) +void do_tag(char *tag, int type, int count, int forceit, bool verbose) { taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; @@ -287,17 +297,17 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose) int cur_fnum = curbuf->b_fnum; int oldtagstackidx = tagstackidx; int prevtagstackidx = tagstackidx; - int new_tag = false; - int no_regexp = false; + bool new_tag = false; + bool no_regexp = false; int error_cur_match = 0; - int save_pos = false; + bool save_pos = false; fmark_T saved_fmark; int new_num_matches; char **new_matches; - int use_tagstack; - int skip_msg = false; + bool use_tagstack; + bool skip_msg = false; char *buf_ffname = curbuf->b_ffname; // name for priority computation - int use_tfu = 1; + bool use_tfu = true; char *tofree = NULL; // remember the matches for the last used tag @@ -323,7 +333,7 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose) if (type == DT_HELP) { type = DT_TAG; no_regexp = true; - use_tfu = 0; + use_tfu = false; } int prev_num_matches = num_matches; @@ -378,7 +388,7 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose) for (int i = 1; i < tagstacklen; i++) { tagstack[i - 1] = tagstack[i]; } - tagstackidx--; + tagstack[--tagstackidx].user_data = NULL; } // put the tag name in the tag stack @@ -545,7 +555,6 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose) // Repeat searching for tags, when a file has not been found. while (true) { - int other_name; char *name; // When desired match not found yet, try to find it (and others). @@ -559,7 +568,7 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose) } else { name = tag; } - other_name = (tagmatchname == NULL || strcmp(tagmatchname, name) != 0); + bool other_name = (tagmatchname == NULL || strcmp(tagmatchname, name) != 0); if (new_tag || (cur_match >= num_matches && max_num_matches != MAXCOL) || other_name) { @@ -716,7 +725,7 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose) smsg(0, _("File \"%s\" does not exist"), nofile_fname); } - int ic = (matches[cur_match][0] & MT_IC_OFF); + bool ic = (matches[cur_match][0] & MT_IC_OFF); if (type != DT_TAG && type != DT_SELECT && type != DT_JUMP && (num_matches > 1 || ic) && !skip_msg) { @@ -792,7 +801,7 @@ end_do_tag: } // List all the matching tags. -static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char **matches) +static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, char **matches) { taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; @@ -1159,7 +1168,7 @@ static int tag_strnicmp(char *s1, char *s2, size_t len) } // Extract info from the tag search pattern "pats->pat". -static void prepare_pats(pat_T *pats, int has_re) +static void prepare_pats(pat_T *pats, bool has_re) { pats->head = pats->pat; pats->headlen = pats->len; @@ -1274,7 +1283,7 @@ static int find_tagfunc_tags(char *pat, garray_T *ga, int *match_count, int flag char *res_fname; char *res_cmd; char *res_kind; - int has_extra = 0; + bool has_extra = false; int name_only = flags & TAG_NAMES; if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) { @@ -1309,7 +1318,7 @@ static int find_tagfunc_tags(char *pat, garray_T *ga, int *match_count, int flag res_cmd = tv->vval.v_string; continue; } - has_extra = 1; + has_extra = true; if (!strcmp(dict_key, "kind")) { res_kind = tv->vval.v_string; continue; @@ -1529,7 +1538,7 @@ static int findtags_apply_tfu(findtags_state_T *st, char *pat, char *buf_ffname) /// reached end of a emacs included tags file) static tags_read_status_T findtags_get_next_line(findtags_state_T *st, tagsearch_info_T *sinfo_p) { - int eof; + bool eof; // For binary search: compute the next offset to use. if (st->state == TS_BINARY) { @@ -1544,7 +1553,7 @@ static tags_read_status_T findtags_get_next_line(findtags_state_T *st, tagsearch sinfo_p->curr_offset -= st->lbuf_size * 2; if (sinfo_p->curr_offset < 0) { sinfo_p->curr_offset = 0; - (void)fseek(st->fp, 0, SEEK_SET); + fseek(st->fp, 0, SEEK_SET); st->state = TS_STEP_FORWARD; } } @@ -2303,7 +2312,7 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc char *saved_pat = NULL; // copy of pat[] int findall = (mincount == MAXCOL || mincount == TAG_MANY); // find all matching tags - int has_re = (flags & TAG_REGEXP); // regexp used + bool has_re = (flags & TAG_REGEXP); // regexp used int noic = (flags & TAG_NOIC); int verbose = (flags & TAG_VERBOSE); int save_p_ic = p_ic; @@ -2563,7 +2572,7 @@ int get_tagfname(tagname_T *tnp, int first, char *buf) // Copy next file name into buf. buf[0] = NUL; - (void)copy_option_part(&tnp->tn_np, buf, MAXPATHL - 1, " ,"); + copy_option_part(&tnp->tn_np, buf, MAXPATHL - 1, " ,"); char *r_ptr = vim_findfile_stopdir(buf); // move the filename one char forward and truncate the @@ -2773,7 +2782,7 @@ static char *tag_full_fname(tagptrs_T *tagp) /// @param keep_help keep help flag /// /// @return OK for success, NOTAGFILE when file not found, FAIL otherwise. -static int jumpto_tag(const char *lbuf_arg, int forceit, int keep_help) +static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) { char *pbuf_end; char *tofree_fname = NULL; @@ -2946,11 +2955,10 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, int keep_help) // try again, ignore case now p_ic = true; - if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, 1, - search_options, NULL)) { + if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, 1, search_options, NULL)) { // Failed to find pattern, take a guess: "^func (" found = 2; - (void)test_for_static(&tagp); + test_for_static(&tagp); char cc = *tagp.tagname_end; *tagp.tagname_end = NUL; snprintf(pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname); @@ -3170,7 +3178,7 @@ static void tagstack_clear_entry(taggy_T *item) } /// @param tagnames expand tag names -int expand_tags(int tagnames, char *pat, int *num_file, char ***file) +int expand_tags(bool tagnames, char *pat, int *num_file, char ***file) { int extra_flag; size_t name_buf_size = 100; @@ -3202,10 +3210,8 @@ int expand_tags(int tagnames, char *pat, int *num_file, char ***file) parse_match((*file)[i], &t_p); len = (size_t)(t_p.tagname_end - t_p.tagname); if (len > name_buf_size - 3) { - char *buf; - name_buf_size = len + 3; - buf = xrealloc(name_buf, name_buf_size); + char *buf = xrealloc(name_buf, name_buf_size); name_buf = buf; } @@ -3316,12 +3322,11 @@ int get_tags(list_T *list, char *pat, char *buf_fname) // skip "file:" (static tag) p += 4; } else if (!ascii_iswhite(*p)) { - char *n; int len; // Add extra field as a dict entry. Fields are // separated by Tabs. - n = p; + char *n = p; while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') { p++; } diff --git a/src/nvim/tag.h b/src/nvim/tag.h index 87e71c8bef..42196b44b7 100644 --- a/src/nvim/tag.h +++ b/src/nvim/tag.h @@ -1,9 +1,9 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/option_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep enum { LSIZE = 512, }; ///< max. size of a line in the tags file diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 1527738165..b5a3cffe2f 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -43,26 +43,31 @@ #include <vterm.h> #include <vterm_keycodes.h> -#include "nvim/api/private/defs.h" +#include "klib/kvec.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/cursor.h" #include "nvim/drawline.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/event/defs.h" +#include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/time.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" @@ -74,19 +79,23 @@ #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel_defs.h" -#include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/pos_defs.h" #include "nvim/state.h" +#include "nvim/state_defs.h" +#include "nvim/strings.h" #include "nvim/terminal.h" #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/vim_defs.h" +#include "nvim/window.h" -typedef struct terminal_state { +typedef struct { VimState state; Terminal *term; int save_rd; // saved value of RedrawingDisabled @@ -154,6 +163,9 @@ struct terminal { bool color_set[16]; + // When there is a pending TermRequest autocommand, block and store input. + StringBuilder *pending_send; + size_t refcount; // reference count }; @@ -169,6 +181,82 @@ static VTermScreenCallbacks vterm_screen_callbacks = { static Set(ptr_t) invalidated_terminals = SET_INIT; +static void emit_termrequest(void **argv) +{ + Terminal *term = argv[0]; + char *payload = argv[1]; + size_t payload_length = (size_t)argv[2]; + StringBuilder *pending_send = argv[3]; + + buf_T *buf = handle_get_buffer(term->buf_handle); + String termrequest = { .data = payload, .size = payload_length }; + Object data = STRING_OBJ(termrequest); + set_vim_var_string(VV_TERMREQUEST, payload, (ptrdiff_t)payload_length); + apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, false, AUGROUP_ALL, buf, NULL, &data); + xfree(payload); + + StringBuilder *term_pending_send = term->pending_send; + term->pending_send = NULL; + if (kv_size(*pending_send)) { + terminal_send(term, pending_send->items, pending_send->size); + kv_destroy(*pending_send); + } + if (term_pending_send != pending_send) { + term->pending_send = term_pending_send; + } + xfree(pending_send); +} + +static void schedule_termrequest(Terminal *term, char *payload, size_t payload_length) +{ + term->pending_send = xmalloc(sizeof(StringBuilder)); + kv_init(*term->pending_send); + multiqueue_put(main_loop.events, emit_termrequest, term, payload, (void *)payload_length, + term->pending_send); +} + +static int on_osc(int command, VTermStringFragment frag, void *user) +{ + if (frag.str == NULL) { + return 0; + } + if (!has_event(EVENT_TERMREQUEST)) { + return 1; + } + + StringBuilder request = KV_INITIAL_VALUE; + kv_printf(request, "\x1b]%d;", command); + kv_concat_len(request, frag.str, frag.len); + schedule_termrequest(user, request.items, request.size); + return 1; +} + +static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) +{ + if (command == NULL || frag.str == NULL) { + return 0; + } + if (!has_event(EVENT_TERMREQUEST)) { + return 1; + } + + StringBuilder request = KV_INITIAL_VALUE; + kv_printf(request, "\x1bP%*s", (int)commandlen, command); + kv_concat_len(request, frag.str, frag.len); + schedule_termrequest(user, request.items, request.size); + return 1; +} + +static VTermStateFallbacks vterm_fallbacks = { + .control = NULL, + .csi = NULL, + .osc = on_osc, + .dcs = on_dcs, + .apc = NULL, + .pm = NULL, + .sos = NULL, +}; + void terminal_init(void) { time_watcher_init(&main_loop, &refresh_timer, NULL); @@ -189,7 +277,7 @@ void terminal_teardown(void) static void term_output_callback(const char *s, size_t len, void *user_data) { - terminal_send((Terminal *)user_data, (char *)s, len); + terminal_send((Terminal *)user_data, s, len); } // public API {{{ @@ -205,36 +293,37 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) FUNC_ATTR_NONNULL_ALL { // Create a new terminal instance and configure it - Terminal *rv = *termpp = xcalloc(1, sizeof(Terminal)); - rv->opts = opts; - rv->cursor.visible = true; + Terminal *term = *termpp = xcalloc(1, sizeof(Terminal)); + term->opts = opts; + term->cursor.visible = true; // Associate the terminal instance with the new buffer - rv->buf_handle = buf->handle; - buf->terminal = rv; + term->buf_handle = buf->handle; + buf->terminal = term; // Create VTerm - rv->vt = vterm_new(opts.height, opts.width); - vterm_set_utf8(rv->vt, 1); + term->vt = vterm_new(opts.height, opts.width); + vterm_set_utf8(term->vt, 1); // Setup state - VTermState *state = vterm_obtain_state(rv->vt); + VTermState *state = vterm_obtain_state(term->vt); // Set up screen - rv->vts = vterm_obtain_screen(rv->vt); - vterm_screen_enable_altscreen(rv->vts, true); - vterm_screen_enable_reflow(rv->vts, true); + term->vts = vterm_obtain_screen(term->vt); + vterm_screen_enable_altscreen(term->vts, true); + vterm_screen_enable_reflow(term->vts, true); // delete empty lines at the end of the buffer - vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv); - vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL); - vterm_screen_reset(rv->vts, 1); - vterm_output_set_callback(rv->vt, term_output_callback, rv); + vterm_screen_set_callbacks(term->vts, &vterm_screen_callbacks, term); + vterm_screen_set_unrecognised_fallbacks(term->vts, &vterm_fallbacks, term); + vterm_screen_set_damage_merge(term->vts, VTERM_DAMAGE_SCROLL); + vterm_screen_reset(term->vts, 1); + vterm_output_set_callback(term->vt, term_output_callback, term); // force a initial refresh of the screen to ensure the buffer will always // have as many lines as screen rows when refresh_scrollback is called - rv->invalid_start = 0; - rv->invalid_end = opts.height; + term->invalid_start = 0; + term->invalid_end = opts.height; aco_save_T aco; aucmd_prepbuf(&aco, buf); - refresh_screen(rv, buf); - set_option_value("buftype", STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL); + refresh_screen(term, buf); + set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL); // Default settings for terminal buffers buf->b_p_ma = false; // 'nomodifiable' @@ -242,8 +331,8 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) buf->b_p_scbk = // 'scrollback' (initialize local from global) (p_scbk < 0) ? 10000 : MAX(1, p_scbk); buf->b_p_tw = 0; // 'textwidth' - set_option_value("wrap", BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value("list", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value(kOptWrap, BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value(kOptList, BOOLEAN_OPTVAL(false), OPT_LOCAL); if (buf->b_ffname != NULL) { buf_set_term_title(buf, buf->b_ffname, strlen(buf->b_ffname)); } @@ -251,7 +340,7 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) // Reset cursor in current window. curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 }; // Initialize to check if the scrollback buffer has been allocated in a TermOpen autocmd. - rv->sb_buffer = NULL; + term->sb_buffer = NULL; // Apply TermOpen autocmds _before_ configuring the scrollback buffer. apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, buf); @@ -261,14 +350,14 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) return; // Terminal has already been destroyed. } - if (rv->sb_buffer == NULL) { + if (term->sb_buffer == NULL) { // Local 'scrollback' _after_ autocmds. if (buf->b_p_scbk < 1) { buf->b_p_scbk = SB_MAX; } // Configure the scrollback buffer. - rv->sb_size = (size_t)buf->b_p_scbk; - rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); + term->sb_size = (size_t)buf->b_p_scbk; + term->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * term->sb_size); } // Configure the color palette. Try to get the color from: @@ -277,14 +366,12 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) // - g:terminal_color_{NUM} // - the VTerm instance for (int i = 0; i < 16; i++) { - RgbValue color_val = -1; char var[64]; snprintf(var, sizeof(var), "terminal_color_%d", i); char *name = get_config_string(var); if (name) { int dummy; - color_val = name_to_color(name, &dummy); - xfree(name); + RgbValue color_val = name_to_color(name, &dummy); if (color_val != -1) { VTermColor color; @@ -293,7 +380,7 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) (uint8_t)((color_val >> 8) & 0xFF), (uint8_t)((color_val >> 0) & 0xFF)); vterm_state_set_palette_color(state, i, &color); - rv->color_set[i] = true; + term->color_set[i] = true; } } } @@ -388,7 +475,8 @@ void terminal_check_size(Terminal *term) int curwidth, curheight; vterm_get_size(term->vt, &curheight, &curwidth); - uint16_t width = 0, height = 0; + uint16_t width = 0; + uint16_t height = 0; // Check if there is a window that displays the terminal and find the maximum width and height. // Skip the autocommand window which isn't actually displayed. @@ -556,6 +644,8 @@ static int terminal_check(VimState *state) curbuf->b_locked--; } + may_trigger_win_scrolled_resized(); + if (need_maketitle) { // Update title in terminal-mode. #7248 maketitle(); } @@ -681,11 +771,15 @@ void terminal_destroy(Terminal **termpp) } } -void terminal_send(Terminal *term, char *data, size_t size) +static void terminal_send(Terminal *term, const char *data, size_t size) { if (term->closed) { return; } + if (term->pending_send) { + kv_concat_len(*term->pending_send, data, size); + return; + } term->opts.write_cb(data, size, term->opts.data); } @@ -763,7 +857,7 @@ void terminal_paste(int count, char **y_array, size_t y_size) vterm_keyboard_end_paste(curbuf->terminal->vt); } -void terminal_send_key(Terminal *term, int c) +static void terminal_send_key(Terminal *term, int c) { VTermModifier mod = VTERM_MOD_NONE; @@ -781,13 +875,27 @@ void terminal_send_key(Terminal *term, int c) } } -void terminal_receive(Terminal *term, char *data, size_t len) +void terminal_receive(Terminal *term, const char *data, size_t len) { if (!data) { return; } - vterm_input_write(term->vt, data, len); + if (term->opts.force_crlf) { + StringBuilder crlf_data = KV_INITIAL_VALUE; + + for (size_t i = 0; i < len; i++) { + if (data[i] == '\n' && (i == 0 || (i > 0 && data[i - 1] != '\r'))) { + kv_push(crlf_data, '\r'); + } + kv_push(crlf_data, data[i]); + } + + vterm_input_write(term->vt, crlf_data.items, kv_size(crlf_data)); + kv_destroy(crlf_data); + } else { + vterm_input_write(term->vt, data, len); + } vterm_screen_flush_damage(term->vts); } @@ -840,8 +948,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te bool fg_indexed = VTERM_COLOR_IS_INDEXED(&cell.fg); bool bg_indexed = VTERM_COLOR_IS_INDEXED(&cell.bg); - int vt_fg_idx = ((!fg_default && fg_indexed) ? cell.fg.indexed.idx + 1 : 0); - int vt_bg_idx = ((!bg_default && bg_indexed) ? cell.bg.indexed.idx + 1 : 0); + int16_t vt_fg_idx = ((!fg_default && fg_indexed) ? cell.fg.indexed.idx + 1 : 0); + int16_t vt_bg_idx = ((!bg_default && bg_indexed) ? cell.bg.indexed.idx + 1 : 0); bool fg_set = vt_fg_idx && vt_fg_idx <= 16 && term->color_set[vt_fg_idx - 1]; bool bg_set = vt_bg_idx && vt_bg_idx <= 16 && term->color_set[vt_bg_idx - 1]; @@ -866,6 +974,7 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te .rgb_bg_color = vt_bg, .rgb_sp_color = -1, .hl_blend = -1, + .url = -1, }); } @@ -926,6 +1035,7 @@ static void buf_set_term_title(buf_T *buf, const char *title, size_t len) STRING_OBJ(((String){ .data = (char *)title, .size = len })), false, false, + NULL, &err); api_clear_error(&err); status_redraw_buf(buf); @@ -1414,15 +1524,19 @@ static void mouse_action(Terminal *term, int button, int row, int col, bool pres // terminal should lose focus static bool send_mouse_event(Terminal *term, int c) { - int row = mouse_row, col = mouse_col, grid = mouse_grid; + int row = mouse_row; + int col = mouse_col; + int grid = mouse_grid; win_T *mouse_win = mouse_find_win(&grid, &row, &col); if (mouse_win == NULL) { goto end; } int offset; - if (term->forward_mouse && mouse_win->w_buffer->terminal == term - && col >= (offset = win_col_off(mouse_win))) { + if (term->forward_mouse && mouse_win->w_buffer->terminal == term && row >= 0 + && (grid > 1 || row + mouse_win->w_winbar_height < mouse_win->w_height) + && col >= (offset = win_col_off(mouse_win)) + && (grid > 1 || col < mouse_win->w_width)) { // event in the terminal window and mouse events was enabled by the // program. translate and forward the event int button; @@ -1802,10 +1916,10 @@ static char *get_config_string(char *key) { Error err = ERROR_INIT; // Only called from terminal_open where curbuf->terminal is the context. - Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), &err); + Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), NULL, &err); api_clear_error(&err); if (obj.type == kObjectTypeNil) { - obj = dict_get_value(&globvardict, cstr_as_string(key), &err); + obj = dict_get_value(&globvardict, cstr_as_string(key), NULL, &err); api_clear_error(&err); } if (obj.type == kObjectTypeString) { diff --git a/src/nvim/terminal.h b/src/nvim/terminal.h index 66cad7ee7a..317003c0d2 100644 --- a/src/nvim/terminal.h +++ b/src/nvim/terminal.h @@ -1,21 +1,23 @@ #pragma once +#include <stdbool.h> #include <stddef.h> #include <stdint.h> -typedef struct terminal Terminal; -typedef void (*terminal_write_cb)(char *buffer, size_t size, void *data); +#include "nvim/api/private/defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep + +typedef void (*terminal_write_cb)(const char *buffer, size_t size, void *data); typedef void (*terminal_resize_cb)(uint16_t width, uint16_t height, void *data); typedef void (*terminal_close_cb)(void *data); -#include "nvim/buffer_defs.h" // IWYU pragma: keep - typedef struct { void *data; // PTY process channel uint16_t width, height; terminal_write_cb write_cb; terminal_resize_cb resize_cb; terminal_close_cb close_cb; + bool force_crlf; } TerminalOptions; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/testing.c b/src/nvim/testing.c index cada04d276..343568d71e 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -12,17 +12,19 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/os/fs.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/strings.h" #include "nvim/testing.h" #include "nvim/types_defs.h" diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c index b69d438a59..bfe3ed5972 100644 --- a/src/nvim/textformat.c +++ b/src/nvim/textformat.c @@ -14,12 +14,10 @@ #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/indent.h" #include "nvim/indent_c.h" -#include "nvim/macros_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -28,6 +26,7 @@ #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/os/input.h" #include "nvim/pos_defs.h" @@ -79,10 +78,10 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on bool first_line = true; colnr_T leader_len; bool no_leader = false; - int do_comments = (flags & INSCHAR_DO_COM); + bool do_comments = (flags & INSCHAR_DO_COM); int has_lbr = curwin->w_p_lbr; - // make sure win_lbr_chartabsize() counts correctly + // make sure win_charsize() counts correctly curwin->w_p_lbr = false; // When 'ai' is off we don't want a space under the cursor to be @@ -102,17 +101,19 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on int wantcol; // column at textwidth border int foundcol; // column for start of spaces int end_foundcol = 0; // column for start of word - colnr_T virtcol; int orig_col = 0; char *saved_text = NULL; colnr_T col; - colnr_T end_col; bool did_do_comment = false; - virtcol = get_nolist_virtcol() - + char2cells(c != NUL ? c : gchar_cursor()); - if (virtcol <= (colnr_T)textwidth) { - break; + // Cursor is currently at the end of line. No need to format + // if line length is less than textwidth (8 * textwidth for + // utf safety) + if (curwin->w_cursor.col < 8 * textwidth) { + colnr_T virtcol = get_nolist_virtcol() + char2cells(c != NUL ? c : gchar_cursor()); + if (virtcol <= (colnr_T)textwidth) { + break; + } } if (no_leader) { @@ -160,9 +161,16 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on coladvance((colnr_T)textwidth); wantcol = curwin->w_cursor.col; - curwin->w_cursor.col = startcol; + // If startcol is large (a long line), formatting takes too much + // time. The algorithm is O(n^2), it walks from the end of the + // line to textwidth border every time for each line break. + // + // Ceil to 8 * textwidth to optimize. + curwin->w_cursor.col = startcol < 8 * textwidth ? startcol : 8 * textwidth; + foundcol = 0; int skip_pos = 0; + bool first_pass = true; // Find position to break at. // Stop at first entered white when 'formatoptions' has 'v' @@ -170,14 +178,15 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on || (flags & INSCHAR_FORMAT) || curwin->w_cursor.lnum != Insstart.lnum || curwin->w_cursor.col >= Insstart.col) { - if (curwin->w_cursor.col == startcol && c != NUL) { + if (first_pass && c != NUL) { cc = c; + first_pass = false; } else { cc = gchar_cursor(); } if (WHITECHAR(cc)) { // remember position of blank just before text - end_col = curwin->w_cursor.col; + colnr_T end_col = curwin->w_cursor.col; // find start of sequence of blanks int wcc = 0; // counter for whitespace chars @@ -421,7 +430,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on ins_str(" "); } } else { - (void)set_indent(second_indent, SIN_CHANGED); + set_indent(second_indent, SIN_CHANGED); } } } @@ -474,9 +483,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on static int fmt_check_par(linenr_T lnum, int *leader_len, char **leader_flags, bool do_comments) { char *flags = NULL; // init for GCC - char *ptr; - - ptr = ml_get(lnum); + char *ptr = ml_get(lnum); if (do_comments) { *leader_len = get_leader_len(ptr, leader_flags, false, true); } else { @@ -626,8 +633,6 @@ static bool paragraph_start(linenr_T lnum) /// @param prev_line may start in previous line void auto_format(bool trailblank, bool prev_line) { - char *linep; - if (!has_format_option(FO_AUTO)) { return; } @@ -679,7 +684,7 @@ void auto_format(bool trailblank, bool prev_line) // Do the formatting and restore the cursor position. "saved_cursor" will // be adjusted for the text formatting. saved_cursor = pos; - format_lines((linenr_T) - 1, false); + format_lines(-1, false); curwin->w_cursor = saved_cursor; saved_cursor.lnum = 0; @@ -696,7 +701,7 @@ void auto_format(bool trailblank, bool prev_line) // need to add a space when 'w' is in 'formatoptions' to keep a paragraph // formatted. if (!wasatend && has_format_option(FO_WHITE_PAR)) { - linep = get_cursor_line_ptr(); + char *linep = get_cursor_line_ptr(); colnr_T len = (colnr_T)strlen(linep); if (curwin->w_cursor.col == len) { char *plinep = xstrnsave(linep, (size_t)len + 2); @@ -758,11 +763,11 @@ int comp_textwidth(bool ff) // The width is the window width minus 'wrapmargin' minus all the // things that add to the margin. textwidth = curwin->w_width_inner - (int)curbuf->b_p_wm; - if (cmdwin_type != 0) { + if (curbuf == cmdwin_buf) { textwidth -= 1; } textwidth -= win_fdccol_count(curwin); - textwidth -= win_signcol_count(curwin); + textwidth -= curwin->w_scwidth; if (curwin->w_p_nu || curwin->w_p_rnu) { textwidth -= 8; @@ -871,7 +876,7 @@ void op_formatexpr(oparg_T *oap) /// @param c character to be inserted int fex_format(linenr_T lnum, long count, int c) { - int use_sandbox = was_set_insecurely(curwin, "formatexpr", OPT_LOCAL); + bool use_sandbox = was_set_insecurely(curwin, kOptFormatexpr, OPT_LOCAL); const sctx_T save_sctx = current_sctx; // Set v:lnum to the first line number and v:count to the number of lines. @@ -1054,7 +1059,7 @@ void format_lines(linenr_T line_count, bool avoid_fex) indent = get_indent(); } } - (void)set_indent(indent, SIN_CHANGED); + set_indent(indent, SIN_CHANGED); } // put cursor on last non-space @@ -1098,13 +1103,13 @@ void format_lines(linenr_T line_count, bool avoid_fex) break; } if (next_leader_len > 0) { - (void)del_bytes(next_leader_len, false, false); + del_bytes(next_leader_len, false, false); mark_col_adjust(curwin->w_cursor.lnum, 0, 0, -next_leader_len, 0); } else if (second_indent > 0) { // the "leader" for FO_Q_SECOND int indent = (int)getwhitecols_curline(); if (indent > 0) { - (void)del_bytes(indent, false, false); + del_bytes(indent, false, false); mark_col_adjust(curwin->w_cursor.lnum, 0, 0, -indent, 0); } } diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c index d4310d47a4..d9c2b3b111 100644 --- a/src/nvim/textobject.c +++ b/src/nvim/textobject.c @@ -6,16 +6,16 @@ #include <string.h> #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" #include "nvim/eval/funcs.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/indent.h" -#include "nvim/macros_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -170,14 +170,13 @@ found: /// @return true if the next paragraph or section was found. bool findpar(bool *pincl, int dir, int count, int what, bool both) { - linenr_T curr; bool first; // true on first line linenr_T fold_first; // first line of a closed fold linenr_T fold_last; // last line of a closed fold bool fold_skipped; // true if a closed fold was skipped this // iteration - curr = curwin->w_cursor.lnum; + linenr_T curr = curwin->w_cursor.lnum; while (count--) { bool did_skip = false; // true after separating lines have been skipped @@ -214,7 +213,7 @@ bool findpar(bool *pincl, int dir, int count, int what, bool both) curr++; } curwin->w_cursor.lnum = curr; - if (curr == curbuf->b_ml.ml_line_count && what != '}') { + if (curr == curbuf->b_ml.ml_line_count && what != '}' && dir == FORWARD) { char *line = ml_get(curr); // Put the cursor on the last character in the last line and make the @@ -260,9 +259,7 @@ static bool inmacro(char *opt, const char *s) /// If 'both' is true also stop at '}' bool startPS(linenr_T lnum, int para, bool both) { - char *s; - - s = ml_get(lnum); + char *s = ml_get(lnum); if ((uint8_t)(*s) == para || *s == '\f' || (both && *s == '}')) { return true; } @@ -294,9 +291,7 @@ static bool cls_bigword; ///< true for "W", "B" or "E" /// boundaries are of interest. static int cls(void) { - int c; - - c = gchar_cursor(); + int c = gchar_cursor(); if (c == ' ' || c == '\t' || c == NUL) { return 0; } @@ -535,9 +530,7 @@ static bool skip_chars(int cclass, int dir) /// Go back to the start of the word or the start of white space static void back_in_line(void) { - int sclass; // starting class - - sclass = cls(); + int sclass = cls(); // starting class while (true) { if (curwin->w_cursor.col == 0) { // stop at start of line break; @@ -587,7 +580,7 @@ int current_word(oparg_T *oap, int count, bool include, bool bigword) { pos_T start_pos; bool inclusive = true; - int include_white = false; + bool include_white = false; cls_bigword = bigword; clearpos(&start_pos); @@ -724,15 +717,13 @@ int current_word(oparg_T *oap, int count, bool include, bool bigword) /// When Visual active, extend it by one or more sentences. int current_sent(oparg_T *oap, int count, bool include) { - pos_T start_pos; - pos_T pos; bool start_blank; int c; bool at_start_sent; int ncount; - start_pos = curwin->w_cursor; - pos = start_pos; + pos_T start_pos = curwin->w_cursor; + pos_T pos = start_pos; findsent(FORWARD, 1); // Find start of next sentence. // When the Visual area is bigger than one character: Extend it. @@ -965,6 +956,13 @@ int current_block(oparg_T *oap, int count, bool include, int what, int other) } } + // In Visual mode, when resulting area is empty + // i.e. there is no inner block to select, abort. + if (equalpos(start_pos, *end_pos) && VIsual_active) { + curwin->w_cursor = old_pos; + return FAIL; + } + // In Visual mode, when the resulting area is not bigger than what we // started with, extend it to the next block, and then exclude again. // Don't try to expand the area if the area is empty. @@ -1080,7 +1078,7 @@ int current_tagblock(oparg_T *oap, int count_arg, bool include) bool do_include = include; bool save_p_ws = p_ws; int retval = FAIL; - int is_inclusive = true; + bool is_inclusive = true; p_ws = false; @@ -1315,7 +1313,7 @@ extend: } // First move back to the start_lnum of the paragraph or white lines - int white_in_front = linewhite(start_lnum); + bool white_in_front = linewhite(start_lnum); while (start_lnum > 1) { if (white_in_front) { // stop at first white line if (!linewhite(start_lnum - 1)) { diff --git a/src/nvim/textobject.h b/src/nvim/textobject.h index a540c7c98f..735a2fec3d 100644 --- a/src/nvim/textobject.h +++ b/src/nvim/textobject.h @@ -2,7 +2,7 @@ #include "nvim/normal_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep -#include "nvim/vim_defs.h" +#include "nvim/vim_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "textobject.h.generated.h" diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index bdbb5e4872..214474ff51 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -6,14 +6,16 @@ #include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/event/defs.h" -#include "nvim/func_attr.h" +#include "nvim/event/loop.h" +#include "nvim/event/stream.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/map_defs.h" #include "nvim/memory.h" #include "nvim/option_vars.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" +#include "nvim/rbuffer.h" #include "nvim/strings.h" #include "nvim/tui/input.h" #include "nvim/tui/input_defs.h" @@ -28,6 +30,11 @@ #define READ_STREAM_SIZE 0xfff #define KEY_BUFFER_SIZE 0xfff +/// Size of libtermkey's internal input buffer. The buffer may grow larger than +/// this when processing very long escape sequences, but will shrink back to +/// this size afterward +#define INPUT_BUFFER_SIZE 256 + static const struct kitty_key_map_entry { int key; const char *name; @@ -140,6 +147,7 @@ void tinput_init(TermInput *input, Loop *loop) input->tk = termkey_new_abstract(term, TERMKEY_FLAG_UTF8 | TERMKEY_FLAG_NOSTART); + termkey_set_buffer_size(input->tk, INPUT_BUFFER_SIZE); termkey_hook_terminfo_getstr(input->tk, input->tk_ti_hook_fn, input); termkey_start(input->tk); @@ -148,17 +156,17 @@ void tinput_init(TermInput *input, Loop *loop) // setup input handle rstream_init_fd(loop, &input->read_stream, input->in_fd, READ_STREAM_SIZE); - termkey_set_buffer_size(input->tk, rbuffer_capacity(input->read_stream.buffer)); // initialize a timer handle for handling ESC with libtermkey - time_watcher_init(loop, &input->timer_handle, input); + uv_timer_init(&loop->uv, &input->timer_handle); + input->timer_handle.data = input; } void tinput_destroy(TermInput *input) { map_destroy(int, &kitty_key_map); rbuffer_free(input->key_buffer); - time_watcher_close(&input->timer_handle, NULL); + uv_close((uv_handle_t *)&input->timer_handle, NULL); stream_close(&input->read_stream, NULL, NULL); termkey_destroy(input->tk); } @@ -171,7 +179,7 @@ void tinput_start(TermInput *input) void tinput_stop(TermInput *input) { rstream_stop(&input->read_stream); - time_watcher_stop(&input->timer_handle); + uv_timer_stop(&input->timer_handle); } static void tinput_done_event(void **argv) @@ -231,13 +239,13 @@ static size_t handle_termkey_modifiers(TermKeyKey *key, char *buf, size_t buflen { size_t len = 0; if (key->modifiers & TERMKEY_KEYMOD_SHIFT) { // Shift - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "S-"); + len += (size_t)snprintf(buf + len, buflen - len, "S-"); } if (key->modifiers & TERMKEY_KEYMOD_ALT) { // Alt - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "A-"); + len += (size_t)snprintf(buf + len, buflen - len, "A-"); } if (key->modifiers & TERMKEY_KEYMOD_CTRL) { // Ctrl - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "C-"); + len += (size_t)snprintf(buf + len, buflen - len, "C-"); } assert(len < buflen); return len; @@ -364,15 +372,6 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) button = last_pressed_button; } - if (ev == TERMKEY_MOUSE_UNKNOWN && !(key->code.mouse[0] & 0x20)) { - int code = key->code.mouse[0] & ~0x3c; - // https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Other-buttons - if (code == 66 || code == 67) { - ev = TERMKEY_MOUSE_PRESS; - button = code + 4 - 64; - } - } - if ((button == 0 && ev != TERMKEY_MOUSE_RELEASE) || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG && ev != TERMKEY_MOUSE_RELEASE)) { return; @@ -461,17 +460,16 @@ static void tk_getkeys(TermInput *input, bool force) if (input->ttimeout && input->ttimeoutlen >= 0) { // Stop the current timer if already running - time_watcher_stop(&input->timer_handle); - time_watcher_start(&input->timer_handle, tinput_timer_cb, - (uint64_t)input->ttimeoutlen, 0); + uv_timer_stop(&input->timer_handle); + uv_timer_start(&input->timer_handle, tinput_timer_cb, (uint64_t)input->ttimeoutlen, 0); } else { tk_getkeys(input, true); } } -static void tinput_timer_cb(TimeWatcher *watcher, void *data) +static void tinput_timer_cb(uv_timer_t *handle) { - TermInput *input = (TermInput *)data; + TermInput *input = handle->data; // If the raw buffer is not empty, process the raw buffer first because it is // processing an incomplete bracketed paster sequence. if (rbuffer_size(input->read_stream.buffer)) { @@ -484,8 +482,8 @@ static void tinput_timer_cb(TimeWatcher *watcher, void *data) /// Handle focus events. /// /// If the upcoming sequence of bytes in the input stream matches the termcode -/// for "focus gained" or "focus lost", consume that sequence and schedule an -/// event on the main loop. +/// for "focus gained" or "focus lost", consume that sequence and send an event +/// to Nvim server. /// /// @param input the input stream /// @return true iff handle_focus_event consumed some input @@ -692,20 +690,44 @@ static void handle_raw_buffer(TermInput *input, bool force) } // Push through libtermkey (translates to "<keycode>" strings, etc.). RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) { - size_t consumed = termkey_push_bytes(input->tk, ptr, MIN(count, len)); - // termkey_push_bytes can return (size_t)-1, so it is possible that - // `consumed > rbuffer_size(input->read_stream.buffer)`, but since tk_getkeys is - // called soon, it shouldn't happen. + const size_t size = MIN(count, len); + if (size > termkey_get_buffer_remaining(input->tk)) { + // We are processing a very long escape sequence. Increase termkey's + // internal buffer size. We don't handle out of memory situations so + // abort if it fails + const size_t delta = size - termkey_get_buffer_remaining(input->tk); + const size_t bufsize = termkey_get_buffer_size(input->tk); + if (!termkey_set_buffer_size(input->tk, MAX(bufsize + delta, bufsize * 2))) { + abort(); + } + } + + size_t consumed = termkey_push_bytes(input->tk, ptr, size); + + // We resize termkey's buffer when it runs out of space, so this should + // never happen assert(consumed <= rbuffer_size(input->read_stream.buffer)); rbuffer_consumed(input->read_stream.buffer, consumed); - // Process the keys now: there is no guarantee `count` will - // fit into libtermkey's input buffer. + + // Process the input buffer now for any keys tk_getkeys(input, false); + if (!(count -= consumed)) { break; } } } while (rbuffer_size(input->read_stream.buffer)); + + const size_t tk_size = termkey_get_buffer_size(input->tk); + const size_t tk_remaining = termkey_get_buffer_remaining(input->tk); + const size_t tk_count = tk_size - tk_remaining; + if (tk_count < INPUT_BUFFER_SIZE && tk_size > INPUT_BUFFER_SIZE) { + // If the termkey buffer was resized to handle a large input sequence then + // shrink it back down to its original size. + if (!termkey_set_buffer_size(input->tk, INPUT_BUFFER_SIZE)) { + abort(); + } + } } static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data, bool eof) @@ -713,7 +735,7 @@ static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, void *da TermInput *input = data; if (eof) { - loop_schedule_fast(&main_loop, event_create(tinput_done_event, 0)); + loop_schedule_fast(&main_loop, event_create(tinput_done_event, NULL)); return; } @@ -728,8 +750,8 @@ static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, void *da int64_t ms = input->ttimeout ? (input->ttimeoutlen >= 0 ? input->ttimeoutlen : 0) : 0; // Stop the current timer if already running - time_watcher_stop(&input->timer_handle); - time_watcher_start(&input->timer_handle, tinput_timer_cb, (uint32_t)ms, 0); + uv_timer_stop(&input->timer_handle); + uv_timer_start(&input->timer_handle, tinput_timer_cb, (uint32_t)ms, 0); return; } diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h index 2743a5e286..bf6d0f2978 100644 --- a/src/nvim/tui/input.h +++ b/src/nvim/tui/input.h @@ -2,16 +2,14 @@ #include <stdbool.h> #include <stdint.h> -#include <termkey.h> #include <uv.h> -#include "nvim/event/loop.h" -#include "nvim/event/stream.h" -#include "nvim/event/time.h" -#include "nvim/rbuffer.h" -#include "nvim/tui/input_defs.h" // IWYU pragma: export -#include "nvim/tui/tui.h" +#include "nvim/event/defs.h" +#include "nvim/rbuffer_defs.h" +#include "nvim/tui/input_defs.h" // IWYU pragma: keep +#include "nvim/tui/tui_defs.h" #include "nvim/types_defs.h" +#include "termkey/termkey.h" typedef enum { kKeyEncodingLegacy, ///< Legacy key encoding @@ -33,7 +31,7 @@ typedef struct { OptInt ttimeoutlen; TermKey *tk; TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook - TimeWatcher timer_handle; + uv_timer_t timer_handle; Loop *loop; Stream read_stream; RBuffer *key_buffer; diff --git a/src/nvim/tui/terminfo_defs.h b/src/nvim/tui/terminfo_defs.h index e221e80d1e..74ba23e4d9 100644 --- a/src/nvim/tui/terminfo_defs.h +++ b/src/nvim/tui/terminfo_defs.h @@ -1,6 +1,6 @@ // uncrustify:off -// Generated by scripts/update_terminfo.sh and ncurses 6.4.20221231 +// Generated by scripts/update_terminfo.sh and ncurses 6.4.20230520 #pragma once @@ -158,6 +158,7 @@ static const int8_t ansi_terminfo[] = { // key_a3=\EOy, // key_b2=\E[G, // key_backspace=^H, +// key_beg=\EOE, // key_btab=\E[Z, // key_c1=\EOq, // key_c3=\EOs, @@ -279,7 +280,9 @@ static const int8_t ansi_terminfo[] = { // set_a_background=\E[48;5;%p1%dm, // set_a_foreground=\E[38;5;%p1%dm, // set_attributes=\E[0%?%p1%p3%|%t;7%;%?%p2%t;4%;%?%p6%t;1%;m, +// set_left_margin_parm@, // set_lr_margin@, +// set_right_margin_parm@, // set_tab@, // tab=^I, // user6@, @@ -287,7 +290,7 @@ static const int8_t ansi_terminfo[] = { // user8@, // user9@, static const int8_t conemu_terminfo[] = { - 30,2,61,0,38,0,15,0,-99,1,57,3,99,111,110,101,109,117,124,65,78,73,83,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,69,0,73,0,80,0,-1,-1,82,0,89,0,-1,-1,93,0,-2,-1,97,0,101,0,-1,-1,-1,-1,-2,-1,-2,-1,105,0,110,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,119,0,124,0,-127,0,-122,0,-2,-1,-113,0,-108,0,-1,-1,-2,-1,-99,0,-93,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-87,0,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-81,0,-1,-1,-76,0,-1,-1,-1,-1,-1,-1,-1,-1,-72,0,-67,0,-61,0,-56,0,-51,0,-46,0,-41,0,-35,0,-29,0,-23,0,-17,0,-12,0,-1,-1,-7,0,-1,-1,-3,0,2,1,7,1,11,1,18,1,-1,-1,25,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,29,1,-1,-1,32,1,41,1,50,1,59,1,68,1,77,1,86,1,95,1,104,1,113,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,122,1,-2,-1,-2,-1,-1,-1,-1,-1,-114,1,-111,1,-100,1,-97,1,-95,1,-92,1,-2,-1,-1,-1,-49,1,-1,-1,-1,-1,-1,-1,-1,-1,-47,1,-43,1,-39,1,-35,1,-31,1,-1,-1,-1,-1,-2,-1,-1,-1,-27,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-23,1,-18,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-14,1,-1,-1,-1,-1,-7,1,-1,-1,-1,-1,-1,-1,-1,-1,0,2,7,2,14,2,-1,-1,-1,-1,21,2,-1,-1,28,2,-1,-1,-1,-1,-1,-1,35,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,42,2,48,2,54,2,60,2,66,2,72,2,78,2,84,2,90,2,96,2,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-94,2,-88,2,-82,2,-76,2,-70,2,-64,2,-58,2,-52,2,-46,2,-40,2,-33,2,-27,2,-21,2,-15,2,-9,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-3,2,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,9,3,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,18,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,29,3,43,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,0,2,0,0,0,74,0,92,0,-46,1,1,1,-2,-1,-2,-1,0,0,-2,-1,5,0,11,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,28,0,32,0,36,0,40,0,44,0,48,0,52,0,56,0,60,0,64,0,68,0,72,0,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0 + 30,2,61,0,38,0,15,0,-99,1,61,3,99,111,110,101,109,117,124,65,78,73,83,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,69,0,73,0,80,0,-1,-1,82,0,89,0,-1,-1,93,0,-2,-1,97,0,101,0,-1,-1,-1,-1,-2,-1,-2,-1,105,0,110,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,119,0,124,0,-127,0,-122,0,-2,-1,-113,0,-108,0,-1,-1,-2,-1,-99,0,-93,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-87,0,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-81,0,-1,-1,-76,0,-1,-1,-1,-1,-1,-1,-1,-1,-72,0,-67,0,-61,0,-56,0,-51,0,-46,0,-41,0,-35,0,-29,0,-23,0,-17,0,-12,0,-1,-1,-7,0,-1,-1,-3,0,2,1,7,1,11,1,18,1,-1,-1,25,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,29,1,-1,-1,32,1,41,1,50,1,59,1,68,1,77,1,86,1,95,1,104,1,113,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,122,1,-2,-1,-2,-1,-1,-1,-1,-1,-114,1,-111,1,-100,1,-97,1,-95,1,-92,1,-2,-1,-1,-1,-49,1,-1,-1,-1,-1,-1,-1,-1,-1,-47,1,-43,1,-39,1,-35,1,-31,1,-1,-1,-1,-1,-2,-1,-1,-1,-27,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-23,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-19,1,-14,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-10,1,-1,-1,-1,-1,-3,1,-1,-1,-1,-1,-1,-1,-1,-1,4,2,11,2,18,2,-1,-1,-1,-1,25,2,-1,-1,32,2,-1,-1,-1,-1,-1,-1,39,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,46,2,52,2,58,2,64,2,70,2,76,2,82,2,88,2,94,2,100,2,106,2,112,2,118,2,124,2,-126,2,-120,2,-114,2,-108,2,-102,2,-96,2,-90,2,-84,2,-78,2,-72,2,-66,2,-60,2,-54,2,-48,2,-42,2,-36,2,-29,2,-23,2,-17,2,-11,2,-5,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,1,3,6,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,13,3,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,33,3,47,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,27,91,90,0,27,79,69,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,0,3,0,0,0,86,0,117,0,121,2,1,1,1,0,0,0,9,0,-2,-1,-2,-1,18,0,-2,-1,23,0,30,0,37,0,42,0,48,0,-2,-1,58,0,64,0,73,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,82,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,89,0,93,0,97,0,101,0,105,0,109,0,113,0,117,0,121,0,125,0,-127,0,-123,0,-119,0,-115,0,-2,-1,-111,0,-2,-1,-2,-1,-86,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,51,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-83,0,-78,0,-73,0,-68,0,-63,0,-57,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,61,1,66,1,71,1,76,1,81,1,86,1,90,1,94,1,98,1,102,1,106,1,112,1,118,1,124,1,-126,1,-120,1,-114,1,-108,1,-103,1,-97,1,-92,1,-89,1,-84,1,-81,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,51,74,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,73,0,27,91,79,0,27,92,91,52,49,59,91,49,45,54,93,91,48,45,57,93,91,48,45,57,93,59,48,99,0,27,80,62,92,124,88,84,101,114,109,92,40,91,49,45,57,93,91,48,45,57,93,43,92,41,27,92,92,0,65,88,0,88,70,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,101,0,83,115,0,88,77,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,109,0,120,114,0 }; // cygwin|ANSI emulation for Cygwin, @@ -670,10 +673,10 @@ static const int8_t interix_8colour_terminfo[] = { // user8=\E[?%[;0123456789]c, // user9=\E[c, static const int8_t iterm_256colour_terminfo[] = { - 30,2,49,0,29,0,15,0,105,1,-29,3,105,84,101,114,109,50,46,97,112,112,124,105,116,101,114,109,50,124,116,101,114,109,105,110,97,108,32,101,109,117,108,97,116,111,114,32,102,111,114,32,77,97,99,32,79,83,32,88,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,1,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,50,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,95,0,-1,-1,99,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-109,0,-104,0,-1,-1,-1,-1,-99,0,-94,0,-89,0,-1,-1,-84,0,-82,0,-77,0,-1,-1,-59,0,-54,0,-48,0,-42,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-22,0,-18,0,-1,-1,-14,0,-1,-1,-1,-1,-1,-1,-12,0,-1,-1,-7,0,-1,-1,-1,-1,-1,-1,-1,-1,-3,0,1,1,7,1,11,1,15,1,19,1,25,1,31,1,37,1,43,1,49,1,-1,-1,-1,-1,53,1,-1,-1,57,1,62,1,67,1,71,1,78,1,-1,-1,85,1,89,1,97,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,105,1,-1,-1,108,1,117,1,126,1,-121,1,-112,1,-103,1,-94,1,-85,1,-76,1,-67,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,1,-1,-1,-1,-1,-32,1,-29,1,-18,1,-15,1,-13,1,-10,1,68,2,-1,-1,71,2,73,2,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,78,2,-1,-1,-127,2,-1,-1,-1,-1,-123,2,-117,2,-1,-1,-1,-1,-111,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-104,2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-100,2,-1,-1,-1,-1,-1,-1,-1,-1,-93,2,-1,-1,-86,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-79,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-72,2,-66,2,-60,2,-53,2,-46,2,-39,2,-32,2,-24,2,-16,2,-8,2,0,3,8,3,16,3,24,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,37,3,48,3,53,3,72,3,76,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,85,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,3,-1,-1,-1,-1,-1,-1,100,3,-93,3,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,50,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,7,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,50,59,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,79,70,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,49,59,50,68,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,0,0,0,0,33,0,66,0,-37,1,0,0,5,0,32,0,37,0,45,0,52,0,59,0,66,0,74,0,81,0,88,0,96,0,104,0,111,0,119,0,126,0,-123,0,-115,0,-107,0,-102,0,-94,0,-87,0,-80,0,-74,0,-68,0,-63,0,-55,0,-48,0,-41,0,-36,0,-28,0,-21,0,-14,0,0,0,3,0,6,0,11,0,16,0,21,0,26,0,32,0,38,0,44,0,50,0,56,0,62,0,68,0,74,0,80,0,86,0,92,0,98,0,104,0,110,0,116,0,122,0,-128,0,-122,0,-116,0,-110,0,-104,0,-98,0,-93,0,-88,0,-83,0,-78,0,27,93,50,59,0,27,91,63,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,27,91,66,0,27,91,49,59,49,48,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,57,70,0,27,91,49,59,49,48,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,49,51,70,0,27,91,49,59,49,52,70,0,27,91,49,59,57,72,0,27,91,49,59,49,48,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,49,51,72,0,27,91,49,59,49,52,72,0,27,27,91,68,0,27,91,49,59,49,48,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,27,91,54,126,0,27,27,91,53,126,0,27,27,91,67,0,27,91,49,59,49,48,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,27,91,65,0,27,91,49,59,49,48,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,77,37,63,37,112,52,37,116,37,112,51,37,101,37,123,51,125,37,59,37,39,32,39,37,43,37,99,37,112,50,37,39,33,39,37,43,37,99,37,112,49,37,39,33,39,37,43,37,99,0,84,83,0,88,77,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,78,88,84,51,0,107,80,82,86,51,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,120,109,0 + 30,2,49,0,29,0,15,0,105,1,-29,3,105,84,101,114,109,50,46,97,112,112,124,105,116,101,114,109,50,124,116,101,114,109,105,110,97,108,32,101,109,117,108,97,116,111,114,32,102,111,114,32,77,97,99,32,79,83,32,88,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,1,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,50,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,95,0,-1,-1,99,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-109,0,-104,0,-1,-1,-1,-1,-99,0,-94,0,-89,0,-1,-1,-84,0,-82,0,-77,0,-1,-1,-59,0,-54,0,-48,0,-42,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-22,0,-18,0,-1,-1,-14,0,-1,-1,-1,-1,-1,-1,-12,0,-1,-1,-7,0,-1,-1,-1,-1,-1,-1,-1,-1,-3,0,1,1,7,1,11,1,15,1,19,1,25,1,31,1,37,1,43,1,49,1,-1,-1,-1,-1,53,1,-1,-1,57,1,62,1,67,1,71,1,78,1,-1,-1,85,1,89,1,97,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,105,1,-1,-1,108,1,117,1,126,1,-121,1,-112,1,-103,1,-94,1,-85,1,-76,1,-67,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,1,-1,-1,-1,-1,-32,1,-29,1,-18,1,-15,1,-13,1,-10,1,68,2,-1,-1,71,2,73,2,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,78,2,-1,-1,-127,2,-1,-1,-1,-1,-123,2,-117,2,-1,-1,-1,-1,-111,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-104,2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-100,2,-1,-1,-1,-1,-1,-1,-1,-1,-93,2,-1,-1,-86,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-79,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-72,2,-66,2,-60,2,-53,2,-46,2,-39,2,-32,2,-24,2,-16,2,-8,2,0,3,8,3,16,3,24,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,37,3,48,3,53,3,72,3,76,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,85,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,3,-1,-1,-1,-1,-1,-1,100,3,-93,3,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,50,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,7,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,50,59,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,79,70,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,49,59,50,68,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,0,0,0,0,37,0,74,0,7,2,0,0,9,0,18,0,25,0,32,0,37,0,64,0,69,0,77,0,84,0,91,0,98,0,106,0,113,0,120,0,-128,0,-120,0,-113,0,-105,0,-98,0,-91,0,-83,0,-75,0,-70,0,-62,0,-55,0,-48,0,-42,0,-36,0,-31,0,-23,0,-16,0,-9,0,-4,0,4,1,11,1,18,1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,23,0,28,0,33,0,38,0,44,0,50,0,56,0,62,0,68,0,74,0,80,0,86,0,92,0,98,0,104,0,110,0,116,0,122,0,-128,0,-122,0,-116,0,-110,0,-104,0,-98,0,-92,0,-86,0,-81,0,-76,0,-71,0,-66,0,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,93,50,59,0,27,91,63,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,27,91,66,0,27,91,49,59,49,48,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,57,70,0,27,91,49,59,49,48,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,49,51,70,0,27,91,49,59,49,52,70,0,27,91,49,59,57,72,0,27,91,49,59,49,48,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,49,51,72,0,27,91,49,59,49,52,72,0,27,27,91,68,0,27,91,49,59,49,48,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,27,91,54,126,0,27,27,91,53,126,0,27,27,91,67,0,27,91,49,59,49,48,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,27,91,65,0,27,91,49,59,49,48,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,77,37,63,37,112,52,37,116,37,112,51,37,101,37,123,51,125,37,59,37,39,32,39,37,43,37,99,37,112,50,37,39,33,39,37,43,37,99,37,112,49,37,39,33,39,37,43,37,99,0,66,68,0,66,69,0,80,69,0,80,83,0,84,83,0,88,77,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,78,88,84,51,0,107,80,82,86,51,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,120,109,0 }; -// linux|linux console, +// linux|Linux console, // auto_right_margin, // back_color_erase, // can_change, @@ -732,7 +735,7 @@ static const int8_t iterm_256colour_terminfo[] = { // insert_line=\E[L, // key_b2=\E[G, // key_backspace=\177, -// key_btab=\E[Z, +// key_btab=\E^I, // key_dc=\E[3~, // key_down=\E[B, // key_end=\E[4~, @@ -792,7 +795,7 @@ static const int8_t iterm_256colour_terminfo[] = { // user8=\E[?6c, // user9=\E[c, static const int8_t linux_16colour_terminfo[] = { - 26,1,20,0,29,0,16,0,125,1,67,3,108,105,110,117,120,124,108,105,110,117,120,32,99,111,110,115,111,108,101,0,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,18,0,-1,-1,0,0,2,0,4,0,21,0,26,0,33,0,37,0,41,0,-1,-1,52,0,69,0,71,0,75,0,87,0,-1,-1,89,0,101,0,-1,-1,105,0,109,0,121,0,125,0,-1,-1,-1,-1,-127,0,-125,0,-120,0,-1,-1,-1,-1,-115,0,-110,0,-1,-1,-1,-1,-105,0,-100,0,-95,0,-90,0,-81,0,-79,0,-1,-1,-1,-1,-74,0,-69,0,-63,0,-57,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,0,-35,0,-1,-1,-31,0,-1,-1,-1,-1,-1,-1,-29,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-20,0,-15,0,-9,0,-4,0,1,1,6,1,11,1,17,1,23,1,29,1,35,1,40,1,-1,-1,45,1,-1,-1,49,1,54,1,59,1,-1,-1,-1,-1,-1,-1,63,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,67,1,-1,-1,70,1,79,1,88,1,97,1,-1,-1,106,1,115,1,124,1,-1,-1,-123,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-114,1,-1,-1,-1,-1,-1,-1,-108,1,-105,1,-94,1,-91,1,-89,1,-86,1,1,2,-1,-1,4,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,6,2,-1,-1,-1,-1,-1,-1,-1,-1,10,2,-1,-1,75,2,-1,-1,-1,-1,79,2,85,2,-1,-1,-1,-1,91,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,95,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,100,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-94,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-89,2,-78,2,-73,2,-67,2,-63,2,-54,2,-50,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,31,3,-1,-1,-1,-1,-1,-1,35,3,45,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,55,3,61,3,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,27,91,63,49,99,0,8,0,27,91,63,50,53,104,27,91,63,48,99,0,27,91,67,0,27,91,65,0,27,91,63,50,53,104,27,91,63,56,99,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,93,82,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,54,37,116,59,49,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,71,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,41,48,0,27,91,52,126,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,93,80,37,112,49,37,120,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,0,27,91,77,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,49,109,0,27,91,49,48,109,0,0,1,0,1,0,1,0,4,0,14,0,1,0,1,0,0,0,0,0,3,0,6,0,27,91,51,74,0,65,88,0,85,56,0,69,51,0 + 26,1,20,0,29,0,16,0,125,1,66,3,108,105,110,117,120,124,76,105,110,117,120,32,99,111,110,115,111,108,101,0,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,18,0,-1,-1,0,0,2,0,4,0,21,0,26,0,33,0,37,0,41,0,-1,-1,52,0,69,0,71,0,75,0,87,0,-1,-1,89,0,101,0,-1,-1,105,0,109,0,121,0,125,0,-1,-1,-1,-1,-127,0,-125,0,-120,0,-1,-1,-1,-1,-115,0,-110,0,-1,-1,-1,-1,-105,0,-100,0,-95,0,-90,0,-81,0,-79,0,-1,-1,-1,-1,-74,0,-69,0,-63,0,-57,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,0,-35,0,-1,-1,-31,0,-1,-1,-1,-1,-1,-1,-29,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-20,0,-15,0,-9,0,-4,0,1,1,6,1,11,1,17,1,23,1,29,1,35,1,40,1,-1,-1,45,1,-1,-1,49,1,54,1,59,1,-1,-1,-1,-1,-1,-1,63,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,67,1,-1,-1,70,1,79,1,88,1,97,1,-1,-1,106,1,115,1,124,1,-1,-1,-123,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-114,1,-1,-1,-1,-1,-1,-1,-108,1,-105,1,-94,1,-91,1,-89,1,-86,1,1,2,-1,-1,4,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,6,2,-1,-1,-1,-1,-1,-1,-1,-1,10,2,-1,-1,75,2,-1,-1,-1,-1,78,2,84,2,-1,-1,-1,-1,90,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,94,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,99,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,101,2,107,2,113,2,119,2,125,2,-125,2,-119,2,-113,2,-107,2,-101,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-95,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-90,2,-79,2,-74,2,-68,2,-64,2,-55,2,-51,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,30,3,-1,-1,-1,-1,-1,-1,34,3,44,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,54,3,60,3,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,27,91,63,49,99,0,8,0,27,91,63,50,53,104,27,91,63,48,99,0,27,91,67,0,27,91,65,0,27,91,63,50,53,104,27,91,63,56,99,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,93,82,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,54,37,116,59,49,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,71,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,9,0,27,91,63,55,104,0,27,91,63,55,108,0,27,41,48,0,27,91,52,126,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,93,80,37,112,49,37,120,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,0,27,91,77,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,49,109,0,27,91,49,48,109,0,1,0,1,0,2,0,6,0,24,0,1,0,1,0,0,0,5,0,0,0,3,0,6,0,9,0,27,91,51,74,0,27,91,90,0,65,88,0,85,56,0,69,51,0,107,99,98,116,50,0 }; // putty-256color|PuTTY 0.58 with xterm 256-colors, @@ -836,7 +839,7 @@ static const int8_t linux_16colour_terminfo[] = { // enter_am_mode=\E[?7h, // enter_blink_mode=\E[5m, // enter_bold_mode=\E[1m, -// enter_ca_mode=\E[?47h, +// enter_ca_mode=\E[?1049h, // enter_insert_mode=\E[4h, // enter_pc_charset_mode=\E[11m, // enter_reverse_mode=\E[7m, @@ -846,7 +849,7 @@ static const int8_t linux_16colour_terminfo[] = { // exit_alt_charset_mode=^O, // exit_am_mode=\E[?7l, // exit_attribute_mode=\E[m^O, -// exit_ca_mode=\E[2J\E[?47l, +// exit_ca_mode=\E[?1049l, // exit_insert_mode=\E[4l, // exit_pc_charset_mode=\E[10m, // exit_standout_mode=\E[27m, @@ -933,7 +936,7 @@ static const int8_t linux_16colour_terminfo[] = { // user8=\E[?6c, // user9=\E[c, static const int8_t putty_256colour_terminfo[] = { - 30,2,48,0,29,0,16,0,125,1,-70,4,112,117,116,116,121,45,50,53,54,99,111,108,111,114,124,80,117,84,84,89,32,48,46,53,56,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,-1,-1,-1,-1,8,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,22,0,0,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-1,-1,-120,0,-1,-1,-1,-1,-115,0,-110,0,-105,0,-100,0,-91,0,-89,0,-84,0,-1,-1,-73,0,-68,0,-62,0,-56,0,-1,-1,-38,0,-1,-1,-36,0,-1,-1,-1,-1,-1,-1,-2,0,-1,-1,2,1,-1,-1,-1,-1,-1,-1,4,1,-1,-1,9,1,-1,-1,-1,-1,-1,-1,13,1,17,1,23,1,29,1,35,1,41,1,47,1,53,1,59,1,65,1,71,1,77,1,82,1,-1,-1,87,1,-1,-1,91,1,96,1,101,1,105,1,109,1,-1,-1,113,1,117,1,125,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-123,1,-1,-1,-120,1,-111,1,-102,1,-1,-1,-93,1,-84,1,-75,1,-66,1,-57,1,-48,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,1,-1,-1,-19,1,-1,-1,-1,-1,14,2,17,2,28,2,31,2,33,2,36,2,108,2,-1,-1,111,2,113,2,-1,-1,-1,-1,-1,-1,118,2,122,2,126,2,-126,2,-122,2,-1,-1,-1,-1,-118,2,-1,-1,-67,2,-1,-1,-1,-1,-63,2,-57,2,-1,-1,-1,-1,-51,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-44,2,-39,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-35,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-33,2,-27,2,-21,2,-15,2,-9,2,-3,2,3,3,9,3,15,3,21,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,3,43,3,48,3,54,3,58,3,67,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,71,3,-1,-1,-1,-1,-1,-1,75,3,-118,3,-1,-1,-1,-1,-1,-1,-54,3,-48,3,-42,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-36,3,-82,4,-76,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,68,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,52,55,104,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,50,74,27,91,63,52,55,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,55,27,91,114,27,91,109,27,91,63,55,104,27,91,63,49,59,52,59,54,108,27,91,52,108,27,56,27,62,27,93,82,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,121,0,27,91,49,49,126,0,27,91,50,49,126,0,27,91,49,50,126,0,27,91,49,51,126,0,27,91,49,52,126,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,66,0,27,91,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,60,27,91,34,112,27,91,53,48,59,54,34,112,27,99,27,91,63,51,108,27,93,82,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,54,37,124,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,27,79,113,0,27,79,115,0,27,79,114,0,27,79,112,0,27,79,110,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,91,52,126,0,27,79,77,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,49,48,109,0,27,91,49,49,109,0,27,91,49,50,109,0,37,63,37,112,49,37,123,56,125,37,61,37,116,27,37,37,71,-30,-105,-104,27,37,37,64,37,101,37,112,49,37,123,49,48,125,37,61,37,116,27,37,37,71,-30,-105,-103,27,37,37,64,37,101,37,112,49,37,123,49,50,125,37,61,37,116,27,37,37,71,-30,-103,-128,27,37,37,64,37,101,37,112,49,37,123,49,51,125,37,61,37,116,27,37,37,71,-30,-103,-86,27,37,37,64,37,101,37,112,49,37,123,49,52,125,37,61,37,116,27,37,37,71,-30,-103,-85,27,37,37,64,37,101,37,112,49,37,123,49,53,125,37,61,37,116,27,37,37,71,-30,-104,-68,27,37,37,64,37,101,37,112,49,37,123,50,55,125,37,61,37,116,27,37,37,71,-30,-122,-112,27,37,37,64,37,101,37,112,49,37,123,49,53,53,125,37,61,37,116,27,37,37,71,-32,-126,-94,27,37,37,64,37,101,37,112,49,37,99,37,59,0,27,91,49,49,109,0,27,91,49,48,109,0,1,0,1,0,20,0,42,0,-17,0,1,0,1,0,0,0,0,0,5,0,10,0,42,0,46,0,50,0,54,0,58,0,62,0,66,0,70,0,74,0,78,0,82,0,86,0,90,0,94,0,98,0,102,0,106,0,0,0,3,0,6,0,9,0,12,0,15,0,19,0,23,0,27,0,31,0,35,0,39,0,43,0,47,0,51,0,57,0,63,0,69,0,75,0,81,0,87,0,93,0,27,91,51,74,0,27,93,48,59,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,79,113,0,27,79,114,0,27,79,115,0,27,79,116,0,27,79,117,0,27,79,118,0,27,79,119,0,27,79,120,0,27,79,121,0,27,79,108,0,27,79,81,0,27,79,110,0,27,79,82,0,27,79,80,0,27,79,83,0,27,79,112,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,88,84,0,85,56,0,69,51,0,84,83,0,88,77,0,107,112,49,0,107,112,50,0,107,112,51,0,107,112,52,0,107,112,53,0,107,112,54,0,107,112,55,0,107,112,56,0,107,112,57,0,107,112,65,68,68,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,78,85,77,0,107,112,83,85,66,0,107,112,90,82,79,0,120,109,0 + 30,2,48,0,29,0,16,0,125,1,-70,4,112,117,116,116,121,45,50,53,54,99,111,108,111,114,124,80,117,84,84,89,32,48,46,53,56,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,-1,-1,-1,-1,8,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,22,0,0,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-1,-1,-118,0,-1,-1,-1,-1,-113,0,-108,0,-103,0,-98,0,-89,0,-87,0,-82,0,-1,-1,-73,0,-68,0,-62,0,-56,0,-1,-1,-38,0,-1,-1,-36,0,-1,-1,-1,-1,-1,-1,-2,0,-1,-1,2,1,-1,-1,-1,-1,-1,-1,4,1,-1,-1,9,1,-1,-1,-1,-1,-1,-1,13,1,17,1,23,1,29,1,35,1,41,1,47,1,53,1,59,1,65,1,71,1,77,1,82,1,-1,-1,87,1,-1,-1,91,1,96,1,101,1,105,1,109,1,-1,-1,113,1,117,1,125,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-123,1,-1,-1,-120,1,-111,1,-102,1,-1,-1,-93,1,-84,1,-75,1,-66,1,-57,1,-48,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,1,-1,-1,-19,1,-1,-1,-1,-1,14,2,17,2,28,2,31,2,33,2,36,2,108,2,-1,-1,111,2,113,2,-1,-1,-1,-1,-1,-1,118,2,122,2,126,2,-126,2,-122,2,-1,-1,-1,-1,-118,2,-1,-1,-67,2,-1,-1,-1,-1,-63,2,-57,2,-1,-1,-1,-1,-51,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-44,2,-39,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-35,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-33,2,-27,2,-21,2,-15,2,-9,2,-3,2,3,3,9,3,15,3,21,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,3,43,3,48,3,54,3,58,3,67,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,71,3,-1,-1,-1,-1,-1,-1,75,3,-118,3,-1,-1,-1,-1,-1,-1,-54,3,-48,3,-42,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-36,3,-82,4,-76,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,68,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,55,27,91,114,27,91,109,27,91,63,55,104,27,91,63,49,59,52,59,54,108,27,91,52,108,27,56,27,62,27,93,82,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,121,0,27,91,49,49,126,0,27,91,50,49,126,0,27,91,49,50,126,0,27,91,49,51,126,0,27,91,49,52,126,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,66,0,27,91,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,60,27,91,34,112,27,91,53,48,59,54,34,112,27,99,27,91,63,51,108,27,93,82,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,54,37,124,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,27,79,113,0,27,79,115,0,27,79,114,0,27,79,112,0,27,79,110,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,91,52,126,0,27,79,77,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,49,48,109,0,27,91,49,49,109,0,27,91,49,50,109,0,37,63,37,112,49,37,123,56,125,37,61,37,116,27,37,37,71,-30,-105,-104,27,37,37,64,37,101,37,112,49,37,123,49,48,125,37,61,37,116,27,37,37,71,-30,-105,-103,27,37,37,64,37,101,37,112,49,37,123,49,50,125,37,61,37,116,27,37,37,71,-30,-103,-128,27,37,37,64,37,101,37,112,49,37,123,49,51,125,37,61,37,116,27,37,37,71,-30,-103,-86,27,37,37,64,37,101,37,112,49,37,123,49,52,125,37,61,37,116,27,37,37,71,-30,-103,-85,27,37,37,64,37,101,37,112,49,37,123,49,53,125,37,61,37,116,27,37,37,71,-30,-104,-68,27,37,37,64,37,101,37,112,49,37,123,50,55,125,37,61,37,116,27,37,37,71,-30,-122,-112,27,37,37,64,37,101,37,112,49,37,123,49,53,53,125,37,61,37,116,27,37,37,71,-32,-126,-94,27,37,37,64,37,101,37,112,49,37,99,37,59,0,27,91,49,49,109,0,27,91,49,48,109,0,1,0,1,0,30,0,62,0,86,1,1,0,1,0,0,0,0,0,9,0,18,0,23,0,30,0,37,0,42,0,74,0,78,0,82,0,86,0,90,0,94,0,98,0,102,0,106,0,110,0,114,0,118,0,122,0,126,0,-126,0,-122,0,-118,0,-114,0,-110,0,-106,0,-102,0,-96,0,-91,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,38,0,44,0,49,0,53,0,57,0,61,0,65,0,69,0,73,0,77,0,81,0,85,0,91,0,97,0,103,0,109,0,115,0,121,0,127,0,-124,0,-119,0,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,51,74,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,93,48,59,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,66,0,27,91,68,0,27,91,67,0,27,91,65,0,27,79,113,0,27,79,114,0,27,79,115,0,27,79,116,0,27,79,117,0,27,79,118,0,27,79,119,0,27,79,120,0,27,79,121,0,27,79,108,0,27,79,81,0,27,79,110,0,27,79,82,0,27,79,80,0,27,79,83,0,27,79,112,0,27,91,50,57,109,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,88,84,0,85,56,0,66,68,0,66,69,0,69,51,0,80,69,0,80,83,0,84,83,0,88,77,0,107,68,78,53,0,107,76,70,84,53,0,107,82,73,84,53,0,107,85,80,53,0,107,112,49,0,107,112,50,0,107,112,51,0,107,112,52,0,107,112,53,0,107,112,54,0,107,112,55,0,107,112,56,0,107,112,57,0,107,112,65,68,68,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,78,85,77,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0 }; // rxvt-256color|rxvt 2.7.9 with xterm 256-colors, @@ -1404,7 +1407,7 @@ static const int8_t screen_256colour_terminfo[] = { // user8=\E[?1;2c, // user9=\E[c, static const int8_t st_256colour_terminfo[] = { - 30,2,55,0,29,0,15,0,105,1,-28,5,115,116,45,50,53,54,99,111,108,111,114,124,115,116,116,101,114,109,45,50,53,54,99,111,108,111,114,124,115,105,109,112,108,101,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,104,0,108,0,112,0,-1,-1,118,0,122,0,127,0,-124,0,-1,-1,-115,0,-110,0,-105,0,-1,-1,-100,0,-95,0,-90,0,-85,0,-76,0,-72,0,-67,0,-1,-1,-58,0,-53,0,-47,0,-41,0,-1,-1,-23,0,-1,-1,-21,0,-1,-1,-1,-1,-1,-1,-6,0,-1,-1,-2,0,-1,-1,0,1,-1,-1,7,1,12,1,19,1,23,1,30,1,37,1,-1,-1,44,1,48,1,54,1,58,1,62,1,66,1,72,1,78,1,84,1,90,1,96,1,101,1,106,1,113,1,-1,-1,117,1,122,1,127,1,-125,1,-118,1,-1,-1,-111,1,-107,1,-99,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-91,1,-82,1,-73,1,-64,1,-55,1,-46,1,-37,1,-28,1,-19,1,-10,1,-1,-1,-1,-1,-1,-1,-1,1,3,2,8,2,-1,-1,13,2,16,2,-1,-1,-1,-1,31,2,34,2,45,2,48,2,50,2,53,2,-110,2,-1,-1,-107,2,-105,2,-1,-1,-1,-1,-1,-1,-100,2,-95,2,-90,2,-86,2,-81,2,-1,-1,-1,-1,-76,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-11,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-7,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,2,-1,-1,-1,-1,5,3,-1,-1,-1,-1,-1,-1,-1,-1,12,3,19,3,26,3,-1,-1,-1,-1,33,3,-1,-1,40,3,-1,-1,-1,-1,-1,-1,47,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,54,3,60,3,66,3,73,3,80,3,87,3,94,3,102,3,110,3,118,3,126,3,-122,3,-114,3,-106,3,-98,3,-91,3,-84,3,-77,3,-70,3,-62,3,-54,3,-46,3,-38,3,-30,3,-22,3,-14,3,-6,3,1,4,8,4,15,4,22,4,30,4,38,4,46,4,54,4,62,4,70,4,78,4,86,4,93,4,100,4,107,4,114,4,122,4,-126,4,-118,4,-110,4,-102,4,-94,4,-86,4,-78,4,-71,4,-64,4,-57,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-52,4,-41,4,-36,4,-28,4,-24,4,-15,4,-8,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,91,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,97,5,-1,-1,-1,-1,-1,-1,101,5,-92,5,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,91,76,0,127,0,27,91,51,59,53,126,0,27,91,51,126,0,27,91,51,59,50,126,0,27,79,66,0,27,91,50,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,53,70,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,50,59,53,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,27,99,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,93,48,59,0,27,91,49,126,0,27,91,53,126,0,27,79,117,0,27,91,52,126,0,27,91,54,126,0,43,67,44,68,45,65,46,66,48,69,96,96,97,97,102,102,103,103,104,70,105,71,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,1,0,0,0,18,0,37,0,-29,0,1,0,0,0,18,0,24,0,34,0,39,0,46,0,53,0,60,0,67,0,74,0,81,0,88,0,95,0,102,0,109,0,116,0,123,0,-127,0,0,0,3,0,6,0,9,0,12,0,15,0,20,0,25,0,31,0,37,0,43,0,49,0,55,0,61,0,67,0,73,0,78,0,83,0,88,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,49,59,51,66,0,27,91,49,59,53,66,0,27,91,49,59,51,68,0,27,91,49,59,53,68,0,27,91,54,59,51,126,0,27,91,54,59,53,126,0,27,91,53,59,51,126,0,27,91,53,59,53,126,0,27,91,49,59,51,67,0,27,91,49,59,53,67,0,27,91,49,59,51,65,0,27,91,49,59,53,65,0,27,91,50,57,109,0,27,91,57,109,0,88,84,0,77,115,0,83,101,0,83,115,0,84,83,0,107,68,78,51,0,107,68,78,53,0,107,76,70,84,51,0,107,76,70,84,53,0,107,78,88,84,51,0,107,78,88,84,53,0,107,80,82,86,51,0,107,80,82,86,53,0,107,82,73,84,51,0,107,82,73,84,53,0,107,85,80,51,0,107,85,80,53,0,114,109,120,120,0,115,109,120,120,0 + 30,2,55,0,29,0,15,0,105,1,-28,5,115,116,45,50,53,54,99,111,108,111,114,124,115,116,116,101,114,109,45,50,53,54,99,111,108,111,114,124,115,105,109,112,108,101,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,104,0,108,0,112,0,-1,-1,118,0,122,0,127,0,-124,0,-1,-1,-115,0,-110,0,-105,0,-1,-1,-100,0,-95,0,-90,0,-85,0,-76,0,-72,0,-67,0,-1,-1,-58,0,-53,0,-47,0,-41,0,-1,-1,-23,0,-1,-1,-21,0,-1,-1,-1,-1,-1,-1,-6,0,-1,-1,-2,0,-1,-1,0,1,-1,-1,7,1,12,1,19,1,23,1,30,1,37,1,-1,-1,44,1,48,1,54,1,58,1,62,1,66,1,72,1,78,1,84,1,90,1,96,1,101,1,106,1,113,1,-1,-1,117,1,122,1,127,1,-125,1,-118,1,-1,-1,-111,1,-107,1,-99,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-91,1,-82,1,-73,1,-64,1,-55,1,-46,1,-37,1,-28,1,-19,1,-10,1,-1,-1,-1,-1,-1,-1,-1,1,3,2,8,2,-1,-1,13,2,16,2,-1,-1,-1,-1,31,2,34,2,45,2,48,2,50,2,53,2,-110,2,-1,-1,-107,2,-105,2,-1,-1,-1,-1,-1,-1,-100,2,-95,2,-90,2,-86,2,-81,2,-1,-1,-1,-1,-76,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-11,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-7,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,2,-1,-1,-1,-1,5,3,-1,-1,-1,-1,-1,-1,-1,-1,12,3,19,3,26,3,-1,-1,-1,-1,33,3,-1,-1,40,3,-1,-1,-1,-1,-1,-1,47,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,54,3,60,3,66,3,73,3,80,3,87,3,94,3,102,3,110,3,118,3,126,3,-122,3,-114,3,-106,3,-98,3,-91,3,-84,3,-77,3,-70,3,-62,3,-54,3,-46,3,-38,3,-30,3,-22,3,-14,3,-6,3,1,4,8,4,15,4,22,4,30,4,38,4,46,4,54,4,62,4,70,4,78,4,86,4,93,4,100,4,107,4,114,4,122,4,-126,4,-118,4,-110,4,-102,4,-94,4,-86,4,-78,4,-71,4,-64,4,-57,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-52,4,-41,4,-36,4,-28,4,-24,4,-15,4,-8,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,91,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,97,5,-1,-1,-1,-1,-1,-1,101,5,-92,5,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,91,76,0,127,0,27,91,51,59,53,126,0,27,91,51,126,0,27,91,51,59,50,126,0,27,79,66,0,27,91,50,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,53,70,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,50,59,53,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,27,99,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,93,48,59,0,27,91,49,126,0,27,91,53,126,0,27,79,117,0,27,91,52,126,0,27,91,54,126,0,43,67,44,68,45,65,46,66,48,69,96,96,97,97,102,102,103,103,104,70,105,71,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,1,0,0,0,22,0,45,0,15,1,1,0,0,0,9,0,18,0,36,0,43,0,50,0,56,0,66,0,71,0,78,0,85,0,92,0,99,0,106,0,113,0,120,0,127,0,-122,0,-115,0,-108,0,-101,0,-95,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,43,0,49,0,55,0,61,0,67,0,73,0,79,0,85,0,90,0,95,0,100,0,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,49,59,51,66,0,27,91,49,59,53,66,0,27,91,49,59,51,68,0,27,91,49,59,53,68,0,27,91,54,59,51,126,0,27,91,54,59,53,126,0,27,91,53,59,51,126,0,27,91,53,59,53,126,0,27,91,49,59,51,67,0,27,91,49,59,53,67,0,27,91,49,59,51,65,0,27,91,49,59,53,65,0,27,91,50,57,109,0,27,91,57,109,0,88,84,0,66,68,0,66,69,0,77,115,0,80,69,0,80,83,0,83,101,0,83,115,0,84,83,0,107,68,78,51,0,107,68,78,53,0,107,76,70,84,51,0,107,76,70,84,53,0,107,78,88,84,51,0,107,78,88,84,53,0,107,80,82,86,51,0,107,80,82,86,53,0,107,82,73,84,51,0,107,82,73,84,53,0,107,85,80,51,0,107,85,80,53,0,114,109,120,120,0,115,109,120,120,0 }; // tmux-256color|tmux with 256 colors, @@ -1467,7 +1470,7 @@ static const int8_t st_256colour_terminfo[] = { // from_status_line=^G, // init_2string=\E)0, // insert_line=\E[L, -// key_backspace=\177, +// key_backspace=^H, // key_btab=\E[Z, // key_dc=\E[3~, // key_down=\EOB, @@ -1565,6 +1568,7 @@ static const int8_t st_256colour_terminfo[] = { // parm_insert_line=\E[%p1%dL, // parm_left_cursor=\E[%p1%dD, // parm_right_cursor=\E[%p1%dC, +// parm_rindex=\E[%p1%dT, // parm_up_cursor=\E[%p1%dA, // reset_2string=\Ec\E[?1000l\E[?25h, // restore_cursor=\E8, @@ -1583,7 +1587,7 @@ static const int8_t st_256colour_terminfo[] = { // user8=\E[?1;2c, // user9=\E[c, static const int8_t tmux_256colour_terminfo[] = { - 30,2,35,0,43,0,15,0,105,1,13,5,116,109,117,120,45,50,53,54,99,111,108,111,114,124,116,109,117,120,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,100,0,-1,-1,104,0,107,0,113,0,117,0,121,0,-1,-1,127,0,-127,0,-122,0,-117,0,-1,-1,-108,0,-103,0,-98,0,-1,-1,-93,0,-88,0,-83,0,-1,-1,-78,0,-76,0,-71,0,-1,-1,-62,0,-57,0,-51,0,-45,0,-1,-1,-42,0,-1,-1,-40,0,-1,-1,-1,-1,-1,-1,-36,0,-1,-1,-32,0,-1,-1,-1,-1,-1,-1,-30,0,-1,-1,-25,0,-1,-1,-1,-1,-1,-1,-1,-1,-21,0,-17,0,-11,0,-7,0,-3,0,1,1,7,1,13,1,19,1,25,1,31,1,36,1,-1,-1,41,1,-1,-1,45,1,50,1,55,1,59,1,66,1,-1,-1,73,1,77,1,85,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,93,1,-1,-1,96,1,105,1,114,1,123,1,-124,1,-115,1,-106,1,-97,1,-1,-1,-88,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-79,1,-1,-1,-1,-1,-62,1,-59,1,-48,1,-45,1,-43,1,-40,1,49,2,-1,-1,52,2,54,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,59,2,-1,-1,124,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-128,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-121,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-116,2,-1,-1,-1,-1,-109,2,-1,-1,-1,-1,-1,-1,-1,-1,-102,2,-95,2,-88,2,-1,-1,-1,-1,-81,2,-1,-1,-74,2,-1,-1,-1,-1,-1,-1,-67,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-60,2,-54,2,-48,2,-41,2,-34,2,-27,2,-20,2,-12,2,-4,2,4,3,12,3,20,3,28,3,36,3,44,3,51,3,58,3,65,3,72,3,80,3,88,3,96,3,104,3,112,3,120,3,-128,3,-120,3,-113,3,-106,3,-99,3,-92,3,-84,3,-76,3,-68,3,-60,3,-52,3,-44,3,-36,3,-28,3,-21,3,-14,3,-7,3,0,4,8,4,16,4,24,4,32,4,40,4,48,4,56,4,64,4,71,4,78,4,85,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,4,101,4,106,4,114,4,118,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,127,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-124,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-118,4,-1,-1,-1,-1,-1,-1,-114,4,-51,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,51,52,104,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,51,52,108,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,103,0,7,0,27,41,48,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,2,0,1,0,64,0,-125,0,33,3,1,1,1,0,0,0,0,0,7,0,19,0,23,0,28,0,46,0,54,0,60,0,71,0,81,0,86,0,93,0,100,0,107,0,114,0,121,0,-128,0,-121,0,-114,0,-107,0,-100,0,-93,0,-86,0,-79,0,-72,0,-65,0,-58,0,-51,0,-44,0,-37,0,-30,0,-23,0,-16,0,-9,0,-2,0,5,1,12,1,19,1,26,1,33,1,40,1,47,1,54,1,61,1,68,1,75,1,82,1,89,1,96,1,103,1,110,1,117,1,124,1,-125,1,-118,1,-111,1,-104,1,-97,1,-90,1,-83,1,-76,1,-69,1,-62,1,-56,1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,36,0,39,0,42,0,47,0,52,0,57,0,62,0,67,0,71,0,76,0,81,0,86,0,91,0,96,0,102,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-95,0,-90,0,-85,0,-80,0,-75,0,-69,0,-63,0,-57,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,49,1,54,1,59,1,64,1,69,1,74,1,79,1,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,40,66,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,40,37,112,49,37,99,0,27,91,50,32,113,0,27,91,52,58,37,112,49,37,100,109,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,50,57,109,0,27,91,57,109,0,65,88,0,71,48,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,109,117,108,120,0,83,115,0,84,83,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,114,109,120,120,0,115,109,120,120,0 + 30,2,35,0,43,0,15,0,105,1,22,5,116,109,117,120,45,50,53,54,99,111,108,111,114,124,116,109,117,120,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,100,0,-1,-1,104,0,107,0,113,0,117,0,121,0,-1,-1,127,0,-127,0,-122,0,-117,0,-1,-1,-108,0,-103,0,-98,0,-1,-1,-93,0,-88,0,-83,0,-1,-1,-78,0,-76,0,-71,0,-1,-1,-62,0,-57,0,-51,0,-45,0,-1,-1,-42,0,-1,-1,-40,0,-1,-1,-1,-1,-1,-1,-36,0,-1,-1,-32,0,-1,-1,-1,-1,-1,-1,-30,0,-1,-1,-25,0,-1,-1,-1,-1,-1,-1,-1,-1,-21,0,-17,0,-11,0,-7,0,-3,0,1,1,7,1,13,1,19,1,25,1,31,1,36,1,-1,-1,41,1,-1,-1,45,1,50,1,55,1,59,1,66,1,-1,-1,73,1,77,1,85,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,93,1,-1,-1,96,1,105,1,114,1,123,1,-124,1,-115,1,-106,1,-97,1,-88,1,-79,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-70,1,-1,-1,-1,-1,-53,1,-50,1,-39,1,-36,1,-34,1,-31,1,58,2,-1,-1,61,2,63,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,68,2,-1,-1,-123,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-119,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-112,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-107,2,-1,-1,-1,-1,-100,2,-1,-1,-1,-1,-1,-1,-1,-1,-93,2,-86,2,-79,2,-1,-1,-1,-1,-72,2,-1,-1,-65,2,-1,-1,-1,-1,-1,-1,-58,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-51,2,-45,2,-39,2,-32,2,-25,2,-18,2,-11,2,-3,2,5,3,13,3,21,3,29,3,37,3,45,3,53,3,60,3,67,3,74,3,81,3,89,3,97,3,105,3,113,3,121,3,-127,3,-119,3,-111,3,-104,3,-97,3,-90,3,-83,3,-75,3,-67,3,-59,3,-51,3,-43,3,-35,3,-27,3,-19,3,-12,3,-5,3,2,4,9,4,17,4,25,4,33,4,41,4,49,4,57,4,65,4,73,4,80,4,87,4,94,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,99,4,110,4,115,4,123,4,127,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-120,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-115,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-109,4,-1,-1,-1,-1,-1,-1,-105,4,-42,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,51,52,104,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,51,52,108,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,103,0,7,0,27,41,48,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,99,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,3,0,1,0,76,0,-100,0,-70,3,1,1,1,0,1,0,0,0,0,0,9,0,18,0,25,0,37,0,41,0,46,0,64,0,71,0,78,0,83,0,91,0,97,0,108,0,118,0,123,0,-127,0,-118,0,-109,0,-102,0,-95,0,-88,0,-81,0,-74,0,-67,0,-60,0,-53,0,-46,0,-39,0,-32,0,-25,0,-18,0,-11,0,-4,0,3,1,10,1,17,1,24,1,31,1,38,1,45,1,52,1,59,1,66,1,73,1,80,1,87,1,94,1,101,1,108,1,115,1,122,1,-127,1,-120,1,-113,1,-106,1,-99,1,-92,1,-85,1,-78,1,-71,1,-64,1,-57,1,-50,1,-43,1,-36,1,-29,1,-22,1,-15,1,-8,1,-1,1,3,2,7,2,13,2,38,2,43,2,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,54,0,57,0,60,0,63,0,66,0,69,0,74,0,79,0,84,0,89,0,94,0,98,0,103,0,108,0,113,0,118,0,123,0,-127,0,-121,0,-115,0,-109,0,-103,0,-97,0,-91,0,-85,0,-79,0,-73,0,-68,0,-63,0,-58,0,-53,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,36,1,42,1,48,1,54,1,60,1,66,1,72,1,76,1,81,1,86,1,91,1,96,1,101,1,106,1,112,1,117,1,120,1,125,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,40,66,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,40,37,112,49,37,99,0,27,91,50,32,113,0,27,91,52,58,37,112,49,37,100,109,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,73,0,27,91,79,0,27,91,50,57,109,0,27,92,91,91,48,45,57,93,43,59,91,48,45,57,93,43,59,91,48,45,57,93,43,99,0,27,91,57,109,0,27,80,62,92,124,91,32,45,126,93,43,27,92,92,0,65,88,0,71,48,0,88,70,0,85,56,0,66,68,0,66,69,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,48,0,83,101,0,83,109,117,108,120,0,83,115,0,84,83,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,114,0 }; // vte-256color|VTE with xterm 256-colors, @@ -1620,11 +1624,12 @@ static const int8_t tmux_256colour_terminfo[] = { // cursor_up=\E[A, // delete_character=\E[P, // delete_line=\E[M, -// ena_acs=\E)0, +// ena_acs=\E(B\E)0, // enter_alt_charset_mode=^N, // enter_am_mode=\E[?7h, +// enter_blink_mode=\E[5m, // enter_bold_mode=\E[1m, -// enter_ca_mode=\E7\E[?47h, +// enter_ca_mode=\E[?1049h\E[22;0;0t, // enter_dim_mode=\E[2m, // enter_insert_mode=\E[4h, // enter_italics_mode=\E[3m, @@ -1636,7 +1641,7 @@ static const int8_t tmux_256colour_terminfo[] = { // exit_alt_charset_mode=^O, // exit_am_mode=\E[?7l, // exit_attribute_mode=\E[0m^O, -// exit_ca_mode=\E[2J\E[?47l\E8, +// exit_ca_mode=\E[?1049l\E[23;0;0t, // exit_insert_mode=\E[4l, // exit_italics_mode=\E[23m, // exit_standout_mode=\E[27m, @@ -1739,6 +1744,7 @@ static const int8_t tmux_256colour_terminfo[] = { // keypad_xmit=\E[?1h\E=, // memory_lock=\El, // memory_unlock=\Em, +// newline=\EE, // orig_colors=\E]104^G, // orig_pair=\E[39;49m, // parm_dch=\E[%p1%dP, @@ -1751,6 +1757,7 @@ static const int8_t tmux_256colour_terminfo[] = { // parm_right_cursor=\E[%p1%dC, // parm_rindex=\E[%p1%dT, // parm_up_cursor=\E[%p1%dA, +// repeat_char=%p1%c\E[%p2%{1}%-%db, // reset_1string=\Ec, // reset_2string=\E7\E[r\E8\E[m\E[?7h\E[\041p\E[?1;3;4;6l\E[4l\E>\E[?1000l\E[?25h, // restore_cursor=\E8, @@ -1760,7 +1767,7 @@ static const int8_t tmux_256colour_terminfo[] = { // scroll_reverse=\EM, // set_a_background=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, // set_a_foreground=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, -// set_attributes=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p5%t;2%;%?%p7%t;8%;%?%p1%p3%|%t;7%;m%?%p9%t^N%e^O%;, +// set_attributes=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p4%t;5%;%?%p5%t;2%;%?%p7%t;8%;%?%p1%p3%|%t;7%;m%?%p9%t^N%e^O%;, // set_tab=\EH, // tab=^I, // user6=\E[%i%d;%dR, @@ -1768,7 +1775,7 @@ static const int8_t tmux_256colour_terminfo[] = { // user8=\E[?%[;0123456789]c, // user9=\E[c, static const int8_t vte_256colour_terminfo[] = { - 30,2,39,0,38,0,15,0,-99,1,-49,5,118,116,101,45,50,53,54,99,111,108,111,114,124,86,84,69,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,104,0,108,0,-1,-1,-1,-1,112,0,-1,-1,114,0,119,0,-1,-1,-128,0,-123,0,-118,0,-1,-1,-113,0,-108,0,-103,0,-98,0,-89,0,-87,0,-81,0,-1,-1,-68,0,-63,0,-57,0,-51,0,-1,-1,-1,-1,-1,-1,-33,0,-1,-1,-1,-1,-1,-1,0,1,-1,-1,4,1,-1,-1,-1,-1,-1,-1,6,1,-1,-1,11,1,-1,-1,-1,-1,-1,-1,-1,-1,15,1,19,1,25,1,29,1,33,1,37,1,43,1,49,1,55,1,61,1,67,1,71,1,-1,-1,76,1,-1,-1,80,1,85,1,90,1,94,1,101,1,-1,-1,108,1,112,1,120,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-128,1,-119,1,-110,1,-101,1,-92,1,-83,1,-74,1,-65,1,-56,1,-47,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-38,1,-35,1,-1,-1,-1,-1,16,2,19,2,30,2,33,2,35,2,38,2,116,2,-1,-1,119,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,121,2,-1,-1,-1,-1,-1,-1,-1,-1,125,2,-1,-1,-78,2,-1,-1,-1,-1,-74,2,-68,2,-1,-1,-1,-1,-62,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,2,-54,2,-1,-1,-50,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-45,2,-1,-1,-38,2,-33,2,-1,-1,-1,-1,-1,-1,-1,-1,-26,2,-19,2,-12,2,-1,-1,-1,-1,-5,2,-1,-1,2,3,-1,-1,-1,-1,-1,-1,9,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,16,3,22,3,28,3,35,3,42,3,49,3,56,3,64,3,72,3,80,3,88,3,96,3,104,3,112,3,120,3,127,3,-122,3,-115,3,-108,3,-100,3,-92,3,-84,3,-76,3,-68,3,-60,3,-52,3,-44,3,-37,3,-30,3,-23,3,-16,3,-8,3,0,4,8,4,16,4,24,4,32,4,40,4,48,4,55,4,62,4,69,4,76,4,84,4,92,4,100,4,108,4,116,4,124,4,-124,4,-116,4,-109,4,-102,4,-95,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-90,4,-79,4,-74,4,-55,4,-51,4,-42,4,-35,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,59,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,64,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,70,5,-1,-1,-1,-1,-1,-1,74,5,-119,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-55,5,-52,5,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,14,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,48,109,15,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,109,27,91,63,55,104,27,91,52,108,27,62,27,55,27,91,114,27,91,63,49,59,51,59,52,59,54,108,27,56,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,99,0,27,55,27,91,114,27,56,27,91,109,27,91,63,55,104,27,91,33,112,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,69,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,41,48,0,27,79,70,0,27,79,77,0,27,91,49,126,0,27,91,51,59,50,126,0,27,91,52,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,108,0,27,109,0,0,1,0,0,0,59,0,119,0,22,3,1,0,0,0,6,0,12,0,23,0,55,0,62,0,69,0,76,0,83,0,90,0,97,0,104,0,111,0,118,0,125,0,-124,0,-117,0,-110,0,-103,0,-96,0,-89,0,-82,0,-75,0,-68,0,-61,0,-54,0,-47,0,-40,0,-33,0,-26,0,-19,0,-12,0,-5,0,2,1,9,1,16,1,23,1,30,1,37,1,44,1,51,1,58,1,65,1,72,1,79,1,86,1,93,1,100,1,107,1,114,1,121,1,-128,1,-121,1,-114,1,-107,1,-100,1,-93,1,-87,1,-82,1,0,0,3,0,8,0,13,0,19,0,22,0,27,0,32,0,37,0,42,0,47,0,51,0,56,0,61,0,66,0,71,0,76,0,82,0,88,0,94,0,100,0,106,0,112,0,118,0,124,0,-126,0,-120,0,-115,0,-110,0,-105,0,-100,0,-95,0,-89,0,-83,0,-77,0,-71,0,-65,0,-59,0,-53,0,-47,0,-41,0,-35,0,-29,0,-23,0,-17,0,-11,0,-5,0,1,1,7,1,13,1,19,1,25,1,29,1,34,1,39,1,44,1,49,1,54,1,59,1,64,1,27,91,53,53,109,0,27,91,53,51,109,0,27,91,52,58,37,112,49,37,100,109,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,50,57,109,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,88,84,0,82,109,111,108,0,83,109,111,108,0,83,109,117,108,120,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,114,109,120,120,0,115,109,120,120,0,120,109,0 + 30,2,39,0,38,0,15,0,-99,1,7,6,118,116,101,45,50,53,54,99,111,108,111,114,124,86,84,69,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,104,0,108,0,-1,-1,-1,-1,112,0,114,0,119,0,124,0,-1,-1,-114,0,-109,0,-104,0,-1,-1,-99,0,-94,0,-89,0,-84,0,-75,0,-73,0,-67,0,-1,-1,-49,0,-44,0,-38,0,-32,0,-1,-1,-1,-1,-1,-1,-14,0,-1,-1,-1,-1,-1,-1,19,1,-1,-1,23,1,-1,-1,-1,-1,-1,-1,25,1,-1,-1,30,1,-1,-1,-1,-1,-1,-1,-1,-1,34,1,38,1,44,1,48,1,52,1,56,1,62,1,68,1,74,1,80,1,86,1,90,1,-1,-1,95,1,-1,-1,99,1,104,1,109,1,113,1,120,1,-1,-1,127,1,-125,1,-117,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-109,1,-1,-1,-106,1,-97,1,-88,1,-79,1,-70,1,-61,1,-52,1,-43,1,-34,1,-25,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-16,1,4,2,7,2,-1,-1,-1,-1,58,2,61,2,72,2,75,2,77,2,80,2,-87,2,-1,-1,-84,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-82,2,-1,-1,-1,-1,-1,-1,-1,-1,-78,2,-1,-1,-25,2,-1,-1,-1,-1,-21,2,-15,2,-1,-1,-1,-1,-9,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,2,2,3,-1,-1,6,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,11,3,-1,-1,18,3,23,3,-1,-1,-1,-1,-1,-1,-1,-1,30,3,37,3,44,3,-1,-1,-1,-1,51,3,-1,-1,58,3,-1,-1,-1,-1,-1,-1,65,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,72,3,78,3,84,3,91,3,98,3,105,3,112,3,120,3,-128,3,-120,3,-112,3,-104,3,-96,3,-88,3,-80,3,-73,3,-66,3,-59,3,-52,3,-44,3,-36,3,-28,3,-20,3,-12,3,-4,3,4,4,12,4,19,4,26,4,33,4,40,4,48,4,56,4,64,4,72,4,80,4,88,4,96,4,104,4,111,4,118,4,125,4,-124,4,-116,4,-108,4,-100,4,-92,4,-84,4,-76,4,-68,4,-60,4,-53,4,-46,4,-39,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-34,4,-23,4,-18,4,1,5,5,5,14,5,21,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,115,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,120,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,126,5,-1,-1,-1,-1,-1,-1,-126,5,-63,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,6,4,6,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,48,109,15,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,109,27,91,63,55,104,27,91,52,108,27,62,27,55,27,91,114,27,91,63,49,59,51,59,52,59,54,108,27,56,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,99,0,27,55,27,91,114,27,56,27,91,109,27,91,63,55,104,27,91,33,112,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,69,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,79,70,0,27,79,77,0,27,91,49,126,0,27,91,51,59,50,126,0,27,91,52,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,108,0,27,109,0,0,2,0,0,0,69,0,-116,0,-52,3,1,1,0,0,9,0,18,0,25,0,37,0,55,0,62,0,69,0,75,0,81,0,87,0,98,0,108,0,-116,0,-109,0,-102,0,-95,0,-88,0,-81,0,-74,0,-67,0,-60,0,-53,0,-46,0,-39,0,-32,0,-25,0,-18,0,-11,0,-4,0,3,1,10,1,17,1,24,1,31,1,38,1,45,1,52,1,59,1,66,1,73,1,80,1,87,1,94,1,101,1,108,1,115,1,122,1,-127,1,-120,1,-113,1,-106,1,-99,1,-92,1,-85,1,-78,1,-71,1,-64,1,-57,1,-50,1,-43,1,-36,1,-29,1,-22,1,-15,1,-8,1,-2,1,59,2,64,2,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,35,0,40,0,46,0,49,0,52,0,57,0,62,0,67,0,72,0,77,0,81,0,86,0,91,0,96,0,101,0,106,0,112,0,118,0,124,0,-126,0,-120,0,-114,0,-108,0,-102,0,-96,0,-90,0,-85,0,-80,0,-75,0,-70,0,-65,0,-59,0,-53,0,-47,0,-41,0,-35,0,-29,0,-23,0,-17,0,-11,0,-5,0,1,1,7,1,13,1,19,1,25,1,31,1,37,1,43,1,49,1,55,1,59,1,64,1,69,1,74,1,79,1,84,1,89,1,95,1,100,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,53,53,109,0,27,91,49,32,113,0,27,91,53,51,109,0,27,91,52,58,37,112,49,37,100,109,0,27,91,37,112,49,37,100,32,113,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,50,57,109,0,27,91,53,56,58,50,58,58,37,112,49,37,123,54,53,53,51,54,125,37,47,37,100,58,37,112,49,37,123,50,53,54,125,37,47,37,123,50,53,53,125,37,38,37,100,58,37,112,49,37,123,50,53,53,125,37,38,37,100,109,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,65,88,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,77,115,0,80,69,0,80,83,0,82,109,111,108,0,83,101,0,83,109,111,108,0,83,109,117,108,120,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,114,109,120,120,0,115,101,116,97,108,0,115,109,120,120,0,120,109,0 }; // vtpcon|ANIS emulation for console virtual terminal sequence with libuv, @@ -1776,6 +1783,7 @@ static const int8_t vte_256colour_terminfo[] = { // back_color_erase, // backspaces_with_bs, // has_meta_key, +// has_status_line, // move_insert_mode, // move_standout_mode, // no_pad_char, @@ -1807,6 +1815,7 @@ static const int8_t vte_256colour_terminfo[] = { // cursor_visible@, // delete_character=\E[P, // delete_line=\E[M, +// dis_status_line=\E]0;^G, // enter_alt_charset_mode=\E(0, // enter_am_mode@, // enter_blink_mode@, @@ -1829,6 +1838,7 @@ static const int8_t vte_256colour_terminfo[] = { // exit_standout_mode=\E[27m, // exit_underline_mode=\E[24m, // flash_screen@, +// from_status_line=^G, // init_2string=\E[\041p\E[?3l, // initialize_color=\E]4;%p1%d;rgb\072%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E, // insert_line=\E[L, @@ -1836,6 +1846,7 @@ static const int8_t vte_256colour_terminfo[] = { // key_a3=\EOy, // key_b2=\E[G, // key_backspace=^H, +// key_beg=\EOE, // key_btab=\E[Z, // key_c1=\EOq, // key_c3=\EOs, @@ -1957,15 +1968,18 @@ static const int8_t vte_256colour_terminfo[] = { // set_a_background=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, // set_a_foreground=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, // set_attributes=\E[0%?%p1%p3%|%t;7%;%?%p2%t;4%;%?%p6%t;1%;m, +// set_left_margin_parm@, // set_lr_margin@, +// set_right_margin_parm@, // set_tab=\EH, // tab=^I, +// to_status_line=\E]0;, // user6@, // user7@, // user8@, // user9@, static const int8_t vtpcon_terminfo[] = { - 30,2,71,0,38,0,15,0,-99,1,47,4,118,116,112,99,111,110,124,65,78,73,83,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,73,0,77,0,84,0,-1,-1,86,0,99,0,-1,-1,103,0,-2,-1,107,0,111,0,-1,-1,-1,-1,115,0,-2,-1,119,0,124,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-123,0,-118,0,-113,0,-108,0,-99,0,-95,0,-90,0,-1,-1,-2,-1,-81,0,-75,0,-2,-1,-1,-1,-1,-1,-1,-1,-69,0,-1,-1,-1,-1,-1,-1,-59,0,-1,-1,-55,0,-1,-1,-1,-1,-1,-1,-53,0,-1,-1,-48,0,-1,-1,-1,-1,-1,-1,-1,-1,-44,0,-39,0,-33,0,-28,0,-23,0,-18,0,-13,0,-7,0,-1,0,5,1,11,1,16,1,-1,-1,21,1,-1,-1,25,1,30,1,35,1,39,1,46,1,-1,-1,53,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,57,1,-1,-1,60,1,69,1,78,1,87,1,96,1,105,1,114,1,123,1,-124,1,-115,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-106,1,-2,-1,-2,-1,-1,-1,-1,-1,-86,1,-83,1,-72,1,-69,1,-67,1,-64,1,-21,1,-1,-1,-18,1,-1,-1,-1,-1,-1,-1,-1,-1,-16,1,-12,1,-8,1,-4,1,0,2,-1,-1,-1,-1,4,2,-1,-1,27,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,31,2,36,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,40,2,-1,-1,-1,-1,47,2,-1,-1,-1,-1,-1,-1,-1,-1,54,2,61,2,68,2,-1,-1,-1,-1,75,2,-1,-1,82,2,-1,-1,-1,-1,-1,-1,89,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,2,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-94,2,-88,2,-82,2,-76,2,-70,2,-64,2,-58,2,-52,2,-46,2,-40,2,-34,2,-28,2,-22,2,-16,2,-10,2,-4,2,2,3,8,3,14,3,21,3,27,3,33,3,39,3,45,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,51,3,56,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,63,3,-2,-1,72,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-91,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-86,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-80,3,-17,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,2,0,0,0,74,0,92,0,-46,1,1,1,-2,-1,-2,-1,0,0,-2,-1,5,0,11,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,28,0,32,0,36,0,40,0,44,0,48,0,52,0,56,0,60,0,64,0,68,0,72,0,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0 + 30,2,71,0,38,0,15,0,-99,1,64,4,118,116,112,99,111,110,124,65,78,73,83,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,73,0,77,0,84,0,-1,-1,86,0,99,0,-1,-1,103,0,-2,-1,107,0,111,0,115,0,-1,-1,121,0,-2,-1,125,0,-126,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-117,0,-112,0,-107,0,-102,0,-93,0,-89,0,-84,0,-1,-1,-2,-1,-75,0,-69,0,-2,-1,-1,-1,-63,0,-1,-1,-61,0,-1,-1,-1,-1,-1,-1,-51,0,-1,-1,-47,0,-1,-1,-1,-1,-1,-1,-45,0,-1,-1,-40,0,-1,-1,-1,-1,-1,-1,-1,-1,-36,0,-31,0,-25,0,-20,0,-15,0,-10,0,-5,0,1,1,7,1,13,1,19,1,24,1,-1,-1,29,1,-1,-1,33,1,38,1,43,1,47,1,54,1,-1,-1,61,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,65,1,-1,-1,68,1,77,1,86,1,95,1,104,1,113,1,122,1,-125,1,-116,1,-107,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-98,1,-2,-1,-2,-1,-1,-1,-1,-1,-78,1,-75,1,-64,1,-61,1,-59,1,-56,1,-13,1,-1,-1,-10,1,-8,1,-1,-1,-1,-1,-1,-1,-3,1,1,2,5,2,9,2,13,2,-1,-1,-1,-1,17,2,-1,-1,40,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,44,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,48,2,53,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,57,2,-1,-1,-1,-1,64,2,-1,-1,-1,-1,-1,-1,-1,-1,71,2,78,2,85,2,-1,-1,-1,-1,92,2,-1,-1,99,2,-1,-1,-1,-1,-1,-1,106,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,113,2,119,2,125,2,-125,2,-119,2,-113,2,-107,2,-101,2,-95,2,-89,2,-83,2,-77,2,-71,2,-65,2,-59,2,-53,2,-47,2,-41,2,-35,2,-29,2,-23,2,-17,2,-11,2,-5,2,1,3,7,3,13,3,19,3,25,3,31,3,38,3,44,3,50,3,56,3,62,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,68,3,73,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,80,3,-2,-1,89,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-74,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-69,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-63,3,0,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,7,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,93,48,59,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,79,69,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,3,0,0,0,87,0,119,0,-127,2,1,1,1,0,0,0,9,0,-2,-1,-2,-1,18,0,-2,-1,23,0,30,0,37,0,42,0,48,0,58,0,-2,-1,63,0,69,0,78,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,87,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,94,0,98,0,102,0,106,0,110,0,114,0,118,0,122,0,126,0,-126,0,-122,0,-118,0,-114,0,-110,0,-2,-1,-106,0,-2,-1,-2,-1,-81,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,51,0,54,0,57,0,62,0,67,0,72,0,77,0,82,0,86,0,91,0,96,0,101,0,106,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-109,0,-103,0,-97,0,-91,0,-85,0,-80,0,-75,0,-70,0,-65,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,36,1,42,1,48,1,54,1,60,1,64,1,69,1,74,1,79,1,84,1,89,1,93,1,97,1,101,1,105,1,109,1,115,1,121,1,127,1,-123,1,-117,1,-111,1,-105,1,-100,1,-94,1,-89,1,-86,1,-81,1,-78,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,51,74,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,73,0,27,91,79,0,27,92,91,52,49,59,91,49,45,54,93,91,48,45,57,93,91,48,45,57,93,59,48,99,0,27,80,62,92,124,88,84,101,114,109,92,40,91,49,45,57,93,91,48,45,57,93,43,92,41,27,92,92,0,65,88,0,88,70,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,101,0,83,115,0,84,83,0,88,77,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,109,0,120,114,0 }; // win32con|ANSI emulation for libuv on legacy console, @@ -2172,6 +2186,7 @@ static const int8_t win32con_terminfo[] = { // key_a3=\EOy, // key_b2=\EOu, // key_backspace=^H, +// key_beg=\EOE, // key_btab=\E[Z, // key_c1=\EOq, // key_c3=\EOs, @@ -2293,7 +2308,9 @@ static const int8_t win32con_terminfo[] = { // set_a_background=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, // set_a_foreground=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, // set_attributes=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, +// set_left_margin_parm=\E[?69h\E[%i%p1%ds, // set_lr_margin=\E[?69h\E[%i%p1%d;%p2%ds, +// set_right_margin_parm=\E[?69h\E[%i;%p1%ds, // set_tab=\EH, // tab=^I, // user6=\E[%i%d;%dR, @@ -2301,5 +2318,5 @@ static const int8_t win32con_terminfo[] = { // user8=\E[?%[;0123456789]c, // user9=\E[c, static const int8_t xterm_256colour_terminfo[] = { - 30,2,37,0,38,0,15,0,-99,1,51,6,120,116,101,114,109,45,50,53,54,99,111,108,111,114,124,120,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,102,0,-1,-1,106,0,110,0,120,0,124,0,-1,-1,-1,-1,-128,0,-124,0,-119,0,-114,0,-1,-1,-96,0,-91,0,-86,0,-1,-1,-81,0,-76,0,-71,0,-66,0,-57,0,-53,0,-46,0,-1,-1,-28,0,-23,0,-17,0,-11,0,-1,-1,-1,-1,-1,-1,7,1,-1,-1,-1,-1,-1,-1,25,1,-1,-1,29,1,-1,-1,-1,-1,-1,-1,31,1,-1,-1,36,1,-1,-1,-1,-1,-1,-1,-1,-1,40,1,44,1,50,1,54,1,58,1,62,1,68,1,74,1,80,1,86,1,92,1,96,1,-1,-1,101,1,-1,-1,105,1,110,1,115,1,119,1,126,1,-1,-1,-123,1,-119,1,-111,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-103,1,-94,1,-85,1,-1,-1,-82,1,-73,1,-64,1,-55,1,-46,1,-37,1,-28,1,-19,1,-10,1,-1,1,-1,-1,-1,-1,-1,-1,8,2,12,2,17,2,22,2,42,2,51,2,-1,-1,-1,-1,69,2,72,2,83,2,86,2,88,2,91,2,-72,2,-1,-1,-69,2,-1,-1,-1,-1,-1,-1,-1,-1,-67,2,-63,2,-59,2,-55,2,-51,2,-1,-1,-1,-1,-47,2,-1,-1,6,3,-1,-1,-1,-1,10,3,16,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,3,26,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,30,3,-1,-1,-1,-1,37,3,-1,-1,-1,-1,-1,-1,-1,-1,44,3,51,3,58,3,-1,-1,-1,-1,65,3,-1,-1,72,3,-1,-1,-1,-1,-1,-1,79,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,3,92,3,98,3,105,3,112,3,119,3,126,3,-122,3,-114,3,-106,3,-98,3,-90,3,-82,3,-74,3,-66,3,-59,3,-52,3,-45,3,-38,3,-30,3,-22,3,-14,3,-6,3,2,4,10,4,18,4,26,4,33,4,40,4,47,4,54,4,62,4,70,4,78,4,86,4,94,4,102,4,110,4,118,4,125,4,-124,4,-117,4,-110,4,-102,4,-94,4,-86,4,-78,4,-70,4,-62,4,-54,4,-46,4,-39,4,-32,4,-25,4,-20,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-13,4,-2,4,3,5,22,5,26,5,35,5,42,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-120,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-115,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-109,5,-1,-1,-1,-1,-1,-1,-105,5,-42,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,45,6,48,6,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,63,49,50,59,50,53,104,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,40,66,27,91,109,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,63,49,48,51,52,108,0,27,91,63,49,48,51,52,104,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,99,27,93,49,48,52,7,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,79,117,0,27,79,113,0,27,79,115,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,79,70,0,27,79,77,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,63,54,57,104,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,115,0,27,108,0,27,109,0,0,2,0,0,0,74,0,-106,0,-84,3,1,1,0,0,7,0,19,0,24,0,42,0,48,0,58,0,90,0,97,0,104,0,111,0,118,0,125,0,-124,0,-117,0,-110,0,-103,0,-96,0,-89,0,-82,0,-75,0,-68,0,-61,0,-54,0,-47,0,-40,0,-33,0,-26,0,-19,0,-12,0,-5,0,2,1,9,1,16,1,23,1,30,1,37,1,44,1,51,1,58,1,65,1,72,1,79,1,86,1,93,1,100,1,107,1,114,1,121,1,-128,1,-121,1,-114,1,-107,1,-100,1,-93,1,-86,1,-79,1,-72,1,-65,1,-58,1,-54,1,-50,1,-46,1,-42,1,-38,1,-34,1,-30,1,-26,1,-22,1,-18,1,-14,1,-10,1,-4,1,1,2,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,50,57,109,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0 + 30,2,37,0,38,0,15,0,-99,1,90,6,120,116,101,114,109,45,50,53,54,99,111,108,111,114,124,120,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,102,0,-1,-1,106,0,110,0,120,0,124,0,-1,-1,-1,-1,-128,0,-124,0,-119,0,-114,0,-1,-1,-96,0,-91,0,-86,0,-1,-1,-81,0,-76,0,-71,0,-66,0,-57,0,-53,0,-46,0,-1,-1,-28,0,-23,0,-17,0,-11,0,-1,-1,-1,-1,-1,-1,7,1,-1,-1,-1,-1,-1,-1,25,1,-1,-1,29,1,-1,-1,-1,-1,-1,-1,31,1,-1,-1,36,1,-1,-1,-1,-1,-1,-1,-1,-1,40,1,44,1,50,1,54,1,58,1,62,1,68,1,74,1,80,1,86,1,92,1,96,1,-1,-1,101,1,-1,-1,105,1,110,1,115,1,119,1,126,1,-1,-1,-123,1,-119,1,-111,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-103,1,-94,1,-85,1,-1,-1,-82,1,-73,1,-64,1,-55,1,-46,1,-37,1,-28,1,-19,1,-10,1,-1,1,-1,-1,-1,-1,-1,-1,8,2,12,2,17,2,22,2,42,2,51,2,-1,-1,-1,-1,69,2,72,2,83,2,86,2,88,2,91,2,-72,2,-1,-1,-69,2,-1,-1,-1,-1,-1,-1,-1,-1,-67,2,-63,2,-59,2,-55,2,-51,2,-1,-1,-1,-1,-47,2,-1,-1,6,3,-1,-1,-1,-1,10,3,16,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,3,30,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,34,3,-1,-1,-1,-1,41,3,-1,-1,-1,-1,-1,-1,-1,-1,48,3,55,3,62,3,-1,-1,-1,-1,69,3,-1,-1,76,3,-1,-1,-1,-1,-1,-1,83,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,3,96,3,102,3,109,3,116,3,123,3,-126,3,-118,3,-110,3,-102,3,-94,3,-86,3,-78,3,-70,3,-62,3,-55,3,-48,3,-41,3,-34,3,-26,3,-18,3,-10,3,-2,3,6,4,14,4,22,4,30,4,37,4,44,4,51,4,58,4,66,4,74,4,82,4,90,4,98,4,106,4,114,4,122,4,-127,4,-120,4,-113,4,-106,4,-98,4,-90,4,-82,4,-74,4,-66,4,-58,4,-50,4,-42,4,-35,4,-28,4,-21,4,-16,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-9,4,2,5,7,5,26,5,30,5,39,5,46,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-116,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-111,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-105,5,-88,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-70,5,-1,-1,-1,-1,-1,-1,-66,5,-3,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,61,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,84,6,87,6,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,63,49,50,59,50,53,104,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,40,66,27,91,109,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,63,49,48,51,52,108,0,27,91,63,49,48,51,52,104,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,99,27,93,49,48,52,7,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,79,117,0,27,79,113,0,27,79,115,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,79,69,0,27,79,70,0,27,79,77,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,63,54,57,104,27,91,37,105,37,112,49,37,100,115,0,27,91,63,54,57,104,27,91,37,105,59,37,112,49,37,100,115,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,63,54,57,104,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,115,0,27,108,0,27,109,0,3,0,0,0,86,0,-81,0,83,4,1,1,1,0,0,0,9,0,18,0,25,0,37,0,42,0,60,0,67,0,74,0,79,0,85,0,95,0,127,0,-123,0,-114,0,-105,0,-98,0,-91,0,-84,0,-77,0,-70,0,-63,0,-56,0,-49,0,-42,0,-35,0,-28,0,-21,0,-14,0,-7,0,0,1,7,1,14,1,21,1,28,1,35,1,42,1,49,1,56,1,63,1,70,1,77,1,84,1,91,1,98,1,105,1,112,1,119,1,126,1,-123,1,-116,1,-109,1,-102,1,-95,1,-88,1,-81,1,-74,1,-67,1,-60,1,-53,1,-46,1,-39,1,-32,1,-25,1,-18,1,-11,1,-4,1,3,2,7,2,11,2,15,2,19,2,23,2,27,2,31,2,35,2,39,2,43,2,47,2,51,2,55,2,59,2,65,2,90,2,95,2,-124,2,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,51,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-83,0,-78,0,-73,0,-68,0,-63,0,-57,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,61,1,66,1,71,1,76,1,81,1,86,1,90,1,94,1,98,1,102,1,106,1,112,1,118,1,124,1,-126,1,-120,1,-114,1,-108,1,-103,1,-97,1,-92,1,-89,1,-84,1,-81,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,73,0,27,91,79,0,27,91,50,57,109,0,27,92,91,52,49,59,91,49,45,54,93,91,48,45,57,93,91,48,45,57,93,59,48,99,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,27,80,62,92,124,88,84,101,114,109,92,40,91,49,45,57,93,91,48,45,57,93,43,92,41,27,92,92,0,65,88,0,88,70,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,101,0,83,115,0,88,77,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,109,0,120,114,0 }; diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 197bbcabb5..7fae34d33f 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -3,6 +3,7 @@ #include <assert.h> #include <signal.h> #include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -15,37 +16,37 @@ #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/cursor_shape.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/signal.h" #include "nvim/event/stream.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight_defs.h" #include "nvim/log.h" #include "nvim/macros_defs.h" #include "nvim/main.h" +#include "nvim/map_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" +#include "nvim/strings.h" #include "nvim/tui/input.h" #include "nvim/tui/terminfo.h" #include "nvim/tui/tui.h" #include "nvim/types_defs.h" #include "nvim/ugrid.h" -#include "nvim/ui.h" #include "nvim/ui_client.h" +#include "nvim/ui_defs.h" #ifdef MSWIN # include "nvim/os/os_win_console.h" -# include "nvim/os/tty.h" #endif -// Space reserved in two output buffers to make the cursor normal or invisible -// when flushing. No existing terminal will require 32 bytes to do that. -#define CNORM_COMMAND_MAX_SIZE 32 #define OUTBUF_SIZE 0xffff #define TOO_MANY_EVENTS 1000000 @@ -78,7 +79,7 @@ struct TUIData { TermInput input; uv_loop_t write_loop; unibi_term *ut; - char *term; // value of $TERM + char *term; ///< value of $TERM union { uv_tty_t tty; uv_pipe_t pipe; @@ -136,11 +137,14 @@ struct TUIData { int sync; } unibi_ext; char *space_buf; + size_t space_buf_len; bool stopped; int seen_error_exit; int width; int height; bool rgb; + int url; ///< Index of URL currently being printed, if any + StringBuilder urlbuf; ///< Re-usable buffer for writing OSC 8 control sequences }; static int got_winch = 0; @@ -149,7 +153,10 @@ static bool cursor_style_enabled = false; # include "tui/tui.c.generated.h" #endif -void tui_start(TUIData **tui_p, int *width, int *height, char **term) +static Set(cstr_t) urls = SET_INIT; + +void tui_start(TUIData **tui_p, int *width, int *height, char **term, bool *rgb) + FUNC_ATTR_NONNULL_ALL { TUIData *tui = xcalloc(1, sizeof(TUIData)); tui->is_starting = true; @@ -157,7 +164,9 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term) tui->stopped = false; tui->seen_error_exit = 0; tui->loop = &main_loop; + tui->url = -1; kv_init(tui->invalid_regions); + kv_init(tui->urlbuf); signal_watcher_init(tui->loop, &tui->winch_handle, tui); // TODO(bfredl): zero hl is empty, send this explicitly? @@ -170,14 +179,14 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term) uv_timer_init(&tui->loop->uv, &tui->startup_delay_timer); tui->startup_delay_timer.data = tui; - uv_timer_start(&tui->startup_delay_timer, after_startup_cb, - 100, 0); + uv_timer_start(&tui->startup_delay_timer, after_startup_cb, 100, 0); *tui_p = tui; loop_poll_events(&main_loop, 1); *width = tui->width; *height = tui->height; *term = tui->term; + *rgb = tui->rgb; } void tui_set_key_encoding(TUIData *tui) @@ -261,6 +270,10 @@ static void tui_query_kitty_keyboard(TUIData *tui) out(tui, S_LEN("\x1b[?u\x1b[c")); } +/// Enable the alternate screen and emit other control sequences to start the TUI. +/// +/// This is also called when the TUI is resumed after being suspended. We reinitialize all state +/// from terminfo just in case the controlling terminal has changed (#27177). static void terminfo_start(TUIData *tui) { tui->scroll_region_is_full_screen = true; @@ -335,6 +348,9 @@ static void terminfo_start(TUIData *tui) int konsolev = konsolev_env ? (int)strtol(konsolev_env, NULL, 10) : (konsole ? 1 : 0); + // truecolor support must be checked before patching/augmenting terminfo + tui->rgb = term_has_truecolor(tui, colorterm); + patch_terminfo_bugs(tui, term, colorterm, vtev, konsolev, iterm_env, nsterm); augment_terminfo(tui, term, vtev, konsolev, iterm_env, nsterm); tui->can_change_scroll_region = @@ -407,10 +423,11 @@ static void terminfo_start(TUIData *tui) flush_buf(tui); } +/// Disable the alternate screen and prepare for the TUI to close. static void terminfo_stop(TUIData *tui) { // Destroy output stuff - tui_mode_change(tui, (String)STRING_INIT, SHAPE_IDX_N); + tui_mode_change(tui, NULL_STRING, SHAPE_IDX_N); tui_mouse_off(tui); unibi_out(tui, unibi_exit_attribute_mode); // Reset cursor to normal before exiting alternate screen. @@ -421,7 +438,7 @@ static void terminfo_stop(TUIData *tui) tui_reset_key_encoding(tui); // May restore old title before exiting alternate screen. - tui_set_title(tui, (String)STRING_INIT); + tui_set_title(tui, NULL_STRING); if (ui_client_exit_status == 0) { ui_client_exit_status = tui->seen_error_exit; } @@ -478,7 +495,7 @@ static void tui_terminal_after_startup(TUIData *tui) flush_buf(tui); } -/// stop the terminal but allow it to restart later (like after suspend) +/// Stop the terminal but allow it to restart later (like after suspend) static void tui_terminal_stop(TUIData *tui) { if (uv_is_closing((uv_handle_t *)&tui->output_handle)) { @@ -520,7 +537,15 @@ void tui_free_all_mem(TUIData *tui) { ugrid_free(&tui->grid); kv_destroy(tui->invalid_regions); + + const char *url; + set_foreach(&urls, url, { + xfree((void *)url); + }); + set_destroy(cstr_t, &urls); + kv_destroy(tui->attrs); + kv_destroy(tui->urlbuf); xfree(tui->space_buf); xfree(tui->term); xfree(tui); @@ -548,6 +573,10 @@ static bool attrs_differ(TUIData *tui, int id1, int id2, bool rgb) HlAttrs a1 = kv_A(tui->attrs, (size_t)id1); HlAttrs a2 = kv_A(tui->attrs, (size_t)id2); + if (a1.url != a2.url) { + return true; + } + if (rgb) { return a1.rgb_fg_color != a2.rgb_fg_color || a1.rgb_bg_color != a2.rgb_bg_color @@ -707,6 +736,19 @@ static void update_attrs(TUIData *tui, int attr_id) } } + if (tui->url != attrs.url) { + if (attrs.url >= 0) { + const char *url = urls.keys[attrs.url]; + kv_size(tui->urlbuf) = 0; + kv_printf(tui->urlbuf, "\x1b]8;;%s\x1b\\", url); + out(tui, tui->urlbuf.items, kv_size(tui->urlbuf)); + } else { + out(tui, S_LEN("\x1b]8;;\x1b\\")); + } + + tui->url = attrs.url; + } + tui->default_attr = fg == -1 && bg == -1 && !bold && !italic && !has_any_underline && !reverse && !standout && !strikethrough; @@ -783,6 +825,13 @@ static void cursor_goto(TUIData *tui, int row, int col) if (row == grid->row && col == grid->col) { return; } + + // If an OSC 8 sequence is active terminate it before moving the cursor + if (tui->url >= 0) { + out(tui, S_LEN("\x1b]8;;\x1b\\")); + tui->url = -1; + } + if (0 == row && 0 == col) { unibi_out(tui, unibi_cursor_home); ugrid_goto(grid, row, col); @@ -1007,10 +1056,7 @@ void tui_grid_resize(TUIData *tui, Integer g, Integer width, Integer height) { UGrid *grid = &tui->grid; ugrid_resize(grid, (int)width, (int)height); - - xfree(tui->space_buf); - tui->space_buf = xmalloc((size_t)width * sizeof(*tui->space_buf)); - memset(tui->space_buf, ' ', (size_t)width); + ensure_space_buf_size(tui, (size_t)width); // resize might not always be followed by a clear before flush // so clip the invalid region @@ -1222,8 +1268,10 @@ void tui_grid_scroll(TUIData *tui, Integer g, Integer startrow, Integer endrow, Integer endcol, Integer rows, Integer cols FUNC_ATTR_UNUSED) { UGrid *grid = &tui->grid; - int top = (int)startrow, bot = (int)endrow - 1; - int left = (int)startcol, right = (int)endcol - 1; + int top = (int)startrow; + int bot = (int)endrow - 1; + int left = (int)startcol; + int right = (int)endcol - 1; bool fullwidth = left == 0 && right == tui->width - 1; tui->scroll_region_is_full_screen = fullwidth @@ -1277,11 +1325,32 @@ void tui_grid_scroll(TUIData *tui, Integer g, Integer startrow, Integer endrow, } } +/// Add a URL to be used in an OSC 8 hyperlink. +/// +/// @param tui TUIData +/// @param url URL to add +/// @return Index of new URL, or -1 if URL is invalid +int32_t tui_add_url(TUIData *tui, const char *url) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (url == NULL) { + return -1; + } + + MHPutStatus status; + uint32_t k = set_put_idx(cstr_t, &urls, url, &status); + if (status != kMHExisting) { + urls.keys[k] = xstrdup(url); + } + return (int32_t)k; +} + void tui_hl_attr_define(TUIData *tui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, Array info) { attrs.cterm_ae_attr = cterm_attrs.cterm_ae_attr; attrs.cterm_fg_color = cterm_attrs.cterm_fg_color; attrs.cterm_bg_color = cterm_attrs.cterm_bg_color; + kv_a(tui->attrs, (size_t)id) = attrs; } @@ -1298,49 +1367,19 @@ void tui_visual_bell(TUIData *tui) void tui_default_colors_set(TUIData *tui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg, Integer cterm_bg) { - tui->clear_attrs.rgb_fg_color = (int)rgb_fg; - tui->clear_attrs.rgb_bg_color = (int)rgb_bg; - tui->clear_attrs.rgb_sp_color = (int)rgb_sp; - tui->clear_attrs.cterm_fg_color = (int)cterm_fg; - tui->clear_attrs.cterm_bg_color = (int)cterm_bg; + tui->clear_attrs.rgb_fg_color = (RgbValue)rgb_fg; + tui->clear_attrs.rgb_bg_color = (RgbValue)rgb_bg; + tui->clear_attrs.rgb_sp_color = (RgbValue)rgb_sp; + tui->clear_attrs.cterm_fg_color = (int16_t)cterm_fg; + tui->clear_attrs.cterm_bg_color = (int16_t)cterm_bg; tui->print_attr_id = -1; invalidate(tui, 0, tui->grid.height, 0, tui->grid.width); } -/// Begin flushing the TUI. If 'termsync' is set and the terminal supports synchronized updates, -/// begin a synchronized update. Otherwise, hide the cursor to avoid cursor jumping. -static void tui_flush_start(TUIData *tui) - FUNC_ATTR_NONNULL_ALL -{ - if (tui->sync_output && tui->unibi_ext.sync != -1) { - UNIBI_SET_NUM_VAR(tui->params[0], 1); - unibi_out_ext(tui, tui->unibi_ext.sync); - } else if (!tui->is_invisible) { - unibi_out(tui, unibi_cursor_invisible); - tui->is_invisible = true; - } -} - -/// Finish flushing the TUI. If 'termsync' is set and the terminal supports synchronized updates, -/// end a synchronized update. Otherwise, make the cursor visible again. -static void tui_flush_end(TUIData *tui) - FUNC_ATTR_NONNULL_ALL -{ - if (tui->sync_output && tui->unibi_ext.sync != -1) { - UNIBI_SET_NUM_VAR(tui->params[0], 0); - unibi_out_ext(tui, tui->unibi_ext.sync); - } - bool should_invisible = tui->busy || tui->want_invisible; - if (tui->is_invisible && !should_invisible) { - unibi_out(tui, unibi_cursor_normal); - tui->is_invisible = false; - } else if (!tui->is_invisible && should_invisible) { - unibi_out(tui, unibi_cursor_invisible); - tui->is_invisible = true; - } -} - +/// Flushes TUI grid state to a buffer (which is later flushed to the TTY by `flush_buf`). +/// +/// @see flush_buf void tui_flush(TUIData *tui) { UGrid *grid = &tui->grid; @@ -1357,8 +1396,6 @@ void tui_flush(TUIData *tui) tui_busy_stop(tui); // avoid hidden cursor } - tui_flush_start(tui); - while (kv_size(tui->invalid_regions)) { Rect r = kv_pop(tui->invalid_regions); assert(r.bot <= grid->height && r.right <= grid->width); @@ -1386,8 +1423,6 @@ void tui_flush(TUIData *tui) cursor_goto(tui, tui->row, tui->col); - tui_flush_end(tui); - flush_buf(tui); } @@ -1399,28 +1434,28 @@ static void show_verbose_terminfo(TUIData *tui) abort(); } - Array chunks = ARRAY_DICT_INIT; - Array title = ARRAY_DICT_INIT; - ADD(title, CSTR_TO_OBJ("\n\n--- Terminal info --- {{{\n")); - ADD(title, CSTR_TO_OBJ("Title")); - ADD(chunks, ARRAY_OBJ(title)); - Array info = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(chunks, 3); + MAXSIZE_TEMP_ARRAY(title, 2); + ADD_C(title, CSTR_AS_OBJ("\n\n--- Terminal info --- {{{\n")); + ADD_C(title, CSTR_AS_OBJ("Title")); + ADD_C(chunks, ARRAY_OBJ(title)); + MAXSIZE_TEMP_ARRAY(info, 2); String str = terminfo_info_msg(ut, tui->term); - ADD(info, STRING_OBJ(str)); - ADD(chunks, ARRAY_OBJ(info)); - Array end_fold = ARRAY_DICT_INIT; - ADD(end_fold, CSTR_TO_OBJ("}}}\n")); - ADD(end_fold, CSTR_TO_OBJ("Title")); - ADD(chunks, ARRAY_OBJ(end_fold)); - - Array args = ARRAY_DICT_INIT; - ADD(args, ARRAY_OBJ(chunks)); - ADD(args, BOOLEAN_OBJ(true)); // history - Dictionary opts = ARRAY_DICT_INIT; - PUT(opts, "verbose", BOOLEAN_OBJ(true)); - ADD(args, DICTIONARY_OBJ(opts)); + ADD_C(info, STRING_OBJ(str)); + ADD_C(chunks, ARRAY_OBJ(info)); + MAXSIZE_TEMP_ARRAY(end_fold, 2); + ADD_C(end_fold, CSTR_AS_OBJ("}}}\n")); + ADD_C(end_fold, CSTR_AS_OBJ("Title")); + ADD_C(chunks, ARRAY_OBJ(end_fold)); + + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, ARRAY_OBJ(chunks)); + ADD_C(args, BOOLEAN_OBJ(true)); // history + MAXSIZE_TEMP_DICT(opts, 1); + PUT_C(opts, "verbose", BOOLEAN_OBJ(true)); + ADD_C(args, DICTIONARY_OBJ(opts)); rpc_send_event(ui_client_channel_id, "nvim_echo", args); - api_free_array(args); + xfree(str.data); } void tui_suspend(TUIData *tui) @@ -1440,7 +1475,7 @@ void tui_suspend(TUIData *tui) tui_mouse_on(tui); } stream_set_blocking(tui->input.in_fd, false); // libuv expects this - ui_client_attach(tui->width, tui->height, tui->term); + ui_client_attach(tui->width, tui->height, tui->term, tui->rgb); #endif } @@ -1530,6 +1565,14 @@ void tui_option_set(TUIData *tui, String name, Object value) } } +void tui_chdir(TUIData *tui, String path) +{ + int err = uv_chdir(path.data); + if (err != 0) { + ELOG("Failed to chdir to %s: %s", path.data, strerror(err)); + } +} + void tui_raw_line(TUIData *tui, Integer g, Integer linerow, Integer startcol, Integer endcol, Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) @@ -1597,11 +1640,21 @@ static void invalidate(TUIData *tui, int top, int bot, int left, int right) } } +static void ensure_space_buf_size(TUIData *tui, size_t len) +{ + if (len > tui->space_buf_len) { + tui->space_buf = xrealloc(tui->space_buf, len * sizeof *tui->space_buf); + memset(tui->space_buf + tui->space_buf_len, ' ', len - tui->space_buf_len); + tui->space_buf_len = len; + } +} + /// Tries to get the user's wanted dimensions (columns and rows) for the entire /// application (i.e., the host terminal). void tui_guess_size(TUIData *tui) { - int width = 0, height = 0; + int width = 0; + int height = 0; // 1 - try from a system call(ioctl/TIOCGWINSZ on unix) if (tui->out_isatty @@ -1632,6 +1685,7 @@ void tui_guess_size(TUIData *tui) tui->width = width; tui->height = height; + ensure_space_buf_size(tui, (size_t)tui->width); // Redraw on SIGWINCH event if size didn't change. #23411 ui_client_set_size(width, height); @@ -1753,6 +1807,44 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name) return -1; } +/// Determine if the terminal supports truecolor or not: +/// +/// 1. If $COLORTERM is "24bit" or "truecolor", return true +/// 2. Else, check terminfo for Tc, RGB, setrgbf, or setrgbb capabilities. If +/// found, return true +/// 3. Else, return false +static bool term_has_truecolor(TUIData *tui, const char *colorterm) +{ + // Check $COLORTERM + if (strequal(colorterm, "truecolor") || strequal(colorterm, "24bit")) { + return true; + } + + // Check for Tc and RGB + for (size_t i = 0; i < unibi_count_ext_bool(tui->ut); i++) { + const char *n = unibi_get_ext_bool_name(tui->ut, i); + if (n && (!strcmp(n, "Tc") || !strcmp(n, "RGB"))) { + return true; + } + } + + // Check for setrgbf and setrgbb + bool setrgbf = false; + bool setrgbb = false; + for (size_t i = 0; i < unibi_count_ext_str(tui->ut) && (!setrgbf || !setrgbb); i++) { + const char *n = unibi_get_ext_str_name(tui->ut, i); + if (n) { + if (!setrgbf && !strcmp(n, "setrgbf")) { + setrgbf = true; + } else if (!setrgbb && !strcmp(n, "setrgbb")) { + setrgbb = true; + } + } + } + + return setrgbf && setrgbb; +} + /// Patches the terminfo records after loading from system or built-in db. /// Several entries in terminfo are known to be deficient or outright wrong; /// and several terminal emulators falsely announce incorrect terminal types. @@ -2254,23 +2346,103 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in } } +static bool should_invisible(TUIData *tui) +{ + return tui->busy || tui->want_invisible; +} + +/// Write the sequence to begin flushing output to `buf`. +/// If 'termsync' is set and the terminal supports synchronized output, begin synchronized update. +/// Otherwise, hide the cursor to avoid cursor jumping. +/// +/// @param buf the buffer to write the sequence to +/// @param len the length of `buf` +static size_t flush_buf_start(TUIData *tui, char *buf, size_t len) + FUNC_ATTR_NONNULL_ALL +{ + unibi_var_t params[9]; // Don't use tui->params[] as they may already be in use. + + const char *str = NULL; + if (tui->sync_output && tui->unibi_ext.sync != -1) { + UNIBI_SET_NUM_VAR(params[0], 1); + str = unibi_get_ext_str(tui->ut, (size_t)tui->unibi_ext.sync); + } else if (!tui->is_invisible) { + str = unibi_get_str(tui->ut, unibi_cursor_invisible); + tui->is_invisible = true; + } + + if (str == NULL) { + return 0; + } + + return unibi_run(str, params, buf, len); +} + +/// Write the sequence to end flushing output to `buf`. +/// If 'termsync' is set and the terminal supports synchronized output, end synchronized update. +/// Otherwise, make the cursor visible again. +/// +/// @param buf the buffer to write the sequence to +/// @param len the length of `buf` +static size_t flush_buf_end(TUIData *tui, char *buf, size_t len) + FUNC_ATTR_NONNULL_ALL +{ + unibi_var_t params[9]; // Don't use tui->params[] as they may already be in use. + + size_t offset = 0; + if (tui->sync_output && tui->unibi_ext.sync != -1) { + UNIBI_SET_NUM_VAR(params[0], 0); + const char *str = unibi_get_ext_str(tui->ut, (size_t)tui->unibi_ext.sync); + offset = unibi_run(str, params, buf, len); + } + + const char *str = NULL; + if (tui->is_invisible && !should_invisible(tui)) { + str = unibi_get_str(tui->ut, unibi_cursor_normal); + tui->is_invisible = false; + } else if (!tui->is_invisible && should_invisible(tui)) { + str = unibi_get_str(tui->ut, unibi_cursor_invisible); + tui->is_invisible = true; + } + + if (str != NULL) { + assert(len >= offset); + offset += unibi_run(str, params, buf + offset, len - offset); + } + + return offset; +} + +/// Flushes the rendered buffer to the TTY. +/// +/// @see tui_flush static void flush_buf(TUIData *tui) { uv_write_t req; - uv_buf_t buf; + uv_buf_t bufs[3]; + char pre[32]; + char post[32]; - if (tui->bufpos <= 0) { + if (tui->bufpos <= 0 && tui->is_invisible == should_invisible(tui)) { return; } - buf.base = tui->buf; - buf.len = UV_BUF_LEN(tui->bufpos); + bufs[0].base = pre; + bufs[0].len = UV_BUF_LEN(flush_buf_start(tui, pre, sizeof(pre))); + + bufs[1].base = tui->buf; + bufs[1].len = UV_BUF_LEN(tui->bufpos); + + bufs[2].base = post; + bufs[2].len = UV_BUF_LEN(flush_buf_end(tui, post, sizeof(post))); if (tui->screenshot) { - fwrite(buf.base, buf.len, 1, tui->screenshot); + for (size_t i = 0; i < ARRAY_SIZE(bufs); i++) { + fwrite(bufs[i].base, bufs[i].len, 1, tui->screenshot); + } } else { int ret - = uv_write(&req, (uv_stream_t *)&tui->output_handle, &buf, 1, NULL); + = uv_write(&req, (uv_stream_t *)&tui->output_handle, bufs, ARRAY_SIZE(bufs), NULL); if (ret) { ELOG("uv_write failed: %s", uv_strerror(ret)); } diff --git a/src/nvim/tui/tui.h b/src/nvim/tui/tui.h index 8eb4ac9bd8..12e0491b4b 100644 --- a/src/nvim/tui/tui.h +++ b/src/nvim/tui/tui.h @@ -1,23 +1,8 @@ #pragma once -#include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/highlight_defs.h" // IWYU pragma: keep -#include "nvim/types_defs.h" // IWYU pragma: keep -#include "nvim/ui.h" - -typedef struct TUIData TUIData; - -typedef enum { - kTermModeSynchronizedOutput = 2026, -} TermMode; - -typedef enum { - kTermModeNotRecognized = 0, - kTermModeSet = 1, - kTermModeReset = 2, - kTermModePermanentlySet = 3, - kTermModePermanentlyReset = 4, -} TermModeState; +#include "nvim/tui/tui_defs.h" // IWYU pragma: keep +#include "nvim/ui_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.h.generated.h" diff --git a/src/nvim/tui/tui_defs.h b/src/nvim/tui/tui_defs.h new file mode 100644 index 0000000000..c5149d4829 --- /dev/null +++ b/src/nvim/tui/tui_defs.h @@ -0,0 +1,15 @@ +#pragma once + +typedef struct TUIData TUIData; + +typedef enum { + kTermModeSynchronizedOutput = 2026, +} TermMode; + +typedef enum { + kTermModeNotRecognized = 0, + kTermModeSet = 1, + kTermModeReset = 2, + kTermModePermanentlySet = 3, + kTermModePermanentlyReset = 4, +} TermModeState; diff --git a/src/nvim/types_defs.h b/src/nvim/types_defs.h index c06737abb5..934159b9d9 100644 --- a/src/nvim/types_defs.h +++ b/src/nvim/types_defs.h @@ -1,6 +1,5 @@ #pragma once -#include <stdbool.h> #include <stdint.h> // dummy to pass an ACL to a function @@ -48,3 +47,12 @@ typedef enum { #define TRISTATE_FROM_INT(val) ((val) == 0 ? kFalse : ((val) >= 1 ? kTrue : kNone)) typedef int64_t OptInt; + +enum { SIGN_WIDTH = 2, }; ///< Number of display cells for a sign in the signcolumn + +typedef struct file_buffer buf_T; +typedef struct loop Loop; +typedef struct regprog regprog_T; +typedef struct syn_state synstate_T; +typedef struct terminal Terminal; +typedef struct window_S win_T; diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h index 54cd33e58f..8679769468 100644 --- a/src/nvim/ugrid.h +++ b/src/nvim/ugrid.h @@ -2,12 +2,12 @@ #include "nvim/types_defs.h" -typedef struct ucell { +typedef struct { schar_T data; sattr_T attr; } UCell; -typedef struct ugrid { +typedef struct { int row, col; int width, height; UCell **cells; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 36f34bc75a..bddcf98b53 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -1,10 +1,10 @@ #include <assert.h> #include <limits.h> #include <stdbool.h> -#include <stddef.h> #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <uv.h> #include "klib/kvec.h" #include "nvim/api/private/helpers.h" @@ -13,10 +13,12 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/cursor_shape.h" #include "nvim/drawscreen.h" +#include "nvim/event/multiqueue.h" #include "nvim/ex_getln.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" @@ -25,9 +27,12 @@ #include "nvim/lua/executor.h" #include "nvim/map_defs.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" +#include "nvim/os/os_defs.h" #include "nvim/os/time.h" #include "nvim/state_defs.h" #include "nvim/strings.h" @@ -37,13 +42,18 @@ #include "nvim/window.h" #include "nvim/winfloat.h" +typedef struct { + LuaRef cb; + bool ext_widgets[kUIGlobalCount]; +} UIEventCallback; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui.c.generated.h" #endif #define MAX_UI_COUNT 16 -static UI *uis[MAX_UI_COUNT]; +static RemoteUI *uis[MAX_UI_COUNT]; static bool ui_ext[kUIExtCount] = { 0 }; static size_t ui_count = 0; static int ui_mode_idx = SHAPE_IDX_N; @@ -59,6 +69,7 @@ bool ui_cb_ext[kUIExtCount]; ///< Internalized UI capabilities. static bool has_mouse = false; static int pending_has_mouse = -1; +static bool pending_default_colors = false; static Array call_buf = ARRAY_DICT_INIT; @@ -97,7 +108,7 @@ static void ui_log(const char *funname) do { \ bool any_call = false; \ for (size_t i = 0; i < ui_count; i++) { \ - UI *ui = uis[i]; \ + RemoteUI *ui = uis[i]; \ if ((cond)) { \ remote_ui_##funname(__VA_ARGS__); \ any_call = true; \ @@ -130,6 +141,8 @@ void ui_free_all_mem(void) free_ui_event_callback(event_cb); }) map_destroy(uint32_t, &ui_event_cbs); + + multiqueue_free(resize_events); } #endif @@ -190,7 +203,8 @@ void ui_refresh(void) return; } - int width = INT_MAX, height = INT_MAX; + int width = INT_MAX; + int height = INT_MAX; bool ext_widgets[kUIExtCount]; for (UIExtension i = 0; (int)i < kUIExtCount; i++) { ext_widgets[i] = true; @@ -198,7 +212,7 @@ void ui_refresh(void) bool inclusive = ui_override(); for (size_t i = 0; i < ui_count; i++) { - UI *ui = uis[i]; + RemoteUI *ui = uis[i]; width = MIN(ui->width, width); height = MIN(ui->height, height); for (UIExtension j = 0; (int)j < kUIExtCount; j++) { @@ -215,7 +229,7 @@ void ui_refresh(void) } ui_ext[i] = ext_widgets[i]; if (i < kUIGlobalCount) { - ui_call_option_set(cstr_as_string((char *)ui_ext_names[i]), + ui_call_option_set(cstr_as_string(ui_ext_names[i]), BOOLEAN_OBJ(ext_widgets[i])); } } @@ -228,7 +242,7 @@ void ui_refresh(void) p_lz = save_p_lz; if (ext_widgets[kUIMessages]) { - set_option_value("cmdheight", NUMBER_OPTVAL(0), 0); + set_option_value(kOptCmdheight, NUMBER_OPTVAL(0), 0); command_height(); } ui_mode_info_set(); @@ -272,13 +286,26 @@ static void ui_refresh_event(void **argv) void ui_schedule_refresh(void) { - multiqueue_put(resize_events, ui_refresh_event, 0); + multiqueue_put(resize_events, ui_refresh_event, NULL); } void ui_default_colors_set(void) { - ui_call_default_colors_set(normal_fg, normal_bg, normal_sp, - cterm_normal_fg_color, cterm_normal_bg_color); + // Throttle setting of default colors at startup, so it only happens once + // if the user sets the colorscheme in startup. + pending_default_colors = true; + if (starting == 0) { + ui_may_set_default_colors(); + } +} + +static void ui_may_set_default_colors(void) +{ + if (pending_default_colors) { + pending_default_colors = false; + ui_call_default_colors_set(normal_fg, normal_bg, normal_sp, + cterm_normal_fg_color, cterm_normal_bg_color); + } } void ui_busy_start(void) @@ -340,12 +367,11 @@ void vim_beep(unsigned val) void do_autocmd_uienter_all(void) { for (size_t i = 0; i < ui_count; i++) { - UIData *data = uis[i]->data; - do_autocmd_uienter(data->channel_id, true); + do_autocmd_uienter(uis[i]->channel_id, true); } } -void ui_attach_impl(UI *ui, uint64_t chanid) +void ui_attach_impl(RemoteUI *ui, uint64_t chanid) { if (ui_count == MAX_UI_COUNT) { abort(); @@ -359,6 +385,12 @@ void ui_attach_impl(UI *ui, uint64_t chanid) ui_refresh_options(); resettitle(); + char cwd[MAXPATHL]; + size_t cwdlen = sizeof(cwd); + if (uv_cwd(cwd, &cwdlen) == 0) { + ui_call_chdir((String){ .data = cwd, .size = cwdlen }); + } + for (UIExtension i = kUIGlobalCount; (int)i < kUIExtCount; i++) { ui_set_ext_option(ui, i, ui->ui_ext[i]); } @@ -375,7 +407,7 @@ void ui_attach_impl(UI *ui, uint64_t chanid) do_autocmd_uienter(chanid, true); } -void ui_detach_impl(UI *ui, uint64_t chanid) +void ui_detach_impl(RemoteUI *ui, uint64_t chanid) { size_t shift_index = MAX_UI_COUNT; @@ -411,31 +443,32 @@ void ui_detach_impl(UI *ui, uint64_t chanid) do_autocmd_uienter(chanid, false); } -void ui_set_ext_option(UI *ui, UIExtension ext, bool active) +void ui_set_ext_option(RemoteUI *ui, UIExtension ext, bool active) { if (ext < kUIGlobalCount) { ui_refresh(); return; } if (ui_ext_names[ext][0] != '_' || active) { - remote_ui_option_set(ui, cstr_as_string((char *)ui_ext_names[ext]), - BOOLEAN_OBJ(active)); + remote_ui_option_set(ui, cstr_as_string(ui_ext_names[ext]), BOOLEAN_OBJ(active)); } if (ext == kUITermColors) { ui_default_colors_set(); } } -void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, int clearattr, - bool wrap) +void ui_line(ScreenGrid *grid, int row, bool invalid_row, int startcol, int endcol, int clearcol, + int clearattr, bool wrap) { assert(0 <= row && row < grid->rows); LineFlags flags = wrap ? kLineFlagWrap : 0; - if (startcol == -1) { - startcol = 0; + if (startcol == 0 && invalid_row) { flags |= kLineFlagInvalid; } + // set default colors now so that that text won't have to be repainted later + ui_may_set_default_colors(); + size_t off = grid->line_offset[row] + (size_t)startcol; ui_call_raw_line(grid->handle, row, startcol, endcol, clearcol, clearattr, @@ -611,34 +644,35 @@ bool ui_has(UIExtension ext) return ui_ext[ext]; } -Array ui_array(void) +Array ui_array(Arena *arena) { - Array all_uis = ARRAY_DICT_INIT; + Array all_uis = arena_array(arena, ui_count); for (size_t i = 0; i < ui_count; i++) { - UI *ui = uis[i]; - Dictionary info = ARRAY_DICT_INIT; - PUT(info, "width", INTEGER_OBJ(ui->width)); - PUT(info, "height", INTEGER_OBJ(ui->height)); - PUT(info, "rgb", BOOLEAN_OBJ(ui->rgb)); - PUT(info, "override", BOOLEAN_OBJ(ui->override)); + RemoteUI *ui = uis[i]; + Dictionary info = arena_dict(arena, 10 + kUIExtCount); + PUT_C(info, "width", INTEGER_OBJ(ui->width)); + PUT_C(info, "height", INTEGER_OBJ(ui->height)); + PUT_C(info, "rgb", BOOLEAN_OBJ(ui->rgb)); + PUT_C(info, "override", BOOLEAN_OBJ(ui->override)); // TUI fields. (`stdin_fd` is intentionally omitted.) - PUT(info, "term_name", CSTR_TO_OBJ(ui->term_name)); + PUT_C(info, "term_name", CSTR_AS_OBJ(ui->term_name)); // term_background is deprecated. Populate with an empty string - PUT(info, "term_background", CSTR_TO_OBJ("")); + PUT_C(info, "term_background", STATIC_CSTR_AS_OBJ("")); - PUT(info, "term_colors", INTEGER_OBJ(ui->term_colors)); - PUT(info, "stdin_tty", BOOLEAN_OBJ(ui->stdin_tty)); - PUT(info, "stdout_tty", BOOLEAN_OBJ(ui->stdout_tty)); + PUT_C(info, "term_colors", INTEGER_OBJ(ui->term_colors)); + PUT_C(info, "stdin_tty", BOOLEAN_OBJ(ui->stdin_tty)); + PUT_C(info, "stdout_tty", BOOLEAN_OBJ(ui->stdout_tty)); for (UIExtension j = 0; j < kUIExtCount; j++) { if (ui_ext_names[j][0] != '_' || ui->ui_ext[j]) { - PUT(info, ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j])); + PUT_C(info, (char *)ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j])); } } - remote_ui_inspect(ui, &info); - ADD(all_uis, DICTIONARY_OBJ(info)); + PUT_C(info, "chan", INTEGER_OBJ((Integer)ui->channel_id)); + + ADD_C(all_uis, DICTIONARY_OBJ(info)); } return all_uis; } @@ -657,9 +691,9 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *err) if (wp->w_floating) { if (width != wp->w_width || height != wp->w_height) { - wp->w_float_config.width = width; - wp->w_float_config.height = height; - win_config_float(wp, wp->w_float_config); + wp->w_config.width = width; + wp->w_config.height = height; + win_config_float(wp, wp->w_config); } } else { // non-positive indicates no request @@ -675,8 +709,8 @@ void ui_call_event(char *name, Array args) bool handled = false; map_foreach_value(&ui_event_cbs, event_cb, { Error err = ERROR_INIT; - Object res = nlua_call_ref(event_cb->cb, name, args, false, &err); - if (res.type == kObjectTypeBoolean && res.data.boolean == true) { + Object res = nlua_call_ref(event_cb->cb, name, args, kRetNilBool, NULL, &err); + if (LUARET_TRUTHY(res)) { handled = true; } if (ERROR_SET(&err)) { @@ -692,7 +726,7 @@ void ui_call_event(char *name, Array args) ui_log(name); } -void ui_cb_update_ext(void) +static void ui_cb_update_ext(void) { memset(ui_cb_ext, 0, ARRAY_SIZE(ui_cb_ext)); @@ -708,7 +742,7 @@ void ui_cb_update_ext(void) } } -void free_ui_event_callback(UIEventCallback *event_cb) +static void free_ui_event_callback(UIEventCallback *event_cb) { api_free_luaref(event_cb->cb); xfree(event_cb); diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 666a869c89..52b1334b15 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -1,128 +1,19 @@ #pragma once -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> +#include <stdint.h> // IWYU pragma: keep -#include "nvim/api/private/defs.h" -#include "nvim/event/multiqueue.h" -#include "nvim/globals.h" -#include "nvim/highlight_defs.h" +#include "nvim/api/private/defs.h" // IWYU pragma: keep +#include "nvim/event/defs.h" +#include "nvim/grid_defs.h" // IWYU pragma: keep +#include "nvim/highlight_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" -#include "nvim/memory.h" -#include "nvim/types_defs.h" - -struct ui_t; - -typedef enum { - kUICmdline = 0, - kUIPopupmenu, - kUITabline, - kUIWildmenu, - kUIMessages, -#define kUIGlobalCount kUILinegrid - kUILinegrid, - kUIMultigrid, - kUIHlState, - kUITermColors, - kUIFloatDebug, - kUIExtCount, -} UIExtension; - -EXTERN const char *ui_ext_names[] INIT( = { - "ext_cmdline", - "ext_popupmenu", - "ext_tabline", - "ext_wildmenu", - "ext_messages", - "ext_linegrid", - "ext_multigrid", - "ext_hlstate", - "ext_termcolors", - "_debug_float", -}); - -typedef struct ui_t UI; - -enum { - kLineFlagWrap = 1, - kLineFlagInvalid = 2, -}; - -typedef int LineFlags; - -typedef struct { - uint64_t channel_id; - -#define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data. - /// guaranteed size available for each new event (so packing of simple events - /// and the header of grid_line will never fail) -#define EVENT_BUF_SIZE 256 - char buf[UI_BUF_SIZE]; ///< buffer of packed but not yet sent msgpack data - char *buf_wptr; ///< write head of buffer - const char *cur_event; ///< name of current event (might get multiple arglists) - Array call_buf; ///< buffer for constructing a single arg list (max 16 elements!) - - // state for write_cb, while packing a single arglist to msgpack. This - // might fail due to buffer overflow. - size_t pack_totlen; - bool buf_overflow; - char *temp_buf; - - // We start packing the two outermost msgpack arrays before knowing the total - // number of elements. Thus track the location where array size will need - // to be written in the msgpack buffer, once the specific array is finished. - char *nevents_pos; - char *ncalls_pos; - uint32_t nevents; ///< number of distinct events (top-level args to "redraw" - uint32_t ncalls; ///< number of calls made to the current event (plus one for the name!) - bool flushed_events; ///< events where sent to client without "flush" event - - size_t ncells_pending; ///< total number of cells since last buffer flush - - int hl_id; // Current highlight for legacy put event. - Integer cursor_row, cursor_col; // Intended visible cursor position. - - // Position of legacy cursor, used both for drawing and visible user cursor. - Integer client_row, client_col; - bool wildmenu_active; -} UIData; - -struct ui_t { - bool rgb; - bool override; ///< Force highest-requested UI capabilities. - bool composed; - bool ui_ext[kUIExtCount]; ///< Externalized UI capabilities. - int width; - int height; - int pum_nlines; /// actual nr. lines shown in PUM - bool pum_pos; /// UI reports back pum position? - double pum_row; - double pum_col; - double pum_height; - double pum_width; - - // TUI fields. - char *term_name; - char *term_background; ///< Deprecated. No longer needed since background color detection happens - ///< in Lua. To be removed in a future release. - int term_colors; - bool stdin_tty; - bool stdout_tty; - - // TODO(bfredl): integrate into struct! - UIData data[1]; -}; - -typedef struct ui_event_callback { - LuaRef cb; - bool ext_widgets[kUIGlobalCount]; -} UIEventCallback; +#include "nvim/types_defs.h" // IWYU pragma: keep +#include "nvim/ui_defs.h" // IWYU pragma: keep // uncrustify:off #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui.h.generated.h" -# include "ui_events_call.h.generated.h" // IWYU pragma: export +# include "ui_events_call.h.generated.h" #endif // uncrustify:on diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 30f44d182d..4f36cae4b2 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -5,23 +5,29 @@ #include <stdlib.h> #include "nvim/api/keysets_defs.h" +#include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" -#include "nvim/event/loop.h" -#include "nvim/func_attr.h" +#include "nvim/event/multiqueue.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/log.h" #include "nvim/main.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/os/os_defs.h" #include "nvim/tui/tui.h" +#include "nvim/tui/tui_defs.h" #include "nvim/ui.h" #include "nvim/ui_client.h" +#include "nvim/ui_defs.h" #ifdef MSWIN # include "nvim/os/os_win_console.h" @@ -56,6 +62,9 @@ uint64_t ui_client_start_server(int argc, char **argv) CALLBACK_READER_INIT, on_err, CALLBACK_NONE, false, true, true, false, kChannelStdinPipe, NULL, 0, 0, NULL, &exit_status); + if (!channel) { + return 0; + } // If stdin is not a pty, it is forwarded to the client. // Replace stdin in the TUI process with the tty fd. @@ -71,14 +80,14 @@ uint64_t ui_client_start_server(int argc, char **argv) return channel->id; } -void ui_client_attach(int width, int height, char *term) +void ui_client_attach(int width, int height, char *term, bool rgb) { MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ(width)); ADD_C(args, INTEGER_OBJ(height)); MAXSIZE_TEMP_DICT(opts, 9); - PUT_C(opts, "rgb", BOOLEAN_OBJ(true)); + PUT_C(opts, "rgb", BOOLEAN_OBJ(rgb)); PUT_C(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT_C(opts, "ext_termcolors", BOOLEAN_OBJ(true)); if (term) { @@ -112,9 +121,10 @@ void ui_client_run(bool remote_ui) ui_client_is_remote = remote_ui; int width, height; char *term; - tui_start(&tui, &width, &height, &term); + bool rgb; + tui_start(&tui, &width, &height, &term, &rgb); - ui_client_attach(width, height, term); + ui_client_attach(width, height, term, rgb); // os_exit() will be invoked when the client channel detaches while (true) { @@ -163,12 +173,19 @@ Object handle_ui_client_redraw(uint64_t channel_id, Array args, Arena *arena, Er static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb) { Error err = ERROR_INIT; - Dict(highlight) dict = { 0 }; + Dict(highlight) dict = KEYDICT_INIT; if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, d, &err)) { // TODO(bfredl): log "err" return HLATTRS_INIT; } - return dict2hlattrs(&dict, rgb, NULL, &err); + + HlAttrs attrs = dict2hlattrs(&dict, rgb, NULL, &err); + + if (HAS_KEY(&dict, highlight, url)) { + attrs.url = tui_add_url(tui, dict.url.data); + } + + return attrs; } void ui_client_event_grid_resize(Array args) @@ -203,7 +220,9 @@ void ui_client_event_grid_line(Array args) void ui_client_event_raw_line(GridLineEvent *g) { - int grid = g->args[0], row = g->args[1], startcol = g->args[2]; + int grid = g->args[0]; + int row = g->args[1]; + int startcol = g->args[2]; Integer endcol = startcol + g->coloff; Integer clearcol = endcol + g->clear_width; LineFlags lineflags = g->wrap ? kLineFlagWrap : 0; @@ -211,3 +230,12 @@ void ui_client_event_raw_line(GridLineEvent *g) tui_raw_line(tui, grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags, (const schar_T *)grid_line_buf_char, grid_line_buf_attr); } + +#ifdef EXITFREE +void ui_client_free_all_mem(void) +{ + tui_free_all_mem(tui); + xfree(grid_line_buf_char); + xfree(grid_line_buf_attr); +} +#endif diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index 93170ed86d..d3be9882aa 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -4,15 +4,10 @@ #include <stddef.h> #include <stdint.h> -#include "nvim/api/private/defs.h" #include "nvim/grid_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" #include "nvim/types_defs.h" - -typedef struct { - const char *name; - void (*fn)(Array args); -} UIClientHandler; +#include "nvim/ui_defs.h" // IWYU pragma: keep // Temporary buffer for converting a single grid_line event EXTERN size_t grid_line_buf_size INIT( = 0); @@ -39,6 +34,6 @@ EXTERN bool ui_client_forward_stdin INIT( = false); // uncrustify:off #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_client.h.generated.h" -# include "ui_events_client.h.generated.h" // IWYU pragma: export +# include "ui_events_client.h.generated.h" #endif // uncrustify:on diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index b698e017dc..6e89894e0b 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -9,14 +9,16 @@ #include <stdbool.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/log.h" #include "nvim/macros_defs.h" @@ -60,6 +62,15 @@ void ui_comp_init(void) curgrid = &default_grid; } +#ifdef EXITFREE +void ui_comp_free_all_mem(void) +{ + kv_destroy(layers); + xfree(linebuf); + xfree(attrbuf); +} +#endif + void ui_comp_syn_init(void) { dbghl_normal = syn_check_group(S_LEN("RedrawDebugNormal")); @@ -68,13 +79,13 @@ void ui_comp_syn_init(void) dbghl_recompose = syn_check_group(S_LEN("RedrawDebugRecompose")); } -void ui_comp_attach(UI *ui) +void ui_comp_attach(RemoteUI *ui) { composed_uis++; ui->composed = true; } -void ui_comp_detach(UI *ui) +void ui_comp_detach(RemoteUI *ui) { composed_uis--; if (composed_uis == 0) { @@ -296,7 +307,8 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag startcol = MAX(startcol, 0); // in case we start on the right half of a double-width char, we need to // check the left half. But skip it in output if it wasn't doublewidth. - int skipstart = 0, skipend = 0; + int skipstart = 0; + int skipend = 0; if (startcol > 0 && (flags & kLineFlagInvalid)) { startcol--; skipstart = 1; diff --git a/src/nvim/ui_compositor.h b/src/nvim/ui_compositor.h index f3f5981680..b0dd8e126b 100644 --- a/src/nvim/ui_compositor.h +++ b/src/nvim/ui_compositor.h @@ -1,10 +1,9 @@ #pragma once #include "nvim/api/private/defs.h" // IWYU pragma: keep -#include "nvim/event/defs.h" #include "nvim/grid_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep -#include "nvim/ui.h" +#include "nvim/ui_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_compositor.h.generated.h" diff --git a/src/nvim/ui_defs.h b/src/nvim/ui_defs.h new file mode 100644 index 0000000000..4d73cc2321 --- /dev/null +++ b/src/nvim/ui_defs.h @@ -0,0 +1,90 @@ +#pragma once + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "nvim/api/private/defs.h" +#include "nvim/msgpack_rpc/packer_defs.h" + +/// Keep in sync with ui_ext_names[] in ui.h +typedef enum { + kUICmdline = 0, + kUIPopupmenu, + kUITabline, + kUIWildmenu, + kUIMessages, +#define kUIGlobalCount kUILinegrid + kUILinegrid, + kUIMultigrid, + kUIHlState, + kUITermColors, + kUIFloatDebug, + kUIExtCount, +} UIExtension; + +enum { + kLineFlagWrap = 1, + kLineFlagInvalid = 2, +}; + +typedef int LineFlags; + +typedef struct { + bool rgb; + bool override; ///< Force highest-requested UI capabilities. + bool composed; + bool ui_ext[kUIExtCount]; ///< Externalized UI capabilities. + int width; + int height; + int pum_nlines; /// actual nr. lines shown in PUM + bool pum_pos; /// UI reports back pum position? + double pum_row; + double pum_col; + double pum_height; + double pum_width; + + // TUI fields. + char *term_name; + char *term_background; ///< Deprecated. No longer needed since background color detection happens + ///< in Lua. To be removed in a future release. + int term_colors; + bool stdin_tty; + bool stdout_tty; + + uint64_t channel_id; + +#define UI_BUF_SIZE ARENA_BLOCK_SIZE ///< total buffer size for pending msgpack data. + /// guaranteed size available for each new event (so packing of simple events + /// and the header of grid_line will never fail) +#define EVENT_BUF_SIZE 256 + +// Fields related to packing + PackerBuffer packer; + + const char *cur_event; ///< name of current event (might get multiple arglists) + Array call_buf; ///< buffer for constructing a single arg list (max 16 elements!) + + // We start packing the two outermost msgpack arrays before knowing the total + // number of elements. Thus track the location where array size will need + // to be written in the msgpack buffer, once the specific array is finished. + char *nevents_pos; + char *ncalls_pos; + uint32_t nevents; ///< number of distinct events (top-level args to "redraw" + uint32_t ncalls; ///< number of calls made to the current event (plus one for the name!) + bool flushed_events; ///< events where sent to client without "flush" event + + size_t ncells_pending; ///< total number of cells since last buffer flush + + int hl_id; // Current highlight for legacy put event. + Integer cursor_row, cursor_col; // Intended visible cursor position. + + // Position of legacy cursor, used both for drawing and visible user cursor. + Integer client_row, client_col; + bool wildmenu_active; +} RemoteUI; + +typedef struct { + const char *name; + void (*fn)(Array args); +} UIClientHandler; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 93a973c33d..e9170ba858 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -77,7 +77,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> #include <time.h> #include <uv.h> @@ -86,9 +85,11 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/cursor.h" +#include "nvim/decoration.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" #include "nvim/eval/funcs.h" @@ -97,25 +98,33 @@ #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" +#include "nvim/marktree_defs.h" +#include "nvim/mbyte.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" #include "nvim/os/os_defs.h" #include "nvim/os/time.h" +#include "nvim/os/time_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/sha256.h" @@ -345,7 +354,7 @@ static inline void zero_fmark_additional_data(fmark_T *fmarks) /// "reload" is true when saving for a buffer reload. /// Careful: may trigger autocommands that reload the buffer. /// Returns FAIL when lines could not be saved, OK otherwise. -int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int reload) +int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, bool reload) { if (!reload) { // When making changes is not allowed return FAIL. It's a crude way @@ -935,7 +944,7 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi, const char *file_name) default: // Field not supported, skip it. while (--len >= 0) { - (void)undo_read_byte(bi); + undo_read_byte(bi); } } } @@ -964,12 +973,11 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi, const char *file_name) } // Unserialize all extmark undo information - ExtmarkUndoObject *extup; kv_init(uhp->uh_extmark); while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC) { bool error = false; - extup = unserialize_extmark(bi, &error, file_name); + ExtmarkUndoObject *extup = unserialize_extmark(bi, &error, file_name); if (error) { kv_destroy(uhp->uh_extmark); xfree(extup); @@ -1257,7 +1265,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, semsg(_(e_not_open), file_name); goto theend; } - (void)os_setperm(file_name, perm); + os_setperm(file_name, perm); if (p_verbose > 0) { verbose_enter(); smsg(0, _("Writing undo file: %s"), file_name); @@ -1502,7 +1510,7 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT default: // field not supported, skip while (--len >= 0) { - (void)undo_read_byte(&bi); + undo_read_byte(&bi); } } } @@ -1554,7 +1562,9 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT // We have put all of the headers into a table. Now we iterate through the // table and swizzle each sequence number we have stored in uh_*_seq into // a pointer corresponding to the header with that sequence number. - int16_t old_idx = -1, new_idx = -1, cur_idx = -1; + int16_t old_idx = -1; + int16_t new_idx = -1; + int16_t cur_idx = -1; for (int i = 0; i < num_head; i++) { u_header_T *uhp = uhp_table[i]; if (uhp == NULL) { @@ -2250,7 +2260,7 @@ target_zero: /// /// @param undo If `true`, go up the tree. Down if `false`. /// @param do_buf_event If `true`, send buffer updates. -static void u_undoredo(int undo, bool do_buf_event) +static void u_undoredo(bool undo, bool do_buf_event) { char **newarray = NULL; linenr_T newlnum = MAXLNUM; @@ -2416,17 +2426,14 @@ static void u_undoredo(int undo, bool do_buf_event) } // Adjust Extmarks - ExtmarkUndoObject undo_info; if (undo) { for (int i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) { - undo_info = kv_A(curhead->uh_extmark, i); - extmark_apply_undo(undo_info, undo); + extmark_apply_undo(kv_A(curhead->uh_extmark, i), undo); } // redo } else { for (int i = 0; i < (int)kv_size(curhead->uh_extmark); i++) { - undo_info = kv_A(curhead->uh_extmark, i); - extmark_apply_undo(undo_info, undo); + extmark_apply_undo(kv_A(curhead->uh_extmark, i), undo); } } if (curhead->uh_flags & UH_RELOAD) { @@ -2434,7 +2441,7 @@ static void u_undoredo(int undo, bool do_buf_event) // should have all info to send a buffer-reloaing on_lines/on_bytes event buf_updates_unload(curbuf, true); } - // finish Adjusting extmarks + // Finish adjusting extmarks curhead->uh_entry = newlist; curhead->uh_flags = new_flags; @@ -2981,6 +2988,28 @@ void u_clearall(buf_T *buf) buf->b_u_line_lnum = 0; } +/// Free all allocated memory blocks for the buffer 'buf'. +void u_blockfree(buf_T *buf) +{ + while (buf->b_u_oldhead != NULL) { +#ifndef NDEBUG + u_header_T *previous_oldhead = buf->b_u_oldhead; +#endif + + u_freeheader(buf, buf->b_u_oldhead, NULL); + assert(buf->b_u_oldhead != previous_oldhead); + } + xfree(buf->b_u_line_ptr); +} + +/// Free all allocated memory blocks for the buffer 'buf'. +/// and invalidate the undo buffer +void u_clearallandblockfree(buf_T *buf) +{ + u_blockfree(buf); + u_clearall(buf); +} + /// Save the line "lnum" for the "U" command. void u_saveline(buf_T *buf, linenr_T lnum) { @@ -3032,9 +3061,9 @@ void u_undoline(void) char *oldp = u_save_line(curbuf->b_u_line_lnum); ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, true); - changed_bytes(curbuf->b_u_line_lnum, 0); extmark_splice_cols(curbuf, (int)curbuf->b_u_line_lnum - 1, 0, (colnr_T)strlen(oldp), (colnr_T)strlen(curbuf->b_u_line_ptr), kExtmarkUndo); + changed_bytes(curbuf->b_u_line_lnum, 0); xfree(curbuf->b_u_line_ptr); curbuf->b_u_line_ptr = oldp; @@ -3047,20 +3076,6 @@ void u_undoline(void) check_cursor_col(); } -/// Free all allocated memory blocks for the buffer 'buf'. -void u_blockfree(buf_T *buf) -{ - while (buf->b_u_oldhead != NULL) { -#ifndef NDEBUG - u_header_T *previous_oldhead = buf->b_u_oldhead; -#endif - - u_freeheader(buf, buf->b_u_oldhead, NULL); - assert(buf->b_u_oldhead != previous_oldhead); - } - xfree(buf->b_u_line_ptr); -} - /// Allocate memory and copy curbuf line into it. /// /// @param lnum the line to copy diff --git a/src/nvim/undo.h b/src/nvim/undo.h index f3a8a60d45..800a1bfae0 100644 --- a/src/nvim/undo.h +++ b/src/nvim/undo.h @@ -1,10 +1,10 @@ #pragma once -#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep -#include "nvim/undo_defs.h" // IWYU pragma: export +#include "nvim/undo_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "undo.h.generated.h" diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h index 0b78ea543f..39786fa928 100644 --- a/src/nvim/undo_defs.h +++ b/src/nvim/undo_defs.h @@ -4,10 +4,8 @@ #include "nvim/extmark_defs.h" #include "nvim/mark_defs.h" -#include "nvim/pos_defs.h" -/// Size in bytes of the hash used in the undo file. -enum { UNDO_HASH_SIZE = 32, }; +enum { UNDO_HASH_SIZE = 32, }; ///< Size in bytes of the hash used in the undo file. typedef struct u_header u_header_T; @@ -19,8 +17,6 @@ typedef struct { colnr_T vi_curswant; ///< MAXCOL from w_curswant } visualinfo_T; -#include "nvim/buffer_defs.h" - typedef struct u_entry u_entry_T; struct u_entry { u_entry_T *ue_next; ///< pointer to next entry in list diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 7c65af5138..8d41edec8a 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -8,7 +8,6 @@ #include <string.h> #include "auto/config.h" -#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" @@ -16,11 +15,11 @@ #include "nvim/cmdexpand_defs.h" #include "nvim/eval.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/keycodes.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" @@ -77,6 +76,7 @@ static const char *command_complete[] = { [EXPAND_HELP] = "help", [EXPAND_HIGHLIGHT] = "highlight", [EXPAND_HISTORY] = "history", + [EXPAND_KEYMAP] = "keymap", #ifdef HAVE_WORKING_LIBINTL [EXPAND_LOCALES] = "locale", #endif @@ -584,7 +584,7 @@ static void uc_list(char *name, size_t name_len) msg_outtrans(IObuff, 0); if (cmd->uc_luaref != LUA_NOREF) { - char *fn = nlua_funcref_str(cmd->uc_luaref); + char *fn = nlua_funcref_str(cmd->uc_luaref, NULL); msg_puts_attr(fn, HL_ATTR(HLF_8)); xfree(fn); // put the description on a new line @@ -874,7 +874,7 @@ int uc_add_command(char *name, size_t name_len, const char *rep, uint32_t argt, char *rep_buf = NULL; garray_T *gap; - replace_termcodes(rep, strlen(rep), &rep_buf, 0, 0, NULL, CPO_TO_CPO_FLAGS); + replace_termcodes(rep, strlen(rep), &rep_buf, 0, 0, NULL, p_cpo); if (rep_buf == NULL) { // Can't replace termcodes - try using the string as is rep_buf = xstrdup(rep); @@ -1136,6 +1136,20 @@ bool uc_split_args_iter(const char *arg, size_t arglen, size_t *end, char *buf, return true; } +size_t uc_nargs_upper_bound(const char *arg, size_t arglen) +{ + bool was_white = true; // space before first arg + size_t nargs = 0; + for (size_t i = 0; i < arglen; i++) { + bool is_white = ascii_iswhite(arg[i]); + if (was_white && !is_white) { + nargs++; + } + was_white = is_white; + } + return nargs; +} + /// split and quote args for <f-args> static char *uc_split_args(const char *arg, char **args, const size_t *arglens, size_t argc, size_t *lenp) @@ -1710,8 +1724,8 @@ int do_ucmd(exarg_T *eap, bool preview) save_current_sctx = current_sctx; current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; } - (void)do_cmdline(buf, eap->getline, eap->cookie, - DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); + do_cmdline(buf, eap->getline, eap->cookie, + DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); // Careful: Do not use "cmd" here, it may have become invalid if a user // command was added. @@ -1730,25 +1744,24 @@ int do_ucmd(exarg_T *eap, bool preview) /// @param buf Buffer to inspect, or NULL to get global commands. /// /// @return Map of maps describing commands -Dictionary commands_array(buf_T *buf) +Dictionary commands_array(buf_T *buf, Arena *arena) { - Dictionary rv = ARRAY_DICT_INIT; - char str[20]; garray_T *gap = (buf == NULL) ? &ucmds : &buf->b_ucmds; + Dictionary rv = arena_dict(arena, (size_t)gap->ga_len); for (int i = 0; i < gap->ga_len; i++) { char arg[2] = { 0, 0 }; - Dictionary d = ARRAY_DICT_INIT; + Dictionary d = arena_dict(arena, 14); ucmd_T *cmd = USER_CMD_GA(gap, i); - PUT(d, "name", CSTR_TO_OBJ(cmd->uc_name)); - PUT(d, "definition", CSTR_TO_OBJ(cmd->uc_rep)); - PUT(d, "script_id", INTEGER_OBJ(cmd->uc_script_ctx.sc_sid)); - PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); - PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); - PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); - PUT(d, "keepscript", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_KEEPSCRIPT))); - PUT(d, "preview", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_PREVIEW))); + PUT_C(d, "name", CSTR_AS_OBJ(cmd->uc_name)); + PUT_C(d, "definition", CSTR_AS_OBJ(cmd->uc_rep)); + PUT_C(d, "script_id", INTEGER_OBJ(cmd->uc_script_ctx.sc_sid)); + PUT_C(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); + PUT_C(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); + PUT_C(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); + PUT_C(d, "keepscript", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_KEEPSCRIPT))); + PUT_C(d, "preview", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_PREVIEW))); switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { case 0: @@ -1762,49 +1775,47 @@ Dictionary commands_array(buf_T *buf) case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): arg[0] = '1'; break; } - PUT(d, "nargs", CSTR_TO_OBJ(arg)); + PUT_C(d, "nargs", CSTR_TO_ARENA_OBJ(arena, arg)); char *cmd_compl = get_command_complete(cmd->uc_compl); - PUT(d, "complete", (cmd_compl == NULL - ? NIL : CSTR_TO_OBJ(cmd_compl))); - PUT(d, "complete_arg", cmd->uc_compl_arg == NULL - ? NIL : CSTR_TO_OBJ(cmd->uc_compl_arg)); + PUT_C(d, "complete", (cmd_compl == NULL + ? NIL : CSTR_AS_OBJ(cmd_compl))); + PUT_C(d, "complete_arg", cmd->uc_compl_arg == NULL + ? NIL : CSTR_AS_OBJ(cmd->uc_compl_arg)); Object obj = NIL; if (cmd->uc_argt & EX_COUNT) { if (cmd->uc_def >= 0) { - snprintf(str, sizeof(str), "%" PRId64, cmd->uc_def); - obj = CSTR_TO_OBJ(str); // -count=N + obj = STRING_OBJ(arena_printf(arena, "%" PRId64, cmd->uc_def)); // -count=N } else { - obj = CSTR_TO_OBJ("0"); // -count + obj = CSTR_AS_OBJ("0"); // -count } } - PUT(d, "count", obj); + PUT_C(d, "count", obj); obj = NIL; if (cmd->uc_argt & EX_RANGE) { if (cmd->uc_argt & EX_DFLALL) { - obj = CSTR_TO_OBJ("%"); // -range=% + obj = STATIC_CSTR_AS_OBJ("%"); // -range=% } else if (cmd->uc_def >= 0) { - snprintf(str, sizeof(str), "%" PRId64, cmd->uc_def); - obj = CSTR_TO_OBJ(str); // -range=N + obj = STRING_OBJ(arena_printf(arena, "%" PRId64, cmd->uc_def)); // -range=N } else { - obj = CSTR_TO_OBJ("."); // -range + obj = STATIC_CSTR_AS_OBJ("."); // -range } } - PUT(d, "range", obj); + PUT_C(d, "range", obj); obj = NIL; for (int j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { if (addr_type_complete[j].expand != ADDR_LINES && addr_type_complete[j].expand == cmd->uc_addr_type) { - obj = CSTR_TO_OBJ(addr_type_complete[j].name); + obj = CSTR_AS_OBJ(addr_type_complete[j].name); break; } } - PUT(d, "addr", obj); + PUT_C(d, "addr", obj); - PUT(rv, cmd->uc_name, DICTIONARY_OBJ(d)); + PUT_C(rv, cmd->uc_name, DICTIONARY_OBJ(d)); } return rv; } diff --git a/src/nvim/usercmd.h b/src/nvim/usercmd.h index bcabf9460b..84a8f61719 100644 --- a/src/nvim/usercmd.h +++ b/src/nvim/usercmd.h @@ -3,13 +3,14 @@ #include <stddef.h> // IWYU pragma: keep #include <stdint.h> +#include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/garray_defs.h" -#include "nvim/types_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" -typedef struct ucmd { +typedef struct { char *uc_name; ///< The command name uint32_t uc_argt; ///< The argument type char *uc_rep; ///< The command's replacement string diff --git a/src/nvim/version.c b/src/nvim/version.c index cb9088afae..038c9701bf 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -13,18 +13,18 @@ #include "auto/versiondef.h" // version info generated by the build system #include "auto/versiondef_git.h" -#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" #include "nvim/ex_cmds_defs.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/lua/executor.h" #include "nvim/mbyte.h" #include "nvim/memory.h" @@ -512,7 +512,7 @@ static const int included_patches[] = { 1971, 1970, // 1969, - // 1968, + 1968, 1967, 1966, 1965, @@ -897,7 +897,7 @@ static const int included_patches[] = { // 1586, 1585, // 1584, - // 1583, + 1583, 1582, 1581, // 1580, @@ -2550,24 +2550,6 @@ bool has_vim_patch(int n) return false; } -Dictionary version_dict(void) -{ - Dictionary d = ARRAY_DICT_INIT; - PUT(d, "major", INTEGER_OBJ(NVIM_VERSION_MAJOR)); - PUT(d, "minor", INTEGER_OBJ(NVIM_VERSION_MINOR)); - PUT(d, "patch", INTEGER_OBJ(NVIM_VERSION_PATCH)); -#ifndef NVIM_VERSION_BUILD - PUT(d, "build", NIL); -#else - PUT(d, "build", CSTR_AS_OBJ(NVIM_VERSION_BUILD)); -#endif - PUT(d, "prerelease", BOOLEAN_OBJ(NVIM_VERSION_PRERELEASE[0] != '\0')); - PUT(d, "api_level", INTEGER_OBJ(NVIM_API_LEVEL)); - PUT(d, "api_compatible", INTEGER_OBJ(NVIM_API_LEVEL_COMPAT)); - PUT(d, "api_prerelease", BOOLEAN_OBJ(NVIM_API_PRERELEASE)); - return d; -} - void ex_version(exarg_T *eap) { // Ignore a ":version 9.99" command. @@ -2582,7 +2564,7 @@ void ex_version(exarg_T *eap) /// When "wrap" is true wrap the string in []. /// @param s /// @param wrap -static void version_msg_wrap(char *s, int wrap) +static void version_msg_wrap(char *s, bool wrap) { int len = vim_strsize(s) + (wrap ? 2 : 0); @@ -2649,7 +2631,7 @@ void list_in_columns(char **items, int size, int current) for (int i = 0; !got_int && i < nrow * ncol; i++) { int idx = (i / ncol) + (i % ncol) * nrow; if (idx < item_count) { - int last_col = (i + 1) % ncol == 0; + bool last_col = (i + 1) % ncol == 0; if (idx == current) { msg_putchar('['); } @@ -2681,9 +2663,9 @@ void list_in_columns(char **items, int size, int current) void list_lua_version(void) { - char *code = "return ((jit and jit.version) and jit.version or _VERSION)"; Error err = ERROR_INIT; - Object ret = nlua_exec(cstr_as_string(code), (Array)ARRAY_DICT_INIT, &err); + Object ret = NLUA_EXEC_STATIC("return ((jit and jit.version) and jit.version or _VERSION)", + (Array)ARRAY_DICT_INIT, kRetObject, NULL, &err); assert(!ERROR_SET(&err)); assert(ret.type == kObjectTypeString); msg(ret.data.string.data, 0); @@ -2708,7 +2690,6 @@ void list_version(void) version_msg("\"\n"); #endif -#ifdef HAVE_PATHDEF if (*default_vim_dir != NUL) { version_msg(_(" fall-back for $VIM: \"")); version_msg(default_vim_dir); @@ -2720,7 +2701,6 @@ void list_version(void) version_msg(default_vimruntime_dir); version_msg("\"\n"); } -#endif } version_msg(p_verbose > 0 diff --git a/src/nvim/version.h b/src/nvim/version.h index 94219aaddc..cc355f2df5 100644 --- a/src/nvim/version.h +++ b/src/nvim/version.h @@ -1,5 +1,6 @@ #pragma once +#include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" diff --git a/src/nvim/vim_defs.h b/src/nvim/vim_defs.h index faf79b1c79..f6b348e009 100644 --- a/src/nvim/vim_defs.h +++ b/src/nvim/vim_defs.h @@ -6,20 +6,10 @@ #define SYS_OPTWIN_FILE "$VIMRUNTIME/optwin.vim" #define RUNTIME_DIRNAME "runtime" -#include "auto/config.h" - -// Check if configure correctly managed to find sizeof(int). If this failed, -// it becomes zero. This is likely a problem of not being able to run the -// test program. Other items from configure may also be wrong then! -#if (SIZEOF_INT == 0) -# error Configure did not run properly. -#endif - -// bring lots of system header files -#include "nvim/os/os_defs.h" // IWYU pragma: keep - -/// length of a buffer to store a number in ASCII (64 bits binary + NUL) -enum { NUMBUFLEN = 65, }; +enum { + /// length of a buffer to store a number in ASCII (64 bits binary + NUL) + NUMBUFLEN = 65, +}; #define MAX_TYPENR 65535 @@ -32,6 +22,38 @@ typedef enum { BACKWARD_FILE = -3, } Direction; +/// Used to track the status of external functions. +/// Currently only used for iconv(). +typedef enum { + kUnknown, + kWorking, + kBroken, +} WorkingStatus; + +/// The scope of a working-directory command like `:cd`. +/// +/// Scopes are enumerated from lowest to highest. When adding a scope make sure +/// to update all functions using scopes as well, such as the implementation of +/// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes +/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead. +typedef enum { + kCdScopeInvalid = -1, + kCdScopeWindow, ///< Affects one window. + kCdScopeTabpage, ///< Affects one tab page. + kCdScopeGlobal, ///< Affects the entire Nvim instance. +} CdScope; + +#define MIN_CD_SCOPE kCdScopeWindow +#define MAX_CD_SCOPE kCdScopeGlobal + +/// What caused the current directory to change. +typedef enum { + kCdCauseOther = -1, + kCdCauseManual, ///< Using `:cd`, `:tcd`, `:lcd` or `chdir()`. + kCdCauseWindow, ///< Switching to another window. + kCdCauseAuto, ///< On 'autochdir'. +} CdCause; + // return values for functions #if !(defined(OK) && (OK == 1)) // OK already defined to 1 in MacOS X curses, skip this diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 8b637fbb9b..3403fb7926 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -59,8 +59,7 @@ #include "nvim/assert_defs.h" #include "nvim/charset.h" #include "nvim/eval.h" -#include "nvim/func_attr.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h index 94287ea4e1..ba54c4de07 100644 --- a/src/nvim/viml/parser/expressions.h +++ b/src/nvim/viml/parser/expressions.h @@ -6,9 +6,7 @@ #include "nvim/eval/typval_defs.h" #include "nvim/types_defs.h" -#include "nvim/viml/parser/parser.h" - -struct expr_ast_node; +#include "nvim/viml/parser/parser_defs.h" // Defines whether to ignore case: // == kCCStrategyUseOption diff --git a/src/nvim/viml/parser/parser.c b/src/nvim/viml/parser/parser.c index b854aedca6..bdade3fae2 100644 --- a/src/nvim/viml/parser/parser.c +++ b/src/nvim/viml/parser/parser.c @@ -1,3 +1,6 @@ +#include "nvim/func_attr.h" +#include "nvim/mbyte.h" +#include "nvim/memory.h" #include "nvim/viml/parser/parser.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -5,8 +8,68 @@ #endif void parser_simple_get_line(void *cookie, ParserLine *ret_pline) + FUNC_ATTR_NONNULL_ALL { ParserLine **plines_p = (ParserLine **)cookie; *ret_pline = **plines_p; (*plines_p)++; } + +/// Get currently parsed line, shifted to pstate->pos.col +/// +/// @param pstate Parser state to operate on. +/// +/// @return True if there is a line, false in case of EOF. +bool viml_parser_get_remaining_line(ParserState *const pstate, ParserLine *const ret_pline) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + const size_t num_lines = kv_size(pstate->reader.lines); + if (pstate->pos.line == num_lines) { + viml_preader_get_line(&pstate->reader, ret_pline); + } else { + *ret_pline = kv_last(pstate->reader.lines); + } + assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1); + if (ret_pline->data != NULL) { + ret_pline->data += pstate->pos.col; + ret_pline->size -= pstate->pos.col; + } + return ret_pline->data != NULL; +} + +/// Get one line from ParserInputReader +static void viml_preader_get_line(ParserInputReader *const preader, ParserLine *const ret_pline) + FUNC_ATTR_NONNULL_ALL +{ + ParserLine pline; + preader->get_line(preader->cookie, &pline); + if (preader->conv.vc_type != CONV_NONE && pline.size) { + ParserLine cpline = { + .allocated = true, + .size = pline.size, + }; + cpline.data = string_convert(&preader->conv, (char *)pline.data, &cpline.size); + if (pline.allocated) { + xfree((void *)pline.data); + } + pline = cpline; + } + kvi_push(preader->lines, pline); + *ret_pline = pline; +} + +/// Free all memory allocated by the parser on heap +/// +/// @param pstate Parser state to free. +void viml_parser_destroy(ParserState *const pstate) + FUNC_ATTR_NONNULL_ALL +{ + for (size_t i = 0; i < kv_size(pstate->reader.lines); i++) { + ParserLine pline = kv_A(pstate->reader.lines, i); + if (pline.allocated) { + xfree((void *)pline.data); + } + } + kvi_destroy(pstate->reader.lines); + kvi_destroy(pstate->stack); +} diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h index cd5a493643..31decdc798 100644 --- a/src/nvim/viml/parser/parser.h +++ b/src/nvim/viml/parser/parser.h @@ -6,80 +6,8 @@ #include "klib/kvec.h" #include "nvim/func_attr.h" -#include "nvim/mbyte.h" #include "nvim/mbyte_defs.h" -#include "nvim/memory.h" - -/// One parsed line -typedef struct { - const char *data; ///< Parsed line pointer - size_t size; ///< Parsed line size - bool allocated; ///< True if line may be freed. -} ParserLine; - -/// Line getter type for parser -/// -/// Line getter must return {NULL, 0} for EOF. -typedef void (*ParserLineGetter)(void *cookie, ParserLine *ret_pline); - -/// Parser position in the input -typedef struct { - size_t line; ///< Line index in ParserInputReader.lines. - size_t col; ///< Byte index in the line. -} ParserPosition; - -/// Parser state item. -typedef struct { - enum { - kPTopStateParsingCommand = 0, - kPTopStateParsingExpression, - } type; - union { - struct { - enum { - kExprUnknown = 0, - } type; - } expr; - } data; -} ParserStateItem; - -/// Structure defining input reader -typedef struct { - /// Function used to get next line. - ParserLineGetter get_line; - /// Data for get_line function. - void *cookie; - /// All lines obtained by get_line. - kvec_withinit_t(ParserLine, 4) lines; - /// Conversion, for :scriptencoding. - vimconv_T conv; -} ParserInputReader; - -/// Highlighted region definition -/// -/// Note: one chunk may highlight only one line. -typedef struct { - ParserPosition start; ///< Start of the highlight: line and column. - size_t end_col; ///< End column, points to the start of the next character. - const char *group; ///< Highlight group. -} ParserHighlightChunk; - -/// Highlighting defined by a parser -typedef kvec_withinit_t(ParserHighlightChunk, 16) ParserHighlight; - -/// Structure defining parser state -typedef struct { - /// Line reader. - ParserInputReader reader; - /// Position up to which input was parsed. - ParserPosition pos; - /// Parser state stack. - kvec_withinit_t(ParserStateItem, 16) stack; - /// Highlighting support. - ParserHighlight *colors; - /// True if line continuation can be used. - bool can_continuate; -} ParserState; +#include "nvim/viml/parser/parser_defs.h" // IWYU pragma: keep static inline void viml_parser_init(ParserState *ret_pstate, ParserLineGetter get_line, void *cookie, ParserHighlight *colors) @@ -109,73 +37,6 @@ static inline void viml_parser_init(ParserState *const ret_pstate, const ParserL kvi_init(ret_pstate->stack); } -static inline void viml_parser_destroy(ParserState *pstate) - REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; - -/// Free all memory allocated by the parser on heap -/// -/// @param pstate Parser state to free. -static inline void viml_parser_destroy(ParserState *const pstate) -{ - for (size_t i = 0; i < kv_size(pstate->reader.lines); i++) { - ParserLine pline = kv_A(pstate->reader.lines, i); - if (pline.allocated) { - xfree((void *)pline.data); - } - } - kvi_destroy(pstate->reader.lines); - kvi_destroy(pstate->stack); -} - -static inline void viml_preader_get_line(ParserInputReader *preader, ParserLine *ret_pline) - REAL_FATTR_NONNULL_ALL; - -/// Get one line from ParserInputReader -static inline void viml_preader_get_line(ParserInputReader *const preader, - ParserLine *const ret_pline) -{ - ParserLine pline; - preader->get_line(preader->cookie, &pline); - if (preader->conv.vc_type != CONV_NONE && pline.size) { - ParserLine cpline = { - .allocated = true, - .size = pline.size, - }; - cpline.data = string_convert(&preader->conv, (char *)pline.data, &cpline.size); - if (pline.allocated) { - xfree((void *)pline.data); - } - pline = cpline; - } - kvi_push(preader->lines, pline); - *ret_pline = pline; -} - -static inline bool viml_parser_get_remaining_line(ParserState *pstate, ParserLine *ret_pline) - REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; - -/// Get currently parsed line, shifted to pstate->pos.col -/// -/// @param pstate Parser state to operate on. -/// -/// @return True if there is a line, false in case of EOF. -static inline bool viml_parser_get_remaining_line(ParserState *const pstate, - ParserLine *const ret_pline) -{ - const size_t num_lines = kv_size(pstate->reader.lines); - if (pstate->pos.line == num_lines) { - viml_preader_get_line(&pstate->reader, ret_pline); - } else { - *ret_pline = kv_last(pstate->reader.lines); - } - assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1); - if (ret_pline->data != NULL) { - ret_pline->data += pstate->pos.col; - ret_pline->size -= pstate->pos.col; - } - return ret_pline->data != NULL; -} - static inline void viml_parser_advance(ParserState *pstate, size_t len) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; diff --git a/src/nvim/viml/parser/parser_defs.h b/src/nvim/viml/parser/parser_defs.h new file mode 100644 index 0000000000..59fab23054 --- /dev/null +++ b/src/nvim/viml/parser/parser_defs.h @@ -0,0 +1,79 @@ +#pragma once + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> + +#include "klib/kvec.h" +#include "nvim/mbyte_defs.h" + +/// One parsed line +typedef struct { + const char *data; ///< Parsed line pointer + size_t size; ///< Parsed line size + bool allocated; ///< True if line may be freed. +} ParserLine; + +/// Line getter type for parser +/// +/// Line getter must return {NULL, 0} for EOF. +typedef void (*ParserLineGetter)(void *cookie, ParserLine *ret_pline); + +/// Parser position in the input +typedef struct { + size_t line; ///< Line index in ParserInputReader.lines. + size_t col; ///< Byte index in the line. +} ParserPosition; + +/// Parser state item. +typedef struct { + enum { + kPTopStateParsingCommand = 0, + kPTopStateParsingExpression, + } type; + union { + struct { + enum { + kExprUnknown = 0, + } type; + } expr; + } data; +} ParserStateItem; + +/// Structure defining input reader +typedef struct { + /// Function used to get next line. + ParserLineGetter get_line; + /// Data for get_line function. + void *cookie; + /// All lines obtained by get_line. + kvec_withinit_t(ParserLine, 4) lines; + /// Conversion, for :scriptencoding. + vimconv_T conv; +} ParserInputReader; + +/// Highlighted region definition +/// +/// Note: one chunk may highlight only one line. +typedef struct { + ParserPosition start; ///< Start of the highlight: line and column. + size_t end_col; ///< End column, points to the start of the next character. + const char *group; ///< Highlight group. +} ParserHighlightChunk; + +/// Highlighting defined by a parser +typedef kvec_withinit_t(ParserHighlightChunk, 16) ParserHighlight; + +/// Structure defining parser state +typedef struct { + /// Line reader. + ParserInputReader reader; + /// Position up to which input was parsed. + ParserPosition pos; + /// Parser state stack. + kvec_withinit_t(ParserStateItem, 16) stack; + /// Highlighting support. + ParserHighlight *colors; + /// True if line continuation can be used. + bool can_continuate; +} ParserState; diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua new file mode 100644 index 0000000000..37cb70c725 --- /dev/null +++ b/src/nvim/vvars.lua @@ -0,0 +1,885 @@ +local M = {} + +M.vars = { + argv = { + type = 'string[]', + desc = [=[ + The command line arguments Vim was invoked with. This is a + list of strings. The first item is the Vim command. + See |v:progpath| for the command with full path. + ]=], + }, + char = { + desc = [=[ + Argument for evaluating 'formatexpr' and used for the typed + character when using <expr> in an abbreviation |:map-<expr>|. + It is also used by the |InsertCharPre| and |InsertEnter| events. + ]=], + }, + charconvert_from = { + type = 'string', + desc = [=[ + The name of the character encoding of a file to be converted. + Only valid while evaluating the 'charconvert' option. + ]=], + }, + charconvert_to = { + type = 'string', + desc = [=[ + The name of the character encoding of a file after conversion. + Only valid while evaluating the 'charconvert' option. + ]=], + }, + cmdarg = { + type = 'string', + desc = [=[ + The extra arguments ("++p", "++enc=", "++ff=") given to a file + read/write command. This is set before an autocommand event + for a file read/write command is triggered. There is a + leading space to make it possible to append this variable + directly after the read/write command. Note: "+cmd" isn't + included here, because it will be executed anyway. + ]=], + }, + collate = { + type = 'string', + desc = [=[ + The current locale setting for collation order of the runtime + environment. This allows Vim scripts to be aware of the + current locale encoding. Technical: it's the value of + LC_COLLATE. When not using a locale the value is "C". + This variable can not be set directly, use the |:language| + command. + See |multi-lang|. + ]=], + }, + cmdbang = { + type = 'integer', + desc = [=[ + Set like v:cmdarg for a file read/write command. When a "!" + was used the value is 1, otherwise it is 0. Note that this + can only be used in autocommands. For user commands |<bang>| + can be used. + ]=], + }, + completed_item = { + desc = [=[ + Dictionary containing the most recent |complete-items| after + |CompleteDone|. Empty if the completion failed, or after + leaving and re-entering insert mode. + Note: Plugins can modify the value to emulate the builtin + |CompleteDone| event behavior. + ]=], + }, + count = { + type = 'integer', + desc = [=[ + The count given for the last Normal mode command. Can be used + to get the count before a mapping. Read-only. Example: >vim + :map _x :<C-U>echo "the count is " .. v:count<CR> + < + Note: The <C-U> is required to remove the line range that you + get when typing ':' after a count. + When there are two counts, as in "3d2w", they are multiplied, + just like what happens in the command, "d6w" for the example. + Also used for evaluating the 'formatexpr' option. + ]=], + }, + count1 = { + type = 'integer', + desc = [=[ + Just like "v:count", but defaults to one when no count is + used. + ]=], + }, + ctype = { + desc = [=[ + The current locale setting for characters of the runtime + environment. This allows Vim scripts to be aware of the + current locale encoding. Technical: it's the value of + LC_CTYPE. When not using a locale the value is "C". + This variable can not be set directly, use the |:language| + command. + See |multi-lang|. + ]=], + }, + dying = { + type = 'integer', + desc = [=[ + Normally zero. When a deadly signal is caught it's set to + one. When multiple signals are caught the number increases. + Can be used in an autocommand to check if Vim didn't + terminate normally. + Example: >vim + :au VimLeave * if v:dying | echo "\nAAAAaaaarrrggghhhh!!!\n" | endif + < + Note: if another deadly signal is caught when v:dying is one, + VimLeave autocommands will not be executed. + ]=], + }, + exiting = { + desc = [=[ + Exit code, or |v:null| before invoking the |VimLeavePre| + and |VimLeave| autocmds. See |:q|, |:x| and |:cquit|. + Example: >vim + :au VimLeave * echo "Exit value is " .. v:exiting + < + ]=], + }, + echospace = { + type = 'integer', + desc = [=[ + Number of screen cells that can be used for an `:echo` message + in the last screen line before causing the |hit-enter-prompt|. + Depends on 'showcmd', 'ruler' and 'columns'. You need to + check 'cmdheight' for whether there are full-width lines + available above the last line. + ]=], + }, + errmsg = { + type = 'string', + desc = [=[ + Last given error message. + Modifiable (can be set). + Example: >vim + let v:errmsg = "" + silent! next + if v:errmsg != "" + " ... handle error + < + ]=], + }, + errors = { + type = 'string[]', + tags = { 'assert-return' }, + desc = [=[ + Errors found by assert functions, such as |assert_true()|. + This is a list of strings. + The assert functions append an item when an assert fails. + The return value indicates this: a one is returned if an item + was added to v:errors, otherwise zero is returned. + To remove old results make it empty: >vim + let v:errors = [] + < + If v:errors is set to anything but a list it is made an empty + list by the assert function. + ]=], + }, + event = { + desc = [=[ + Dictionary of event data for the current |autocommand|. Valid + only during the event lifetime; storing or passing v:event is + invalid! Copy it instead: >vim + au TextYankPost * let g:foo = deepcopy(v:event) + < + Keys vary by event; see the documentation for the specific + event, e.g. |DirChanged| or |TextYankPost|. + KEY DESCRIPTION ~ + abort Whether the event triggered during + an aborting condition (e.g. |c_Esc| or + |c_CTRL-C| for |CmdlineLeave|). + chan |channel-id| + cmdlevel Level of cmdline. + cmdtype Type of cmdline, |cmdline-char|. + cwd Current working directory. + inclusive Motion is |inclusive|, else exclusive. + scope Event-specific scope name. + operator Current |operator|. Also set for Ex + commands (unlike |v:operator|). For + example if |TextYankPost| is triggered + by the |:yank| Ex command then + `v:event.operator` is "y". + regcontents Text stored in the register as a + |readfile()|-style list of lines. + regname Requested register (e.g "x" for "xyy) + or the empty string for an unnamed + operation. + regtype Type of register as returned by + |getregtype()|. + visual Selection is visual (as opposed to, + e.g., via motion). + completed_item Current selected complete item on + |CompleteChanged|, Is `{}` when no complete + item selected. + height Height of popup menu on |CompleteChanged| + width Width of popup menu on |CompleteChanged| + row Row count of popup menu on |CompleteChanged|, + relative to screen. + col Col count of popup menu on |CompleteChanged|, + relative to screen. + size Total number of completion items on + |CompleteChanged|. + scrollbar Is |v:true| if popup menu have scrollbar, or + |v:false| if not. + changed_window Is |v:true| if the event fired while + changing window (or tab) on |DirChanged|. + status Job status or exit code, -1 means "unknown". |TermClose| + ]=], + }, + exception = { + type = 'string', + desc = [=[ + The value of the exception most recently caught and not + finished. See also |v:throwpoint| and |throw-variables|. + Example: >vim + try + throw "oops" + catch /.*/ + echo "caught " .. v:exception + endtry + < + Output: "caught oops". + ]=], + }, + ['false'] = { + type = 'boolean', + desc = [=[ + Special value used to put "false" in JSON and msgpack. See + |json_encode()|. This value is converted to "v:false" when used + as a String (e.g. in |expr5| with string concatenation + operator) and to zero when used as a Number (e.g. in |expr5| + or |expr7| when used with numeric operators). Read-only. + ]=], + }, + fcs_reason = { + type = 'string', + desc = [=[ + The reason why the |FileChangedShell| event was triggered. + Can be used in an autocommand to decide what to do and/or what + to set v:fcs_choice to. Possible values: + deleted file no longer exists + conflict file contents, mode or timestamp was + changed and buffer is modified + changed file contents has changed + mode mode of file changed + time only file timestamp changed + ]=], + }, + fcs_choice = { + type = 'string', + desc = [=[ + What should happen after a |FileChangedShell| event was + triggered. Can be used in an autocommand to tell Vim what to + do with the affected buffer: + reload Reload the buffer (does not work if + the file was deleted). + edit Reload the buffer and detect the + values for options such as + 'fileformat', 'fileencoding', 'binary' + (does not work if the file was + deleted). + ask Ask the user what to do, as if there + was no autocommand. Except that when + only the timestamp changed nothing + will happen. + <empty> Nothing, the autocommand should do + everything that needs to be done. + The default is empty. If another (invalid) value is used then + Vim behaves like it is empty, there is no warning message. + ]=], + }, + fname = { + type = 'string', + desc = [=[ + When evaluating 'includeexpr': the file name that was + detected. Empty otherwise. + ]=], + }, + fname_in = { + type = 'string', + desc = [=[ + The name of the input file. Valid while evaluating: + option used for ~ + 'charconvert' file to be converted + 'diffexpr' original file + 'patchexpr' original file + And set to the swap file name for |SwapExists|. + ]=], + }, + fname_out = { + type = 'string', + desc = [=[ + The name of the output file. Only valid while + evaluating: + option used for ~ + 'charconvert' resulting converted file [1] + 'diffexpr' output of diff + 'patchexpr' resulting patched file + [1] When doing conversion for a write command (e.g., ":w + file") it will be equal to v:fname_in. When doing conversion + for a read command (e.g., ":e file") it will be a temporary + file and different from v:fname_in. + ]=], + }, + fname_new = { + type = 'string', + desc = [=[ + The name of the new version of the file. Only valid while + evaluating 'diffexpr'. + ]=], + }, + fname_diff = { + type = 'string', + desc = [=[ + The name of the diff (patch) file. Only valid while + evaluating 'patchexpr'. + ]=], + }, + folddashes = { + type = 'string', + desc = [=[ + Used for 'foldtext': dashes representing foldlevel of a closed + fold. + Read-only in the |sandbox|. |fold-foldtext| + ]=], + }, + foldlevel = { + type = 'integer', + desc = [=[ + Used for 'foldtext': foldlevel of closed fold. + Read-only in the |sandbox|. |fold-foldtext| + ]=], + }, + foldend = { + type = 'integer', + desc = [=[ + Used for 'foldtext': last line of closed fold. + Read-only in the |sandbox|. |fold-foldtext| + ]=], + }, + foldstart = { + type = 'integer', + desc = [=[ + Used for 'foldtext': first line of closed fold. + Read-only in the |sandbox|. |fold-foldtext| + ]=], + }, + hlsearch = { + type = 'integer', + desc = [=[ + Variable that indicates whether search highlighting is on. + Setting it makes sense only if 'hlsearch' is enabled. Setting + this variable to zero acts like the |:nohlsearch| command, + setting it to one acts like >vim + let &hlsearch = &hlsearch + < + Note that the value is restored when returning from a + function. |function-search-undo|. + ]=], + }, + insertmode = { + type = 'string', + desc = [=[ + Used for the |InsertEnter| and |InsertChange| autocommand + events. Values: + i Insert mode + r Replace mode + v Virtual Replace mode + ]=], + }, + key = { + type = 'string', + desc = [=[ + Key of the current item of a |Dictionary|. Only valid while + evaluating the expression used with |map()| and |filter()|. + Read-only. + ]=], + }, + lang = { + type = 'string', + desc = [=[ + The current locale setting for messages of the runtime + environment. This allows Vim scripts to be aware of the + current language. Technical: it's the value of LC_MESSAGES. + The value is system dependent. + This variable can not be set directly, use the |:language| + command. + It can be different from |v:ctype| when messages are desired + in a different language than what is used for character + encoding. See |multi-lang|. + ]=], + }, + lc_time = { + type = 'string', + desc = [=[ + The current locale setting for time messages of the runtime + environment. This allows Vim scripts to be aware of the + current language. Technical: it's the value of LC_TIME. + This variable can not be set directly, use the |:language| + command. See |multi-lang|. + ]=], + }, + lnum = { + type = 'integer', + desc = [=[ + Line number for the 'foldexpr' |fold-expr|, 'formatexpr', + 'indentexpr' and 'statuscolumn' expressions, tab page number + for 'guitablabel' and 'guitabtooltip'. Only valid while one of + these expressions is being evaluated. Read-only when in the + |sandbox|. + ]=], + }, + lua = { + desc = [=[ + Prefix for calling Lua functions from expressions. + See |v:lua-call| for more information. + ]=], + }, + maxcol = { + type = 'integer', + desc = [=[ + Maximum line length. Depending on where it is used it can be + screen columns, characters or bytes. The value currently is + 2147483647 on all systems. + ]=], + }, + mouse_win = { + type = 'integer', + desc = [=[ + Window number for a mouse click obtained with |getchar()|. + First window has number 1, like with |winnr()|. The value is + zero when there was no mouse button click. + ]=], + }, + mouse_winid = { + type = 'integer', + desc = [=[ + |window-ID| for a mouse click obtained with |getchar()|. + The value is zero when there was no mouse button click. + ]=], + }, + mouse_lnum = { + type = 'integer', + desc = [=[ + Line number for a mouse click obtained with |getchar()|. + This is the text line number, not the screen line number. The + value is zero when there was no mouse button click. + ]=], + }, + mouse_col = { + type = 'integer', + desc = [=[ + Column number for a mouse click obtained with |getchar()|. + This is the screen column number, like with |virtcol()|. The + value is zero when there was no mouse button click. + ]=], + }, + msgpack_types = { + desc = [=[ + Dictionary containing msgpack types used by |msgpackparse()| + and |msgpackdump()|. All types inside dictionary are fixed + (not editable) empty lists. To check whether some list is one + of msgpack types, use |is| operator. + ]=], + }, + null = { + type = 'vim.NIL', + desc = [=[ + Special value used to put "null" in JSON and NIL in msgpack. + See |json_encode()|. This value is converted to "v:null" when + used as a String (e.g. in |expr5| with string concatenation + operator) and to zero when used as a Number (e.g. in |expr5| + or |expr7| when used with numeric operators). Read-only. + In some places `v:null` can be used for a List, Dict, etc. + that is not set. That is slightly different than an empty + List, Dict, etc. + ]=], + }, + numbermax = { + type = 'integer', + desc = 'Maximum value of a number.', + }, + numbermin = { + type = 'integer', + desc = 'Minimum value of a number (negative).', + }, + numbersize = { + type = 'integer', + desc = [=[ + Number of bits in a Number. This is normally 64, but on some + systems it may be 32. + ]=], + }, + oldfiles = { + type = 'string[]', + desc = [=[ + List of file names that is loaded from the |shada| file on + startup. These are the files that Vim remembers marks for. + The length of the List is limited by the ' argument of the + 'shada' option (default is 100). + When the |shada| file is not used the List is empty. + Also see |:oldfiles| and |c_#<|. + The List can be modified, but this has no effect on what is + stored in the |shada| file later. If you use values other + than String this will cause trouble. + ]=], + }, + option_new = { + desc = [=[ + New value of the option. Valid while executing an |OptionSet| + autocommand. + ]=], + }, + option_old = { + desc = [=[ + Old value of the option. Valid while executing an |OptionSet| + autocommand. Depending on the command used for setting and the + kind of option this is either the local old value or the + global old value. + ]=], + }, + option_oldlocal = { + desc = [=[ + Old local value of the option. Valid while executing an + |OptionSet| autocommand. + ]=], + }, + option_oldglobal = { + desc = [=[ + Old global value of the option. Valid while executing an + |OptionSet| autocommand. + ]=], + }, + option_type = { + type = 'string', + desc = [=[ + Scope of the set command. Valid while executing an + |OptionSet| autocommand. Can be either "global" or "local" + ]=], + }, + option_command = { + type = 'string', + desc = [=[ + Command used to set the option. Valid while executing an + |OptionSet| autocommand. + value option was set via ~ + "setlocal" |:setlocal| or `:let l:xxx` + "setglobal" |:setglobal| or `:let g:xxx` + "set" |:set| or |:let| + "modeline" |modeline| + ]=], + }, + operator = { + type = 'string', + desc = [=[ + The last operator given in Normal mode. This is a single + character except for commands starting with <g> or <z>, + in which case it is two characters. Best used alongside + |v:prevcount| and |v:register|. Useful if you want to cancel + Operator-pending mode and then use the operator, e.g.: >vim + :omap O <Esc>:call MyMotion(v:operator)<CR> + < + The value remains set until another operator is entered, thus + don't expect it to be empty. + v:operator is not set for |:delete|, |:yank| or other Ex + commands. + Read-only. + ]=], + }, + prevcount = { + type = 'integer', + desc = [=[ + The count given for the last but one Normal mode command. + This is the v:count value of the previous command. Useful if + you want to cancel Visual or Operator-pending mode and then + use the count, e.g.: >vim + :vmap % <Esc>:call MyFilter(v:prevcount)<CR> + < + Read-only. + ]=], + }, + profiling = { + type = 'integer', + desc = [=[ + Normally zero. Set to one after using ":profile start". + See |profiling|. + ]=], + }, + progname = { + type = 'string', + desc = [=[ + The name by which Nvim was invoked (with path removed). + Read-only. + ]=], + }, + progpath = { + type = 'string', + desc = [=[ + Absolute path to the current running Nvim. + Read-only. + ]=], + }, + register = { + type = 'string', + desc = [=[ + The name of the register in effect for the current normal mode + command (regardless of whether that command actually used a + register). Or for the currently executing normal mode mapping + (use this in custom commands that take a register). + If none is supplied it is the default register '"', unless + 'clipboard' contains "unnamed" or "unnamedplus", then it is + "*" or '+'. + Also see |getreg()| and |setreg()| + ]=], + }, + relnum = { + type = 'integer', + desc = [=[ + Relative line number for the 'statuscolumn' expression. + Read-only. + ]=], + }, + scrollstart = { + desc = [=[ + String describing the script or function that caused the + screen to scroll up. It's only set when it is empty, thus the + first reason is remembered. It is set to "Unknown" for a + typed command. + This can be used to find out why your script causes the + hit-enter prompt. + ]=], + }, + servername = { + type = 'string', + desc = [=[ + Primary listen-address of Nvim, the first item returned by + |serverlist()|. Usually this is the named pipe created by Nvim + at |startup| or given by |--listen| (or the deprecated + |$NVIM_LISTEN_ADDRESS| env var). + + See also |serverstart()| |serverstop()|. + Read-only. + + *$NVIM* + $NVIM is set by |terminal| and |jobstart()|, and is thus + a hint that the current environment is a subprocess of Nvim. + Example: >vim + if $NVIM + echo nvim_get_chan_info(v:parent) + endif + < + + Note the contents of $NVIM may change in the future. + ]=], + }, + searchforward = { + type = 'integer', + desc = [=[ + Search direction: 1 after a forward search, 0 after a + backward search. It is reset to forward when directly setting + the last search pattern, see |quote/|. + Note that the value is restored when returning from a + function. |function-search-undo|. + Read-write. + ]=], + }, + shell_error = { + type = 'integer', + desc = [=[ + Result of the last shell command. When non-zero, the last + shell command had an error. When zero, there was no problem. + This only works when the shell returns the error code to Vim. + The value -1 is often used when the command could not be + executed. Read-only. + Example: >vim + !mv foo bar + if v:shell_error + echo 'could not rename "foo" to "bar"!' + endif + < + ]=], + }, + statusmsg = { + type = 'string', + desc = [=[ + Last given status message. + Modifiable (can be set). + ]=], + }, + stderr = { + type = 'integer', + desc = [=[ + |channel-id| corresponding to stderr. The value is always 2; + use this variable to make your code more descriptive. + Unlike stdin and stdout (see |stdioopen()|), stderr is always + open for writing. Example: >vim + :call chansend(v:stderr, "error: toaster empty\n") + < + ]=], + }, + swapname = { + type = 'string', + desc = [=[ + Name of the swapfile found. + Only valid during |SwapExists| event. + Read-only. + ]=], + }, + swapchoice = { + type = 'string', + desc = [=[ + |SwapExists| autocommands can set this to the selected choice + for handling an existing swapfile: + 'o' Open read-only + 'e' Edit anyway + 'r' Recover + 'd' Delete swapfile + 'q' Quit + 'a' Abort + The value should be a single-character string. An empty value + results in the user being asked, as would happen when there is + no SwapExists autocommand. The default is empty. + ]=], + }, + swapcommand = { + type = 'string', + desc = [=[ + Normal mode command to be executed after a file has been + opened. Can be used for a |SwapExists| autocommand to have + another Vim open the file and jump to the right place. For + example, when jumping to a tag the value is ":tag tagname\r". + For ":edit +cmd file" the value is ":cmd\r". + ]=], + }, + t_blob = { + type = 'integer', + tags = { 'v:t_TYPE' }, + desc = 'Value of |Blob| type. Read-only. See: |type()|', + }, + t_bool = { + type = 'integer', + desc = 'Value of |Boolean| type. Read-only. See: |type()|', + }, + t_dict = { + type = 'integer', + desc = 'Value of |Dictionary| type. Read-only. See: |type()|', + }, + t_float = { + type = 'integer', + desc = 'Value of |Float| type. Read-only. See: |type()|', + }, + t_func = { + type = 'integer', + desc = 'Value of |Funcref| type. Read-only. See: |type()|', + }, + t_list = { + type = 'integer', + desc = 'Value of |List| type. Read-only. See: |type()|', + }, + t_number = { + type = 'integer', + desc = 'Value of |Number| type. Read-only. See: |type()|', + }, + t_string = { + type = 'integer', + desc = 'Value of |String| type. Read-only. See: |type()|', + }, + termresponse = { + type = 'string', + desc = [=[ + The value of the most recent OSC or DCS control sequence + received by Nvim from the terminal. This can be read in a + |TermResponse| event handler after querying the terminal using + another escape sequence. + ]=], + }, + termrequest = { + type = 'string', + desc = [=[ + The value of the most recent OSC or DCS control sequence + sent from a process running in the embedded |terminal|. + This can be read in a |TermRequest| event handler to respond + to queries from embedded applications. + ]=], + }, + testing = { + desc = [=[ + Must be set before using `test_garbagecollect_now()`. + ]=], + }, + this_session = { + desc = [=[ + Full filename of the last loaded or saved session file. + Empty when no session file has been saved. See |:mksession|. + Modifiable (can be set). + ]=], + }, + throwpoint = { + desc = [=[ + The point where the exception most recently caught and not + finished was thrown. Not set when commands are typed. See + also |v:exception| and |throw-variables|. + Example: >vim + try + throw "oops" + catch /.*/ + echo "Exception from" v:throwpoint + endtry + < + Output: "Exception from test.vim, line 2" + ]=], + }, + ['true'] = { + type = 'boolean', + desc = [=[ + Special value used to put "true" in JSON and msgpack. See + |json_encode()|. This value is converted to "v:true" when used + as a String (e.g. in |expr5| with string concatenation + operator) and to one when used as a Number (e.g. in |expr5| or + |expr7| when used with numeric operators). Read-only. + ]=], + }, + val = { + desc = [=[ + Value of the current item of a |List| or |Dictionary|. Only + valid while evaluating the expression used with |map()| and + |filter()|. Read-only. + ]=], + }, + version = { + type = 'integer', + desc = [=[ + Vim version number: major version times 100 plus minor + version. Vim 5.0 is 500, Vim 5.1 is 501. + Read-only. + Use |has()| to check the Nvim (not Vim) version: >vim + :if has("nvim-0.2.1") + < + ]=], + }, + virtnum = { + type = 'integer', + desc = [=[ + Virtual line number for the 'statuscolumn' expression. + Negative when drawing the status column for virtual lines, zero + when drawing an actual buffer line, and positive when drawing + the wrapped part of a buffer line. + Read-only. + ]=], + }, + vim_did_enter = { + type = 'integer', + desc = [=[ + 0 during startup, 1 just before |VimEnter|. + Read-only. + ]=], + }, + warningmsg = { + type = 'string', + desc = [=[ + Last given warning message. + Modifiable (can be set). + ]=], + }, + windowid = { + type = 'integer', + desc = [=[ + Application-specific window "handle" which may be set by any + attached UI. Defaults to zero. + Note: For Nvim |windows| use |winnr()| or |win_getid()|, see + |window-ID|. + ]=], + }, +} + +return M diff --git a/src/nvim/window.c b/src/nvim/window.c index bcf245ef93..e2c4524eaa 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -13,6 +13,7 @@ #include "nvim/arglist.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" @@ -23,29 +24,32 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/hashtab.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/map_defs.h" -#include "nvim/mapping.h" // IWYU pragma: keep (langmap_adjust_mb) +#include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/match.h" #include "nvim/mbyte.h" #include "nvim/memory.h" @@ -57,12 +61,14 @@ #include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/os/fs.h" +#include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/quickfix.h" #include "nvim/search.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" @@ -70,6 +76,7 @@ #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -191,7 +198,7 @@ void do_window(int nchar, int Prenum, int xchar) if (bt_quickfix(curbuf)) { goto newwindow; } - (void)win_split(Prenum, 0); + win_split(Prenum, 0); break; // split current window in two parts, vertically @@ -204,7 +211,7 @@ void do_window(int nchar, int Prenum, int xchar) if (bt_quickfix(curbuf)) { goto newwindow; } - (void)win_split(Prenum, WSP_VERT); + win_split(Prenum, WSP_VERT); break; // split current window and edit alternate file @@ -223,8 +230,8 @@ void do_window(int nchar, int Prenum, int xchar) } if (!curbuf_locked() && win_split(0, 0) == OK) { - (void)buflist_getfile(Prenum == 0 ? curwin->w_alt_fnum : Prenum, - 0, GETF_ALT, false); + buflist_getfile(Prenum == 0 ? curwin->w_alt_fnum : Prenum, + 0, GETF_ALT, false); } break; @@ -321,13 +328,13 @@ newwindow: wp = lastwin; // wrap around } while (wp != NULL && wp->w_floating - && !wp->w_float_config.focusable) { + && !wp->w_config.focusable) { wp = wp->w_prev; } } else { // go to next window wp = curwin->w_next; while (wp != NULL && wp->w_floating - && !wp->w_float_config.focusable) { + && !wp->w_config.focusable) { wp = wp->w_next; } if (wp == NULL) { @@ -611,7 +618,7 @@ wingotofile: LANGMAP_ADJUST(xchar, true); no_mapping--; allow_keys--; - (void)add_to_showcmd(xchar); + add_to_showcmd(xchar); switch (xchar) { case '}': @@ -662,7 +669,7 @@ wingotofile: beep_flush(); break; } - FloatConfig config = FLOAT_CONFIG_INIT; + WinConfig config = WIN_CONFIG_INIT; config.width = curwin->w_width; config.height = curwin->w_height; config.external = true; @@ -706,7 +713,7 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err) } switchwin_T switchwin; - if (switch_win_noblock(&switchwin, win, tab, false) == FAIL) { + if (switch_win_noblock(&switchwin, win, tab, true) == FAIL) { api_set_error(err, kErrorTypeException, "Failed to switch to window %d", @@ -726,7 +733,7 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err) // So do it now. validate_cursor(); - restore_win_noblock(&switchwin, false); + restore_win_noblock(&switchwin, true); if (noautocmd) { unblock_autocmds(); } @@ -756,10 +763,11 @@ void ui_ext_win_position(win_T *wp, bool validate) return; } - FloatConfig c = wp->w_float_config; + WinConfig c = wp->w_config; if (!c.external) { ScreenGrid *grid = &default_grid; - Float row = c.row, col = c.col; + Float row = c.row; + Float col = c.col; if (c.relative == kFloatRelativeWindow) { Error dummy = ERROR_INIT; win_T *win = find_window_by_handle(c.window, &dummy); @@ -771,7 +779,8 @@ void ui_ext_win_position(win_T *wp, bool validate) ui_ext_win_position(win, validate); } grid = &win->w_grid; - int row_off = 0, col_off = 0; + int row_off = 0; + int col_off = 0; grid_adjust(&grid, &row_off, &col_off); row += row_off; col += col_off; @@ -785,9 +794,9 @@ void ui_ext_win_position(win_T *wp, bool validate) } } - wp->w_grid_alloc.zindex = wp->w_float_config.zindex; + wp->w_grid_alloc.zindex = wp->w_config.zindex; if (ui_has(kUIMultigrid)) { - String anchor = cstr_as_string((char *)float_anchor_str[c.anchor]); + String anchor = cstr_as_string(float_anchor_str[c.anchor]); if (!c.hide) { ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor, grid->handle, row, col, c.focusable, @@ -808,9 +817,10 @@ void ui_ext_win_position(win_T *wp, bool validate) int comp_row = (int)row - (south ? wp->w_height_outer : 0); int comp_col = (int)col - (east ? wp->w_width_outer : 0); + int above_ch = wp->w_config.zindex < kZIndexMessages ? (int)p_ch : 0; comp_row += grid->comp_row; comp_col += grid->comp_col; - comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - (p_ch > 0 ? 1 : 0)), 0); + comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - above_ch), 0); if (!c.fixed || east) { comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0); } @@ -821,7 +831,7 @@ void ui_ext_win_position(win_T *wp, bool validate) ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col, wp->w_height_outer, wp->w_width_outer, valid, false); ui_check_cursor_grid(wp->w_grid_alloc.handle); - wp->w_grid_alloc.focusable = wp->w_float_config.focusable; + wp->w_grid_alloc.focusable = wp->w_config.focusable; if (!valid) { wp->w_grid_alloc.valid = false; redraw_later(wp, UPD_NOT_VALID); @@ -919,6 +929,7 @@ static int check_split_disallowed(void) // WSP_TOP: open window at the top-left of the screen (help window). // WSP_BOT: open window at the bottom-right of the screen (quickfix window). // WSP_HELP: creating the help window, keep layout snapshot +// WSP_NOENTER: do not enter the new window or trigger WinNew autocommands // // return FAIL for failure, OK otherwise int win_split(int size, int flags) @@ -947,20 +958,20 @@ int win_split(int size, int flags) clear_snapshot(curtab, SNAP_HELP_IDX); } - return win_split_ins(size, flags, NULL, 0); + return win_split_ins(size, flags, NULL, 0) == NULL ? FAIL : OK; } /// When "new_wp" is NULL: split the current window in two. /// When "new_wp" is not NULL: insert this window at the far /// top/left/right/bottom. -/// @return FAIL for failure, OK otherwise -int win_split_ins(int size, int flags, win_T *new_wp, int dir) +/// @return NULL for failure, or pointer to new window +win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) { win_T *wp = new_wp; // aucmd_win[] should always remain floating if (new_wp != NULL && is_aucmd_win(new_wp)) { - return FAIL; + return NULL; } win_T *oldwin; @@ -976,22 +987,24 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) int need_status = 0; int new_size = size; bool new_in_layout = (new_wp == NULL || new_wp->w_floating); + bool vertical = flags & WSP_VERT; + bool toplevel = flags & (WSP_TOP | WSP_BOT); // add a status line when p_ls == 1 and splitting the first window if (one_nonfloat() && p_ls == 1 && oldwin->w_status_height == 0) { if (oldwin->w_height <= p_wmh && new_in_layout) { emsg(_(e_noroom)); - return FAIL; + return NULL; } need_status = STATUS_HEIGHT; } bool do_equal = false; int oldwin_height = 0; - const int layout = flags & WSP_VERT ? FR_ROW : FR_COL; + const int layout = vertical ? FR_ROW : FR_COL; bool did_set_fraction = false; - if (flags & WSP_VERT) { + if (vertical) { // Check if we are able to split the current window and compute its // width. // Current window requires at least 1 space. @@ -1002,7 +1015,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } int minwidth; int available; - if (flags & (WSP_BOT | WSP_TOP)) { + if (toplevel) { minwidth = frame_minwidth(topframe, NOWIN); available = topframe->fr_width; needed += minwidth; @@ -1030,7 +1043,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } if (available < needed && new_in_layout) { emsg(_(e_noroom)); - return FAIL; + return NULL; } if (new_size == 0) { new_size = oldwin->w_width / 2; @@ -1083,7 +1096,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } int minheight; int available; - if (flags & (WSP_BOT | WSP_TOP)) { + if (toplevel) { minheight = frame_minheight(topframe, NOWIN) + need_status; available = topframe->fr_height; needed += minheight; @@ -1110,7 +1123,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } if (available < needed && new_in_layout) { emsg(_(e_noroom)); - return FAIL; + return NULL; } oldwin_height = oldwin->w_height; if (need_status) { @@ -1173,7 +1186,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) && ((flags & WSP_BOT) || (flags & WSP_BELOW) || (!(flags & WSP_ABOVE) - && ((flags & WSP_VERT) ? p_spr : p_sb)))) { + && (vertical ? p_spr : p_sb)))) { // new window below/right of current one if (new_wp == NULL) { wp = win_alloc(oldwin, false); @@ -1190,7 +1203,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (new_wp == NULL) { if (wp == NULL) { - return FAIL; + return NULL; } new_frame(wp); @@ -1201,17 +1214,17 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) new_frame(wp); wp->w_floating = false; // non-floating window doesn't store float config or have a border. - wp->w_float_config = FLOAT_CONFIG_INIT; + wp->w_config = WIN_CONFIG_INIT; CLEAR_FIELD(wp->w_border_adj); } - int before; + bool before; frame_T *curfrp; // Reorganise the tree of frames to insert the new window. - if (flags & (WSP_TOP | WSP_BOT)) { - if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0) - || (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0)) { + if (toplevel) { + if ((topframe->fr_layout == FR_COL && !vertical) + || (topframe->fr_layout == FR_ROW && vertical)) { curfrp = topframe->fr_child; if (flags & WSP_BOT) { while (curfrp->fr_next != NULL) { @@ -1228,7 +1241,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) before = false; } else if (flags & WSP_ABOVE) { before = true; - } else if (flags & WSP_VERT) { + } else if (vertical) { before = !p_spr; } else { before = !p_sb; @@ -1276,14 +1289,14 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } wp->w_fraction = oldwin->w_fraction; - if (flags & WSP_VERT) { + if (vertical) { wp->w_p_scr = curwin->w_p_scr; if (need_status) { win_new_height(oldwin, oldwin->w_height - 1); oldwin->w_status_height = need_status; } - if (flags & (WSP_TOP | WSP_BOT)) { + if (toplevel) { // set height and row of new window to full height wp->w_winrow = tabline_height(); win_new_height(wp, curfrp->fr_height - (p_ls == 1 || p_ls == 2)); @@ -1307,7 +1320,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) wp->w_vsep_width = oldwin->w_vsep_width; oldwin->w_vsep_width = 1; } - if (flags & (WSP_TOP | WSP_BOT)) { + if (toplevel) { if (flags & WSP_BOT) { frame_add_vsep(curfrp); } @@ -1329,7 +1342,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } else { const bool is_stl_global = global_stl_height() > 0; // width and column of new window is same as current window - if (flags & (WSP_TOP | WSP_BOT)) { + if (toplevel) { wp->w_wincol = 0; win_new_width(wp, Columns); wp->w_vsep_width = 0; @@ -1350,7 +1363,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) wp->w_hsep_height = oldwin->w_hsep_height; oldwin->w_hsep_height = is_stl_global ? 1 : 0; } - if (flags & (WSP_TOP | WSP_BOT)) { + if (toplevel) { int new_fr_height = curfrp->fr_height - new_size; if (is_stl_global) { if (flags & WSP_BOT) { @@ -1396,8 +1409,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) frame_fix_height(oldwin); } - if (flags & (WSP_TOP | WSP_BOT)) { - (void)win_comp_pos(); + if (toplevel) { + win_comp_pos(); } // Both windows need redrawing. Update all status lines, in case they @@ -1417,7 +1430,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // equalize the window sizes. if (do_equal || dir != 0) { - win_equal(wp, true, (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v')); + win_equal(wp, true, vertical ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v')); } else if (!is_aucmd_win(wp)) { win_fix_scroll(false); } @@ -1438,10 +1451,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } } - // make the new window the current window - win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS | WEE_TRIGGER_ENTER_AUTOCMDS - | WEE_TRIGGER_LEAVE_AUTOCMDS); - if (flags & WSP_VERT) { + if (!(flags & WSP_NOENTER)) { + // make the new window the current window + win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS | WEE_TRIGGER_ENTER_AUTOCMDS + | WEE_TRIGGER_LEAVE_AUTOCMDS); + } + if (vertical) { p_wiw = i; } else { p_wh = i; @@ -1452,7 +1467,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) oldwin->w_pos_changed = true; } - return OK; + return wp; } // Initialize window "newp" from window "oldp". @@ -1548,7 +1563,7 @@ bool win_valid(const win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT /// Check if "win" is a pointer to an existing window in tabpage "tp". /// /// @param win window to check -static bool tabpage_win_valid(const tabpage_T *tp, const win_T *win) +bool tabpage_win_valid(const tabpage_T *tp, const win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (win == NULL) { @@ -1745,7 +1760,7 @@ static void win_exchange(int Prenum) frame_fix_width(curwin); frame_fix_width(wp); - (void)win_comp_pos(); // recompute window positions + win_comp_pos(); // recompute window positions if (wp->w_buffer != curbuf) { reset_VIsual_and_resel(); @@ -1832,7 +1847,7 @@ static void win_rotate(bool upwards, int count) frame_fix_width(wp2); // recompute w_winrow and w_wincol for all windows - (void)win_comp_pos(); + win_comp_pos(); } wp1->w_pos_changed = true; @@ -1869,14 +1884,14 @@ static void win_totop(int size, int flags) } } else { // Remove the window and frame from the tree of frames. - (void)winframe_remove(curwin, &dir, NULL); + winframe_remove(curwin, &dir, NULL); } win_remove(curwin, NULL); last_status(false); // may need to remove last status line - (void)win_comp_pos(); // recompute window positions + win_comp_pos(); // recompute window positions // Split a window on the desired side and put the window there. - (void)win_split_ins(size, flags, curwin, dir); + win_split_ins(size, flags, curwin, dir); if (!(flags & WSP_VERT)) { win_setheight(height); if (p_ea) { @@ -1943,7 +1958,7 @@ void win_move_after(win_T *win1, win_T *win2) win_append(win2, win1); frame_append(win2->w_frame, win1->w_frame); - (void)win_comp_pos(); // recompute w_winrow for all windows + win_comp_pos(); // recompute w_winrow for all windows redraw_later(curwin, UPD_NOT_VALID); } win_enter(win1, false); @@ -2022,7 +2037,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int int totwincount = 0; int next_curwin_size = 0; int room = 0; - int has_next_curwin = 0; + bool has_next_curwin = false; if (topfr->fr_layout == FR_LEAF) { // Set the width/height of this frame. @@ -2311,7 +2326,7 @@ void leaving_window(win_T *const win) // When leaving the window (or closing the window) was done from a // callback we need to break out of the Insert mode loop and restart Insert // mode when entering the window again. - if (State & MODE_INSERT) { + if ((State & MODE_INSERT) && !stop_insert_mode) { stop_insert_mode = true; if (win->w_buffer->b_prompt_insert == NUL) { win->w_buffer->b_prompt_insert = 'A'; @@ -2476,7 +2491,7 @@ bool can_close_in_cmdwin(win_T *win, Error *err) FUNC_ATTR_NONNULL_ALL { if (cmdwin_type != 0) { - if (win == curwin) { + if (win == cmdwin_win) { cmdwin_result = Ctrl_C; return false; } else if (win == cmdwin_old_curwin) { @@ -2544,6 +2559,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev /// "abort_if_last" is passed to close_buffer(): abort closing if all other /// windows are closed. static void win_close_buffer(win_T *win, int action, bool abort_if_last) + FUNC_ATTR_NONNULL_ALL { // Free independent synblock before the buffer is freed. if (win->w_buffer != NULL) { @@ -2581,6 +2597,7 @@ static void win_close_buffer(win_T *win, int action, bool abort_if_last) // Called by :quit, :close, :xit, :wq and findtag(). // Returns FAIL when the window was not closed. int win_close(win_T *win, bool free_buf, bool force) + FUNC_ATTR_NONNULL_ALL { tabpage_T *prev_curtab = curtab; frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent; @@ -2741,7 +2758,7 @@ int win_close(win_T *win, bool free_buf, bool force) if (win->w_floating) { ui_comp_remove_grid(&win->w_grid_alloc); assert(first_tabpage != NULL); // suppress clang "Dereference of NULL pointer" - if (win->w_float_config.external) { + if (win->w_config.external) { for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { if (tp == curtab) { continue; @@ -2813,7 +2830,7 @@ int win_close(win_T *win, bool free_buf, bool force) // only resize that frame. Otherwise resize all windows. win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir); } else { - (void)win_comp_pos(); + win_comp_pos(); win_fix_scroll(false); } } @@ -2879,6 +2896,7 @@ static void do_autocmd_winclosed(win_T *win) // Caller must check if buffer is hidden and whether the tabline needs to be // updated. void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) + FUNC_ATTR_NONNULL_ALL { // Get here with win->w_buffer == NULL when win_close() detects the tab page // changed. @@ -2980,6 +2998,7 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) /// /// @return a pointer to the window that got the freed up space. static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp) + FUNC_ATTR_NONNULL_ARG(1) { win_T *wp; tabpage_T *win_tp = tp == NULL ? curtab : tp; @@ -2998,6 +3017,7 @@ static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp) wp = firstwin; } } else { + assert(tp != curtab); if (tabpage_win_valid(tp, tp->tp_prevwin) && tp->tp_prevwin != win) { wp = tp->tp_prevwin; } else { @@ -3021,6 +3041,9 @@ void win_free_all(void) { // avoid an error for switching tabpage with the cmdline window open cmdwin_type = 0; + cmdwin_buf = NULL; + cmdwin_win = NULL; + cmdwin_old_curwin = NULL; while (first_tabpage->tp_next != NULL) { tabpage_close(true); @@ -3030,7 +3053,7 @@ void win_free_all(void) win_T *wp = lastwin; win_remove(lastwin, NULL); int dummy; - (void)win_free_mem(wp, &dummy, NULL); + win_free_mem(wp, &dummy, NULL); for (int i = 0; i < AUCMD_WIN_COUNT; i++) { if (aucmd_win[i].auc_win == wp) { aucmd_win[i].auc_win = NULL; @@ -3041,7 +3064,7 @@ void win_free_all(void) for (int i = 0; i < AUCMD_WIN_COUNT; i++) { if (aucmd_win[i].auc_win != NULL) { int dummy; - (void)win_free_mem(aucmd_win[i].auc_win, &dummy, NULL); + win_free_mem(aucmd_win[i].auc_win, &dummy, NULL); aucmd_win[i].auc_win = NULL; } } @@ -3050,7 +3073,7 @@ void win_free_all(void) while (firstwin != NULL) { int dummy; - (void)win_free_mem(firstwin, &dummy, NULL); + win_free_mem(firstwin, &dummy, NULL); } // No window should be used after this. Set curwin to NULL to crash @@ -3067,7 +3090,10 @@ void win_free_all(void) /// /// @return a pointer to the window that got the freed up space. win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) + FUNC_ATTR_NONNULL_ARG(1, 2) { + assert(tp == NULL || tp != curtab); + // If there is only one window there is nothing to remove. if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) { return NULL; @@ -3214,7 +3240,10 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) /// @return a pointer to the frame that will receive the empty screen space that /// is left over after "win" is closed. static frame_T *win_altframe(win_T *win, tabpage_T *tp) + FUNC_ATTR_NONNULL_ARG(1) { + assert(tp == NULL || tp != curtab); + if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) { return alt_tabpage()->tp_curwin->w_frame; } @@ -3278,6 +3307,7 @@ static tabpage_T *alt_tabpage(void) // Find the left-upper window in frame "frp". win_T *frame2win(frame_T *frp) + FUNC_ATTR_NONNULL_ALL { while (frp->fr_win == NULL) { frp = frp->fr_child; @@ -3850,7 +3880,7 @@ void win_alloc_first(void) void win_alloc_aucmd_win(int idx) { Error err = ERROR_INIT; - FloatConfig fconfig = FLOAT_CONFIG_INIT; + WinConfig fconfig = WIN_CONFIG_INIT; fconfig.width = Columns; fconfig.height = 5; fconfig.focusable = false; @@ -4299,7 +4329,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab) for (win_T *wp = old_curtab->tp_firstwin; wp; wp = next_wp) { next_wp = wp->w_next; if (wp->w_floating) { - if (wp->w_float_config.external) { + if (wp->w_config.external) { win_remove(wp, old_curtab); win_append(lastwin_nofloating(), wp); } else { @@ -4310,8 +4340,8 @@ static void tabpage_check_windows(tabpage_T *old_curtab) } for (win_T *wp = firstwin; wp; wp = wp->w_next) { - if (wp->w_floating && !wp->w_float_config.external) { - win_config_float(wp, wp->w_float_config); + if (wp->w_floating && !wp->w_config.external) { + win_config_float(wp, wp->w_config); } wp->w_pos_changed = true; } @@ -4427,6 +4457,10 @@ void tabpage_move(int nr) return; } + if (tabpage_move_disallowed) { + return; + } + int n = 1; tabpage_T *tp; @@ -4471,11 +4505,12 @@ void tabpage_move(int nr) redraw_tabline = true; } -// Go to another window. -// When jumping to another buffer, stop Visual mode. Do this before -// changing windows so we can yank the selection into the '*' register. -// When jumping to another window on the same buffer, adjust its cursor -// position to keep the same Visual area. +/// Go to another window. +/// When jumping to another buffer, stop Visual mode. Do this before +/// changing windows so we can yank the selection into the '*' register. +/// (note: this may trigger ModeChanged autocommand!) +/// When jumping to another window on the same buffer, adjust its cursor +/// position to keep the same Visual area. void win_goto(win_T *wp) { win_T *owp = curwin; @@ -4486,11 +4521,17 @@ void win_goto(win_T *wp) } if (wp->w_buffer != curbuf) { + // careful: triggers ModeChanged autocommand reset_VIsual_and_resel(); } else if (VIsual_active) { wp->w_cursor = curwin->w_cursor; } + // autocommand may have made wp invalid + if (!win_valid(wp)) { + return; + } + win_enter(wp, true); // Conceal cursor line in previous window, unconceal in current window. @@ -4726,10 +4767,14 @@ static void win_enter_ext(win_T *const wp, const int flags) if (wp->w_buffer != curbuf) { buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP); } + if (!curwin_invalid) { prevwin = curwin; // remember for CTRL-W p curwin->w_redr_status = true; + } else if (wp == prevwin) { + prevwin = NULL; // don't want it to be the new curwin } + curwin = wp; curbuf = wp->w_buffer; @@ -4902,12 +4947,12 @@ win_T *buf_jump_open_tab(buf_T *buf) return NULL; } +static int last_win_id = LOWEST_WIN_ID - 1; + /// @param hidden allocate a window structure and link it in the window if // false. win_T *win_alloc(win_T *after, bool hidden) { - static int last_win_id = LOWEST_WIN_ID - 1; - // allocate window structure and linesizes arrays win_T *new_wp = xcalloc(1, sizeof(win_T)); @@ -4939,12 +4984,15 @@ win_T *win_alloc(win_T *after, bool hidden) new_wp->w_cursor.lnum = 1; new_wp->w_scbind_pos = 1; new_wp->w_floating = 0; - new_wp->w_float_config = FLOAT_CONFIG_INIT; + new_wp->w_config = WIN_CONFIG_INIT; new_wp->w_viewport_invalid = true; new_wp->w_viewport_last_topline = 1; new_wp->w_ns_hl = -1; + Set(uint32_t) ns_set = SET_INIT; + new_wp->w_ns_set = ns_set; + // use global option for global-local options new_wp->w_allbuf_opt.wo_so = new_wp->w_p_so = -1; new_wp->w_allbuf_opt.wo_siso = new_wp->w_p_siso = -1; @@ -4972,7 +5020,7 @@ void free_wininfo(wininfo_T *wip, buf_T *bp) /// Remove window 'wp' from the window list and free the structure. /// /// @param tp tab page "win" is in, NULL for current -static void win_free(win_T *wp, tabpage_T *tp) +void win_free(win_T *wp, tabpage_T *tp) { pmap_del(int)(&window_handles, wp->handle, NULL); clearFolding(wp); @@ -4983,6 +5031,8 @@ static void win_free(win_T *wp, tabpage_T *tp) // Don't execute autocommands while the window is halfway being deleted. block_autocmds(); + set_destroy(uint32_t, &wp->w_ns_set); + clear_winopt(&wp->w_onebuf_opt); clear_winopt(&wp->w_allbuf_opt); @@ -5053,8 +5103,8 @@ static void win_free(win_T *wp, tabpage_T *tp) } // free the border text - clear_virttext(&wp->w_float_config.title_chunks); - clear_virttext(&wp->w_float_config.footer_chunks); + clear_virttext(&wp->w_config.title_chunks); + clear_virttext(&wp->w_config.footer_chunks); clear_matches(wp); @@ -5115,7 +5165,10 @@ void win_append(win_T *after, win_T *wp) /// /// @param tp tab page "win" is in, NULL for current void win_remove(win_T *wp, tabpage_T *tp) + FUNC_ATTR_NONNULL_ARG(1) { + assert(tp == NULL || tp != curtab); + if (wp->w_prev != NULL) { wp->w_prev->w_next = wp->w_next; } else if (tp == NULL) { @@ -5177,7 +5230,7 @@ void win_new_screensize(void) if (old_Rows != Rows) { // If 'window' uses the whole screen, keep it using that. // Don't change it when set with "-w size" on the command line. - if (p_window == old_Rows - 1 || (old_Rows == 0 && !option_was_set("window"))) { + if (p_window == old_Rows - 1 || (old_Rows == 0 && !option_was_set(kOptWindow))) { p_window = Rows - 1; } old_Rows = Rows; @@ -5209,7 +5262,7 @@ void win_new_screen_rows(void) frame_new_height(topframe, h, false, false); } - (void)win_comp_pos(); // recompute w_winrow and w_wincol + win_comp_pos(); // recompute w_winrow and w_wincol win_reconfig_floats(); // The size of floats might change compute_cmdrow(); curtab->tp_ch_used = p_ch; @@ -5233,7 +5286,7 @@ void win_new_screen_cols(void) frame_new_width(topframe, Columns, false, false); } - (void)win_comp_pos(); // recompute w_winrow and w_wincol + win_comp_pos(); // recompute w_winrow and w_wincol win_reconfig_floats(); // The size of floats might change } @@ -5343,7 +5396,7 @@ static int check_window_scroll_resize(int *size_count, win_T **first_scroll_win, FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { // Skip floating windows that do not have a snapshot (usually because they are newly-created), - // as unlike split windows, creating floating windows do not cause other windows to resize. + // as unlike split windows, creating floating windows doesn't cause other windows to resize. if (wp->w_floating && wp->w_last_topline == 0) { wp->w_last_topline = wp->w_topline; wp->w_last_topfill = wp->w_topfill; @@ -5452,12 +5505,13 @@ void may_trigger_win_scrolled_resized(void) } int size_count = 0; - win_T *first_scroll_win = NULL, *first_size_win = NULL; + win_T *first_scroll_win = NULL; + win_T *first_size_win = NULL; int cwsr = check_window_scroll_resize(&size_count, &first_scroll_win, &first_size_win, NULL, NULL); - int trigger_resize = do_resize && size_count > 0; - int trigger_scroll = do_scroll && cwsr != 0; + bool trigger_resize = do_resize && size_count > 0; + bool trigger_scroll = do_scroll && cwsr != 0; if (!trigger_resize && !trigger_scroll) { return; // no relevant changes } @@ -5467,7 +5521,7 @@ void may_trigger_win_scrolled_resized(void) // Create the list for v:event.windows before making the snapshot. // windows_list = tv_list_alloc_with_items(size_count); windows_list = tv_list_alloc(size_count); - (void)check_window_scroll_resize(NULL, NULL, NULL, windows_list, NULL); + check_window_scroll_resize(NULL, NULL, NULL, windows_list, NULL); } dict_T *scroll_dict = NULL; @@ -5475,7 +5529,7 @@ void may_trigger_win_scrolled_resized(void) // Create the dict with entries for v:event before making the snapshot. scroll_dict = tv_dict_alloc(); scroll_dict->dv_refcount = 1; - (void)check_window_scroll_resize(NULL, NULL, NULL, NULL, scroll_dict); + check_window_scroll_resize(NULL, NULL, NULL, NULL, scroll_dict); } // WinScrolled/WinResized are triggered only once, even when multiple @@ -5559,7 +5613,7 @@ void win_size_restore(garray_T *gap) } } // recompute the window positions - (void)win_comp_pos(); + win_comp_pos(); } } @@ -5574,7 +5628,7 @@ int win_comp_pos(void) for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { // float might be anchored to moved window - if (wp->w_float_config.relative == kFloatRelativeWindow) { + if (wp->w_config.relative == kFloatRelativeWindow) { wp->w_pos_changed = true; } } @@ -5633,8 +5687,8 @@ void win_setheight_win(int height, win_T *win) height = MAX(height, (int)(win == curwin ? MAX(p_wmh, 1) : p_wmh) + win->w_winbar_height); if (win->w_floating) { - win->w_float_config.height = height; - win_config_float(win, win->w_float_config); + win->w_config.height = height; + win_config_float(win, win->w_config); redraw_later(win, UPD_VALID); } else { frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height); @@ -5645,7 +5699,7 @@ void win_setheight_win(int height, win_T *win) // If there is extra space created between the last window and the command // line, clear it. if (full_screen && msg_scrolled == 0 && row < cmdline_row) { - grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0); + grid_clear(&default_grid, row, cmdline_row, 0, Columns, 0); if (msg_grid.chars) { clear_cmdline = true; } @@ -5845,14 +5899,14 @@ void win_setwidth_win(int width, win_T *wp) width = 0; } if (wp->w_floating) { - wp->w_float_config.width = width; - win_config_float(wp, wp->w_float_config); + wp->w_config.width = width; + win_config_float(wp, wp->w_config); redraw_later(wp, UPD_NOT_VALID); } else { frame_setwidth(wp->w_frame, width + wp->w_vsep_width); // recompute the window positions - (void)win_comp_pos(); + win_comp_pos(); redraw_all_later(UPD_NOT_VALID); } } @@ -6128,7 +6182,7 @@ void win_drag_status_line(win_T *dragwin, int offset) } } int row = win_comp_pos(); - grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0); + grid_clear(&default_grid, row, cmdline_row, 0, Columns, 0); if (msg_grid.chars) { clear_cmdline = true; } @@ -6237,7 +6291,7 @@ void win_drag_vsep_line(win_T *dragwin, int offset) fr = fr->fr_next; } } - (void)win_comp_pos(); + win_comp_pos(); redraw_all_later(UPD_NOT_VALID); } @@ -6262,7 +6316,7 @@ void set_fraction(win_T *wp) /// TODO(vim): Ensure this also works with wrapped lines. /// Requires a not fully visible cursor line to be allowed at the bottom of /// a window("zb"), probably only when 'smoothscroll' is also set. -void win_fix_scroll(int resize) +void win_fix_scroll(bool resize) { if (*p_spk == 'c') { return; // 'splitkeep' is "cursor" @@ -6433,7 +6487,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) } } else if (sline > 0) { while (sline > 0 && lnum > 1) { - (void)hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); if (lnum == 1) { // first line in buffer is folded line_size = 1; @@ -6453,7 +6507,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) if (sline < 0) { // Line we want at top would go off top of screen. Use next // line instead. - (void)hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); + hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); lnum++; wp->w_wrow -= line_size + sline; } else if (sline > 0) { @@ -6548,14 +6602,16 @@ void win_new_width(win_T *wp, int width) win_set_inner_size(wp, true); } +OptInt win_default_scroll(win_T *wp) +{ + return MAX(wp->w_height_inner / 2, 1); +} + void win_comp_scroll(win_T *wp) { const OptInt old_w_p_scr = wp->w_p_scr; + wp->w_p_scr = win_default_scroll(wp); - wp->w_p_scr = wp->w_height_inner / 2; - if (wp->w_p_scr == 0) { - wp->w_p_scr = 1; - } if (wp->w_p_scr != old_w_p_scr) { // Used by "verbose set scroll". wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_sid = SID_WINLAYOUT; @@ -6617,11 +6673,11 @@ void command_height(void) } // Recompute window positions. - (void)win_comp_pos(); + win_comp_pos(); // clear the lines added to cmdline if (full_screen) { - grid_fill(&default_grid, cmdline_row, Rows, 0, Columns, ' ', ' ', 0); + grid_clear(&default_grid, cmdline_row, Rows, 0, Columns, 0); } msg_row = cmdline_row; redraw_cmdline = true; @@ -6637,7 +6693,7 @@ void command_height(void) // Recompute window positions. if (frp != lastwin->w_frame) { - (void)win_comp_pos(); + win_comp_pos(); } } @@ -6853,7 +6909,7 @@ static bool resize_frame_for_status(frame_T *fr) } else if (fp != fr) { frame_new_height(fp, fp->fr_height - 1, false, false); frame_fix_height(wp); - (void)win_comp_pos(); + win_comp_pos(); } else { win_new_height(wp, wp->w_height - 1); } @@ -6875,7 +6931,7 @@ static bool resize_frame_for_winbar(frame_T *fr) frame_new_height(fp, fp->fr_height - 1, false, false); win_new_height(wp, wp->w_height + 1); frame_fix_height(wp); - (void)win_comp_pos(); + win_comp_pos(); return true; } @@ -7211,7 +7267,7 @@ void restore_snapshot(int idx, int close_curwin) && curtab->tp_snapshot[idx]->fr_height == topframe->fr_height && check_snapshot_rec(curtab->tp_snapshot[idx], topframe) == OK) { win_T *wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe); - (void)win_comp_pos(); + win_comp_pos(); if (wp != NULL && close_curwin) { win_goto(wp); } @@ -7309,9 +7365,17 @@ static bool frame_check_width(const frame_T *topfrp, int width) } /// Simple int comparison function for use with qsort() -static int int_cmp(const void *a, const void *b) +static int int_cmp(const void *pa, const void *pb) { - return *(const int *)a - *(const int *)b; + const int a = *(const int *)pa; + const int b = *(const int *)pb; + if (a > b) { + return 1; + } + if (a < b) { + return -1; + } + return 0; } /// Handle setting 'colorcolumn' or 'textwidth' in window "wp". @@ -7388,12 +7452,18 @@ skip: return NULL; // no error } +int get_last_winid(void) +{ + return last_win_id; +} + void win_get_tabwin(handle_T id, int *tabnr, int *winnr) { *tabnr = 0; *winnr = 0; - int tnum = 1, wnum = 1; + int tnum = 1; + int wnum = 1; FOR_ALL_TABS(tp) { FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp->handle == id) { diff --git a/src/nvim/window.h b/src/nvim/window.h index 3650fef46e..d20b799e20 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -21,15 +21,16 @@ enum { /// arguments for win_split() enum { - WSP_ROOM = 0x01, ///< require enough room - WSP_VERT = 0x02, ///< split/equalize vertically - WSP_HOR = 0x04, ///< equalize horizontally - WSP_TOP = 0x08, ///< window at top-left of shell - WSP_BOT = 0x10, ///< window at bottom-right of shell - WSP_HELP = 0x20, ///< creating the help window - WSP_BELOW = 0x40, ///< put new window below/right - WSP_ABOVE = 0x80, ///< put new window above/left - WSP_NEWLOC = 0x100, ///< don't copy location list + WSP_ROOM = 0x01, ///< require enough room + WSP_VERT = 0x02, ///< split/equalize vertically + WSP_HOR = 0x04, ///< equalize horizontally + WSP_TOP = 0x08, ///< window at top-left of shell + WSP_BOT = 0x10, ///< window at bottom-right of shell + WSP_HELP = 0x20, ///< creating the help window + WSP_BELOW = 0x40, ///< put new window below/right + WSP_ABOVE = 0x80, ///< put new window above/left + WSP_NEWLOC = 0x100, ///< don't copy location list + WSP_NOENTER = 0x200, ///< don't enter the new window }; enum { @@ -38,8 +39,12 @@ enum { STATUS_HEIGHT = 1, ///< height of a status line under a window }; -/// Lowest number used for window ID. Cannot have this many windows per tab. -enum { LOWEST_WIN_ID = 1000, }; +enum { + /// Lowest number used for window ID. Cannot have this many windows per tab. + LOWEST_WIN_ID = 1000, +}; + +EXTERN int tabpage_move_disallowed INIT( = 0); ///< moving tabpages around disallowed /// Set to true if 'cmdheight' was explicitly set to 0. EXTERN bool p_ch_was_zero INIT( = false); diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c index 44f0e2fc0b..8fe0315230 100644 --- a/src/nvim/winfloat.c +++ b/src/nvim/winfloat.c @@ -9,9 +9,9 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/drawscreen.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/mouse.h" @@ -20,7 +20,9 @@ #include "nvim/optionstr.h" #include "nvim/pos_defs.h" #include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/vim_defs.h" #include "nvim/window.h" #include "nvim/winfloat.h" @@ -36,7 +38,7 @@ /// @param last make the window the last one in the window list. /// Only used when allocating the autocommand window. /// @param config must already have been validated! -win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err) +win_T *win_new_float(win_T *wp, bool last, WinConfig fconfig, Error *err) { if (wp == NULL) { wp = win_alloc(last ? lastwin : lastwin_nofloating(), false); @@ -57,7 +59,7 @@ win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err) int dir; winframe_remove(wp, &dir, NULL); XFREE_CLEAR(wp->w_frame); - (void)win_comp_pos(); // recompute window positions + win_comp_pos(); // recompute window positions win_remove(wp, NULL); win_append(lastwin_nofloating(), wp); } @@ -136,7 +138,7 @@ int win_border_width(win_T *wp) return wp->w_border_adj[1] + wp->w_border_adj[3]; } -void win_config_float(win_T *wp, FloatConfig fconfig) +void win_config_float(win_T *wp, WinConfig fconfig) { wp->w_width = MAX(fconfig.width, 1); wp->w_height = MAX(fconfig.height, 1); @@ -147,7 +149,9 @@ void win_config_float(win_T *wp, FloatConfig fconfig) fconfig.col += curwin->w_wcol; fconfig.window = curwin->handle; } else if (fconfig.relative == kFloatRelativeMouse) { - int row = mouse_row, col = mouse_col, grid = mouse_grid; + int row = mouse_row; + int col = mouse_col; + int grid = mouse_grid; win_T *mouse_win = mouse_find_win(&grid, &row, &col); if (mouse_win != NULL) { fconfig.relative = kFloatRelativeWindow; @@ -157,17 +161,17 @@ void win_config_float(win_T *wp, FloatConfig fconfig) } } - bool change_external = fconfig.external != wp->w_float_config.external; - bool change_border = (fconfig.border != wp->w_float_config.border + bool change_external = fconfig.external != wp->w_config.external; + bool change_border = (fconfig.border != wp->w_config.border || memcmp(fconfig.border_hl_ids, - wp->w_float_config.border_hl_ids, + wp->w_config.border_hl_ids, sizeof fconfig.border_hl_ids) != 0); - wp->w_float_config = fconfig; + wp->w_config = fconfig; - bool has_border = wp->w_floating && wp->w_float_config.border; + bool has_border = wp->w_floating && wp->w_config.border; for (int i = 0; i < 4; i++) { - int new_adj = has_border && wp->w_float_config.border_chars[2 * i + 1][0]; + int new_adj = has_border && wp->w_config.border_chars[2 * i + 1][0]; if (new_adj != wp->w_border_adj[i]) { change_border = true; wp->w_border_adj[i] = new_adj; @@ -180,7 +184,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig) } win_set_inner_size(wp, true); - must_redraw = MAX(must_redraw, UPD_VALID); + set_must_redraw(UPD_VALID); wp->w_pos_changed = true; if (change_external || change_border) { @@ -189,22 +193,23 @@ void win_config_float(win_T *wp, FloatConfig fconfig) } // compute initial position - if (wp->w_float_config.relative == kFloatRelativeWindow) { - int row = (int)wp->w_float_config.row; - int col = (int)wp->w_float_config.col; + if (wp->w_config.relative == kFloatRelativeWindow) { + int row = (int)wp->w_config.row; + int col = (int)wp->w_config.col; Error dummy = ERROR_INIT; - win_T *parent = find_window_by_handle(wp->w_float_config.window, &dummy); + win_T *parent = find_window_by_handle(wp->w_config.window, &dummy); if (parent) { row += parent->w_winrow; col += parent->w_wincol; ScreenGrid *grid = &parent->w_grid; - int row_off = 0, col_off = 0; + int row_off = 0; + int col_off = 0; grid_adjust(&grid, &row_off, &col_off); row += row_off; col += col_off; - if (wp->w_float_config.bufpos.lnum >= 0) { - pos_T pos = { wp->w_float_config.bufpos.lnum + 1, - wp->w_float_config.bufpos.col, 0 }; + if (wp->w_config.bufpos.lnum >= 0) { + pos_T pos = { wp->w_config.bufpos.lnum + 1, + wp->w_config.bufpos.col, 0 }; int trow, tcol, tcolc, tcole; textpos2screenpos(parent, &pos, &trow, &tcol, &tcolc, &tcole, true); row += trow - 1; @@ -228,7 +233,9 @@ void win_config_float(win_T *wp, FloatConfig fconfig) static int float_zindex_cmp(const void *a, const void *b) { - return (*(win_T **)b)->w_float_config.zindex - (*(win_T **)a)->w_float_config.zindex; + int za = (*(win_T **)a)->w_config.zindex; + int zb = (*(win_T **)b)->w_config.zindex; + return za == zb ? 0 : za < zb ? 1 : -1; } void win_float_remove(bool bang, int count) @@ -237,7 +244,9 @@ void win_float_remove(bool bang, int count) for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { kv_push(float_win_arr, wp); } - qsort(float_win_arr.items, float_win_arr.size, sizeof(win_T *), float_zindex_cmp); + if (float_win_arr.size > 0) { + qsort(float_win_arr.items, float_win_arr.size, sizeof(win_T *), float_zindex_cmp); + } for (size_t i = 0; i < float_win_arr.size; i++) { if (win_close(float_win_arr.items[i], false, false) == FAIL) { break; @@ -256,8 +265,8 @@ void win_check_anchored_floats(win_T *win) { for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { // float might be anchored to moved window - if (wp->w_float_config.relative == kFloatRelativeWindow - && wp->w_float_config.window == win->handle) { + if (wp->w_config.relative == kFloatRelativeWindow + && wp->w_config.window == win->handle) { wp->w_pos_changed = true; } } @@ -266,7 +275,7 @@ void win_check_anchored_floats(win_T *win) void win_reconfig_floats(void) { for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { - win_config_float(wp, wp->w_float_config); + win_config_float(wp, wp->w_config); } } @@ -287,3 +296,13 @@ bool win_float_valid(const win_T *win) } return false; } + +win_T *win_float_find_preview(void) +{ + for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { + if (wp->w_float_is_info) { + return wp; + } + } + return NULL; +} diff --git a/src/nvim/winfloat.h b/src/nvim/winfloat.h index 876ea9ccea..6e30c8da69 100644 --- a/src/nvim/winfloat.h +++ b/src/nvim/winfloat.h @@ -2,6 +2,13 @@ #include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/buffer_defs.h" // IWYU pragma: keep +#include "nvim/macros_defs.h" + +/// NW -> 0 +/// NE -> kFloatAnchorEast +/// SW -> kFloatAnchorSouth +/// SE -> kFloatAnchorSouth | kFloatAnchorEast +EXTERN const char *const float_anchor_str[] INIT( = { "NW", "NE", "SW", "SE" }); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "winfloat.h.generated.h" diff --git a/src/nvim/yankmap.c b/src/nvim/yankmap.c new file mode 100644 index 0000000000..591bcffe33 --- /dev/null +++ b/src/nvim/yankmap.c @@ -0,0 +1,45 @@ +#include "nvim/yankmap.h" + +#include "nvim/memory.h" + +void init_yankmap(yankmap_T* map) +{ + memset(map, 0, sizeof(yankmap_T)); + + map->reg_to_yankreg = (Map(int, ptr_t))MAP_INIT; + map->yankreg_to_reg = (Map(ptr_t, int))MAP_INIT; + // map_init(int, ptr_t, &map->reg_to_yankreg); + // map_init(ptr_t, int, &map->yankreg_to_reg); +} + +yankreg_T* yankmap_get(yankmap_T* yankmap, int reg) +{ + bool is_new = false; + yankreg_T** ret + = (yankreg_T**)map_put_ref(int, ptr_t)(&yankmap->reg_to_yankreg, reg, NULL, &is_new); + + if (ret) { + if (is_new) { + *ret = xcalloc(sizeof(yankreg_T), 1); + } + + /* Add the back-reference */ + int* ref = map_put_ref(ptr_t, int)(&yankmap->yankreg_to_reg, *ret, NULL, NULL); + *ref = reg; + + return *ret; + } + + return NULL; +} + +int yankmap_find(yankmap_T* yankmap, yankreg_T* yankreg) +{ + int* ref = map_ref(ptr_t, int)(&yankmap->yankreg_to_reg, yankreg, NULL); + + if (ref) { + return *ref; + } + + return -1; +} diff --git a/src/nvim/yankmap.h b/src/nvim/yankmap.h new file mode 100644 index 0000000000..4468f3a016 --- /dev/null +++ b/src/nvim/yankmap.h @@ -0,0 +1,25 @@ +#ifndef YANK_TRIE_H_ +#define YANK_TRIE_H_ + +#include <stdbool.h> + +#include "nvim/map_defs.h" +#include "nvim/ops.h" + +typedef struct { + /* Register name to yank register. */ + Map(int, ptr_t) reg_to_yankreg; + + /* Yank register to register name. */ + Map(ptr_t, int) yankreg_to_reg; +} yankmap_T; + +void init_yankmap(yankmap_T* yankmap); + +yankreg_T* yankmap_get(yankmap_T* yankmap, int reg); + +yankreg_T* yankmap_put(yankmap_T* yankmap, int index); + +int yankmap_find(yankmap_T* yankmap, yankreg_T* yankreg); + +#endif diff --git a/src/termkey/driver-csi.c b/src/termkey/driver-csi.c new file mode 100644 index 0000000000..4cd5bbafe4 --- /dev/null +++ b/src/termkey/driver-csi.c @@ -0,0 +1,772 @@ +#include "termkey.h" +#include "termkey-internal.h" + +#include <stdio.h> +#include <string.h> + +// There are 64 codes 0x40 - 0x7F +static int keyinfo_initialised = 0; +static struct keyinfo ss3s[64]; +static char ss3_kpalts[64]; + +typedef struct { + TermKey *tk; + int saved_string_id; + char *saved_string; +} TermKeyCsi; + +typedef TermKeyResult CsiHandler(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args); +static CsiHandler *csi_handlers[64]; + +/* + * Handler for CSI/SS3 cmd keys + */ + +static struct keyinfo csi_ss3s[64]; + +static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +{ + if(args > 1 && arg[1] != -1) + key->modifiers = arg[1] - 1; + else + key->modifiers = 0; + + key->type = csi_ss3s[cmd - 0x40].type; + key->code.sym = csi_ss3s[cmd - 0x40].sym; + key->modifiers &= ~(csi_ss3s[cmd - 0x40].modifier_mask); + key->modifiers |= csi_ss3s[cmd - 0x40].modifier_set; + + if(key->code.sym == TERMKEY_SYM_UNKNOWN) + return TERMKEY_RES_NONE; + + return TERMKEY_RES_KEY; +} + +static void register_csi_ss3_full(TermKeyType type, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char cmd) +{ + if(cmd < 0x40 || cmd >= 0x80) { + return; + } + + csi_ss3s[cmd - 0x40].type = type; + csi_ss3s[cmd - 0x40].sym = sym; + csi_ss3s[cmd - 0x40].modifier_set = modifier_set; + csi_ss3s[cmd - 0x40].modifier_mask = modifier_mask; + + csi_handlers[cmd - 0x40] = &handle_csi_ss3_full; +} + +static void register_csi_ss3(TermKeyType type, TermKeySym sym, unsigned char cmd) +{ + register_csi_ss3_full(type, sym, 0, 0, cmd); +} + +/* + * Handler for SS3 keys with kpad alternate representations + */ + +static void register_ss3kpalt(TermKeyType type, TermKeySym sym, unsigned char cmd, char kpalt) +{ + if(cmd < 0x40 || cmd >= 0x80) { + return; + } + + ss3s[cmd - 0x40].type = type; + ss3s[cmd - 0x40].sym = sym; + ss3s[cmd - 0x40].modifier_set = 0; + ss3s[cmd - 0x40].modifier_mask = 0; + ss3_kpalts[cmd - 0x40] = kpalt; +} + +/* + * Handler for CSI number ~ function keys + */ + +static struct keyinfo csifuncs[35]; /* This value must be increased if more CSI function keys are added */ +#define NCSIFUNCS (sizeof(csifuncs)/sizeof(csifuncs[0])) + +static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +{ + if(args > 1 && arg[1] != -1) + key->modifiers = arg[1] - 1; + else + key->modifiers = 0; + + key->type = TERMKEY_TYPE_KEYSYM; + + if(arg[0] == 27) { + int mod = key->modifiers; + (*tk->method.emit_codepoint)(tk, arg[2], key); + key->modifiers |= mod; + } + else if(arg[0] >= 0 && arg[0] < NCSIFUNCS) { + key->type = csifuncs[arg[0]].type; + key->code.sym = csifuncs[arg[0]].sym; + key->modifiers &= ~(csifuncs[arg[0]].modifier_mask); + key->modifiers |= csifuncs[arg[0]].modifier_set; + } + else + key->code.sym = TERMKEY_SYM_UNKNOWN; + + if(key->code.sym == TERMKEY_SYM_UNKNOWN) { +#ifdef DEBUG + fprintf(stderr, "CSI: Unknown function key %ld\n", arg[0]); +#endif + return TERMKEY_RES_NONE; + } + + return TERMKEY_RES_KEY; +} + +static void register_csifunc(TermKeyType type, TermKeySym sym, int number) +{ + if(number >= NCSIFUNCS) { + return; + } + + csifuncs[number].type = type; + csifuncs[number].sym = sym; + csifuncs[number].modifier_set = 0; + csifuncs[number].modifier_mask = 0; + + csi_handlers['~' - 0x40] = &handle_csifunc; +} + +/* + * Handler for CSI u extended Unicode keys + */ + +static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +{ + switch(cmd) { + case 'u': { + if(args > 1 && arg[1] != -1) + key->modifiers = arg[1] - 1; + else + key->modifiers = 0; + + int mod = key->modifiers; + key->type = TERMKEY_TYPE_KEYSYM; + (*tk->method.emit_codepoint)(tk, arg[0], key); + key->modifiers |= mod; + + return TERMKEY_RES_KEY; + } + default: + return TERMKEY_RES_NONE; + } +} + +/* + * Handler for CSI M / CSI m mouse events in SGR and rxvt encodings + * Note: This does not handle X10 encoding + */ + +static TermKeyResult handle_csi_m(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +{ + int initial = cmd >> 8; + cmd &= 0xff; + + switch(cmd) { + case 'M': + case 'm': + break; + default: + return TERMKEY_RES_NONE; + } + + if(!initial && args >= 3) { // rxvt protocol + key->type = TERMKEY_TYPE_MOUSE; + key->code.mouse[0] = arg[0]; + + key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; + key->code.mouse[0] &= ~0x1c; + + termkey_key_set_linecol(key, arg[1], arg[2]); + + return TERMKEY_RES_KEY; + } + + if(initial == '<' && args >= 3) { // SGR protocol + key->type = TERMKEY_TYPE_MOUSE; + key->code.mouse[0] = arg[0]; + + key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; + key->code.mouse[0] &= ~0x1c; + + termkey_key_set_linecol(key, arg[1], arg[2]); + + if(cmd == 'm') // release + key->code.mouse[3] |= 0x80; + + return TERMKEY_RES_KEY; + } + + return TERMKEY_RES_NONE; +} + +TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKeyMouseEvent *event, int *button, int *line, int *col) +{ + if(key->type != TERMKEY_TYPE_MOUSE) + return TERMKEY_RES_NONE; + + if(button) + *button = 0; + + termkey_key_get_linecol(key, line, col); + + if(!event) + return TERMKEY_RES_KEY; + + int btn = 0; + + int code = key->code.mouse[0]; + + int drag = code & 0x20; + + code &= ~0x3c; + + switch(code) { + case 0: + case 1: + case 2: + *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS; + btn = code + 1; + break; + + case 3: + *event = TERMKEY_MOUSE_RELEASE; + // no button hint + break; + + case 64: + case 65: + case 66: + case 67: + *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS; + btn = code + 4 - 64; + break; + + default: + *event = TERMKEY_MOUSE_UNKNOWN; + } + + if(button) + *button = btn; + + if(key->code.mouse[3] & 0x80) + *event = TERMKEY_MOUSE_RELEASE; + + return TERMKEY_RES_KEY; +} + +/* + * Handler for CSI ? R position reports + * A plain CSI R with no arguments is probably actually <F3> + */ + +static TermKeyResult handle_csi_R(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +{ + switch(cmd) { + case 'R'|'?'<<8: + if(args < 2) + return TERMKEY_RES_NONE; + + key->type = TERMKEY_TYPE_POSITION; + termkey_key_set_linecol(key, arg[1], arg[0]); + return TERMKEY_RES_KEY; + + default: + return handle_csi_ss3_full(tk, key, cmd, arg, args); + } +} + +TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int *line, int *col) +{ + if(key->type != TERMKEY_TYPE_POSITION) + return TERMKEY_RES_NONE; + + termkey_key_get_linecol(key, line, col); + + return TERMKEY_RES_KEY; +} + +/* + * Handler for CSI $y mode status reports + */ + +static TermKeyResult handle_csi_y(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +{ + switch(cmd) { + case 'y'|'$'<<16: + case 'y'|'$'<<16 | '?'<<8: + if(args < 2) + return TERMKEY_RES_NONE; + + key->type = TERMKEY_TYPE_MODEREPORT; + key->code.mouse[0] = (cmd >> 8); + key->code.mouse[1] = arg[0] >> 8; + key->code.mouse[2] = arg[0] & 0xff; + key->code.mouse[3] = arg[1]; + return TERMKEY_RES_KEY; + + default: + return TERMKEY_RES_NONE; + } +} + +TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial, int *mode, int *value) +{ + if(key->type != TERMKEY_TYPE_MODEREPORT) + return TERMKEY_RES_NONE; + + if(initial) + *initial = key->code.mouse[0]; + + if(mode) + *mode = ((uint8_t)key->code.mouse[1] << 8) | (uint8_t)key->code.mouse[2]; + + if(value) + *value = key->code.mouse[3]; + + return TERMKEY_RES_KEY; +} + +#define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) + +static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, long args[], size_t *nargs, unsigned long *commandp) +{ + size_t csi_end = introlen; + + while(csi_end < tk->buffcount) { + if(CHARAT(csi_end) >= 0x40 && CHARAT(csi_end) < 0x80) + break; + csi_end++; + } + + if(csi_end >= tk->buffcount) + return TERMKEY_RES_AGAIN; + + unsigned char cmd = CHARAT(csi_end); + *commandp = cmd; + + char present = 0; + int argi = 0; + + size_t p = introlen; + + // See if there is an initial byte + if(CHARAT(p) >= '<' && CHARAT(p) <= '?') { + *commandp |= (CHARAT(p) << 8); + p++; + } + + // Now attempt to parse out up number;number;... separated values + while(p < csi_end) { + unsigned char c = CHARAT(p); + + if(c >= '0' && c <= '9') { + if(!present) { + args[argi] = c - '0'; + present = 1; + } + else { + args[argi] = (args[argi] * 10) + c - '0'; + } + } + else if(c == ';') { + if(!present) + args[argi] = -1; + present = 0; + argi++; + + if(argi > 16) + break; + } + else if(c >= 0x20 && c <= 0x2f) { + *commandp |= c << 16; + break; + } + + p++; + } + + if(present) + argi++; + + *nargs = argi; + *csi_len = csi_end + 1; + + return TERMKEY_RES_KEY; +} + +TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long args[], size_t *nargs, unsigned long *cmd) +{ + size_t dummy; + + if(tk->hightide == 0) + return TERMKEY_RES_NONE; + if(key->type != TERMKEY_TYPE_UNKNOWN_CSI) + return TERMKEY_RES_NONE; + + return parse_csi(tk, 0, &dummy, args, nargs, cmd); +} + +static int register_keys(void) +{ + int i; + + for(i = 0; i < 64; i++) { + csi_ss3s[i].sym = TERMKEY_SYM_UNKNOWN; + ss3s[i].sym = TERMKEY_SYM_UNKNOWN; + ss3_kpalts[i] = 0; + } + + for(i = 0; i < NCSIFUNCS; i++) + csifuncs[i].sym = TERMKEY_SYM_UNKNOWN; + + register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP, 'A'); + register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN, 'B'); + register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, 'C'); + register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT, 'D'); + register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 'E'); + register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END, 'F'); + register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME, 'H'); + register_csi_ss3(TERMKEY_TYPE_FUNCTION, 1, 'P'); + register_csi_ss3(TERMKEY_TYPE_FUNCTION, 2, 'Q'); + register_csi_ss3(TERMKEY_TYPE_FUNCTION, 3, 'R'); + register_csi_ss3(TERMKEY_TYPE_FUNCTION, 4, 'S'); + + register_csi_ss3_full(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_TAB, TERMKEY_KEYMOD_SHIFT, TERMKEY_KEYMOD_SHIFT, 'Z'); + + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPENTER, 'M', 0); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPEQUALS, 'X', '='); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMULT, 'j', '*'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPLUS, 'k', '+'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPCOMMA, 'l', ','); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMINUS, 'm', '-'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPERIOD, 'n', '.'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPDIV, 'o', '/'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP0, 'p', '0'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP1, 'q', '1'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP2, 'r', '2'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP3, 's', '3'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP4, 't', '4'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP5, 'u', '5'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP6, 'v', '6'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP7, 'w', '7'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP8, 'x', '8'); + register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP9, 'y', '9'); + + register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_FIND, 1); + register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_INSERT, 2); + register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DELETE, 3); + register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SELECT, 4); + register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 5); + register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 6); + register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME, 7); + register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END, 8); + + register_csifunc(TERMKEY_TYPE_FUNCTION, 1, 11); + register_csifunc(TERMKEY_TYPE_FUNCTION, 2, 12); + register_csifunc(TERMKEY_TYPE_FUNCTION, 3, 13); + register_csifunc(TERMKEY_TYPE_FUNCTION, 4, 14); + register_csifunc(TERMKEY_TYPE_FUNCTION, 5, 15); + register_csifunc(TERMKEY_TYPE_FUNCTION, 6, 17); + register_csifunc(TERMKEY_TYPE_FUNCTION, 7, 18); + register_csifunc(TERMKEY_TYPE_FUNCTION, 8, 19); + register_csifunc(TERMKEY_TYPE_FUNCTION, 9, 20); + register_csifunc(TERMKEY_TYPE_FUNCTION, 10, 21); + register_csifunc(TERMKEY_TYPE_FUNCTION, 11, 23); + register_csifunc(TERMKEY_TYPE_FUNCTION, 12, 24); + register_csifunc(TERMKEY_TYPE_FUNCTION, 13, 25); + register_csifunc(TERMKEY_TYPE_FUNCTION, 14, 26); + register_csifunc(TERMKEY_TYPE_FUNCTION, 15, 28); + register_csifunc(TERMKEY_TYPE_FUNCTION, 16, 29); + register_csifunc(TERMKEY_TYPE_FUNCTION, 17, 31); + register_csifunc(TERMKEY_TYPE_FUNCTION, 18, 32); + register_csifunc(TERMKEY_TYPE_FUNCTION, 19, 33); + register_csifunc(TERMKEY_TYPE_FUNCTION, 20, 34); + + csi_handlers['u' - 0x40] = &handle_csi_u; + + csi_handlers['M' - 0x40] = &handle_csi_m; + csi_handlers['m' - 0x40] = &handle_csi_m; + + csi_handlers['R' - 0x40] = &handle_csi_R; + + csi_handlers['y' - 0x40] = &handle_csi_y; + + keyinfo_initialised = 1; + return 1; +} + +static void *new_driver(TermKey *tk, const char *term) +{ + if(!keyinfo_initialised) + if(!register_keys()) + return NULL; + + TermKeyCsi *csi = malloc(sizeof *csi); + if(!csi) + return NULL; + + csi->tk = tk; + csi->saved_string_id = 0; + csi->saved_string = NULL; + + return csi; +} + +static void free_driver(void *info) +{ + TermKeyCsi *csi = info; + + if(csi->saved_string) + free(csi->saved_string); + + free(csi); +} + +static TermKeyResult peekkey_csi(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep) +{ + size_t csi_len; + size_t args = 16; + long arg[16]; + unsigned long cmd; + + TermKeyResult ret = parse_csi(tk, introlen, &csi_len, arg, &args, &cmd); + + if(ret == TERMKEY_RES_AGAIN) { + if(!force) + return TERMKEY_RES_AGAIN; + + (*tk->method.emit_codepoint)(tk, '[', key); + key->modifiers |= TERMKEY_KEYMOD_ALT; + *nbytep = introlen; + return TERMKEY_RES_KEY; + } + + if(cmd == 'M' && args < 3) { // Mouse in X10 encoding consumes the next 3 bytes also + tk->buffstart += csi_len; + tk->buffcount -= csi_len; + + TermKeyResult mouse_result = (*tk->method.peekkey_mouse)(tk, key, nbytep); + + tk->buffstart -= csi_len; + tk->buffcount += csi_len; + + if(mouse_result == TERMKEY_RES_KEY) + *nbytep += csi_len; + + return mouse_result; + } + + TermKeyResult result = TERMKEY_RES_NONE; + + // We know from the logic above that cmd must be >= 0x40 and < 0x80 + if(csi_handlers[(cmd & 0xff) - 0x40]) + result = (*csi_handlers[(cmd & 0xff) - 0x40])(tk, key, cmd, arg, args); + + if(result == TERMKEY_RES_NONE) { +#ifdef DEBUG + switch(args) { + case 0: + fprintf(stderr, "CSI: Unknown cmd=%c\n", (char)cmd); + break; + case 1: + fprintf(stderr, "CSI: Unknown arg1=%ld cmd=%c\n", arg[0], (char)cmd); + break; + case 2: + fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld cmd=%c\n", arg[0], arg[1], (char)cmd); + break; + case 3: + fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld cmd=%c\n", arg[0], arg[1], arg[2], (char)cmd); + break; + default: + fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld ... args=%d cmd=%c\n", arg[0], arg[1], arg[2], args, (char)cmd); + break; + } +#endif + key->type = TERMKEY_TYPE_UNKNOWN_CSI; + key->code.number = cmd; + key->modifiers = 0; + + tk->hightide = csi_len - introlen; + *nbytep = introlen; // Do not yet eat the data bytes + return TERMKEY_RES_KEY; + } + + *nbytep = csi_len; + return result; +} + +static TermKeyResult peekkey_ss3(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep) +{ + if(tk->buffcount < introlen + 1) { + if(!force) + return TERMKEY_RES_AGAIN; + + (*tk->method.emit_codepoint)(tk, 'O', key); + key->modifiers |= TERMKEY_KEYMOD_ALT; + *nbytep = tk->buffcount; + return TERMKEY_RES_KEY; + } + + unsigned char cmd = CHARAT(introlen); + + if(cmd < 0x40 || cmd >= 0x80) + return TERMKEY_RES_NONE; + + key->type = csi_ss3s[cmd - 0x40].type; + key->code.sym = csi_ss3s[cmd - 0x40].sym; + key->modifiers = csi_ss3s[cmd - 0x40].modifier_set; + + if(key->code.sym == TERMKEY_SYM_UNKNOWN) { + if(tk->flags & TERMKEY_FLAG_CONVERTKP && ss3_kpalts[cmd - 0x40]) { + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = ss3_kpalts[cmd - 0x40]; + key->modifiers = 0; + + key->utf8[0] = key->code.codepoint; + key->utf8[1] = 0; + } + else { + key->type = ss3s[cmd - 0x40].type; + key->code.sym = ss3s[cmd - 0x40].sym; + key->modifiers = ss3s[cmd - 0x40].modifier_set; + } + } + + if(key->code.sym == TERMKEY_SYM_UNKNOWN) { +#ifdef DEBUG + fprintf(stderr, "CSI: Unknown SS3 %c (0x%02x)\n", (char)cmd, cmd); +#endif + return TERMKEY_RES_NONE; + } + + *nbytep = introlen + 1; + + return TERMKEY_RES_KEY; +} + +static TermKeyResult peekkey_ctrlstring(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep) +{ + size_t str_end = introlen; + + while(str_end < tk->buffcount) { + if(CHARAT(str_end) == 0x07) // BEL + break; + if(CHARAT(str_end) == 0x9c) // ST + break; + if(CHARAT(str_end) == 0x1b && + (str_end + 1) < tk->buffcount && + CHARAT(str_end+1) == 0x5c) // ESC-prefixed ST + break; + + str_end++; + } + + if(str_end >= tk->buffcount) + return TERMKEY_RES_AGAIN; + +#ifdef DEBUG + fprintf(stderr, "Found a control string: %*s", + str_end - introlen, tk->buffer + tk->buffstart + introlen); +#endif + + *nbytep = str_end + 1; + if(CHARAT(str_end) == 0x1b) + (*nbytep)++; + + if(csi->saved_string) + free(csi->saved_string); + + size_t len = str_end - introlen; + + csi->saved_string_id++; + csi->saved_string = malloc(len + 1); + + strncpy(csi->saved_string, (char *)tk->buffer + tk->buffstart + introlen, len); + csi->saved_string[len] = 0; + + key->type = (CHARAT(introlen-1) & 0x1f) == 0x10 ? + TERMKEY_TYPE_DCS : TERMKEY_TYPE_OSC; + key->code.number = csi->saved_string_id; + key->modifiers = 0; + + return TERMKEY_RES_KEY; +} + +static TermKeyResult peekkey(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytep) +{ + if(tk->buffcount == 0) + return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; + + TermKeyCsi *csi = info; + + switch(CHARAT(0)) { + case 0x1b: + if(tk->buffcount < 2) + return TERMKEY_RES_NONE; + + switch(CHARAT(1)) { + case 0x4f: // ESC-prefixed SS3 + return peekkey_ss3(tk, csi, 2, key, force, nbytep); + + case 0x50: // ESC-prefixed DCS + case 0x5d: // ESC-prefixed OSC + return peekkey_ctrlstring(tk, csi, 2, key, force, nbytep); + + case 0x5b: // ESC-prefixed CSI + return peekkey_csi(tk, csi, 2, key, force, nbytep); + } + + return TERMKEY_RES_NONE; + + case 0x8f: // SS3 + return peekkey_ss3(tk, csi, 1, key, force, nbytep); + + case 0x90: // DCS + case 0x9d: // OSC + return peekkey_ctrlstring(tk, csi, 1, key, force, nbytep); + + case 0x9b: // CSI + return peekkey_csi(tk, csi, 1, key, force, nbytep); + } + + return TERMKEY_RES_NONE; +} + +struct TermKeyDriver termkey_driver_csi = { + .name = "CSI", + + .new_driver = new_driver, + .free_driver = free_driver, + + .peekkey = peekkey, +}; + +TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp) +{ + struct TermKeyDriverNode *p; + for(p = tk->drivers; p; p = p->next) + if(p->driver == &termkey_driver_csi) + break; + + if(!p) + return TERMKEY_RES_NONE; + + if(key->type != TERMKEY_TYPE_DCS && + key->type != TERMKEY_TYPE_OSC) + return TERMKEY_RES_NONE; + + TermKeyCsi *csi = p->info; + + if(csi->saved_string_id != key->code.number) + return TERMKEY_RES_NONE; + + *strp = csi->saved_string; + + return TERMKEY_RES_KEY; +} diff --git a/src/termkey/driver-ti.c b/src/termkey/driver-ti.c new file mode 100644 index 0000000000..1f8ee10808 --- /dev/null +++ b/src/termkey/driver-ti.c @@ -0,0 +1,654 @@ +// we want strdup() +#define _XOPEN_SOURCE 600 + +#include "termkey.h" +#include "termkey-internal.h" + +#ifdef HAVE_UNIBILIUM +# include <unibilium.h> +#else +# include <curses.h> +# include <term.h> + +/* curses.h has just polluted our namespace. We want this back */ +# undef buttons +#endif + +#include <ctype.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#ifndef _WIN32 +# include <unistd.h> +#else +# include <io.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> + +#define streq(a,b) (!strcmp(a,b)) + +#define MAX_FUNCNAME 9 + +static struct { + const char *funcname; + TermKeyType type; + TermKeySym sym; + int mods; +} funcs[] = +{ + /* THIS LIST MUST REMAIN SORTED! */ + { "backspace", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BACKSPACE, 0 }, + { "begin", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 0 }, + { "beg", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 0 }, + { "btab", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_TAB, TERMKEY_KEYMOD_SHIFT }, + { "cancel", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CANCEL, 0 }, + { "clear", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CLEAR, 0 }, + { "close", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CLOSE, 0 }, + { "command", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_COMMAND, 0 }, + { "copy", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_COPY, 0 }, + { "dc", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DELETE, 0 }, + { "down", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN, 0 }, + { "end", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END, 0 }, + { "enter", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_ENTER, 0 }, + { "exit", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_EXIT, 0 }, + { "find", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_FIND, 0 }, + { "help", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HELP, 0 }, + { "home", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME, 0 }, + { "ic", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_INSERT, 0 }, + { "left", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT, 0 }, + { "mark", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MARK, 0 }, + { "message", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MESSAGE, 0 }, + { "move", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MOVE, 0 }, + { "next", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 0 }, // Not quite, but it's the best we can do + { "npage", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 0 }, + { "open", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_OPEN, 0 }, + { "options", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_OPTIONS, 0 }, + { "ppage", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 0 }, + { "previous", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 0 }, // Not quite, but it's the best we can do + { "print", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PRINT, 0 }, + { "redo", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REDO, 0 }, + { "reference", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REFERENCE, 0 }, + { "refresh", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REFRESH, 0 }, + { "replace", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REPLACE, 0 }, + { "restart", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RESTART, 0 }, + { "resume", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RESUME, 0 }, + { "right", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, 0 }, + { "save", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SAVE, 0 }, + { "select", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SELECT, 0 }, + { "suspend", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SUSPEND, 0 }, + { "undo", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UNDO, 0 }, + { "up", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP, 0 }, + { NULL }, +}; + +#ifdef HAVE_UNIBILIUM +static enum unibi_string unibi_lookup_str(const char *name) +{ + for(enum unibi_string ret = unibi_string_begin_+1; ret < unibi_string_end_; ret++) + if(streq(unibi_name_str(ret), name)) + return ret; + + return -1; +} + +static const char *unibi_get_str_by_name(const unibi_term *ut, const char *name) +{ + enum unibi_string idx = unibi_lookup_str(name); + if(idx == (enum unibi_string)-1) + return NULL; + + return unibi_get_str(ut, idx); +} +#endif + +/* To be efficient at lookups, we store the byte sequence => keyinfo mapping + * in a trie. This avoids a slow linear search through a flat list of + * sequences. Because it is likely most nodes will be very sparse, we optimise + * vector to store an extent map after the database is loaded. + */ + +typedef enum { + TYPE_KEY, + TYPE_ARR, +} trie_nodetype; + +struct trie_node { + trie_nodetype type; +}; + +struct trie_node_key { + trie_nodetype type; + struct keyinfo key; +}; + +struct trie_node_arr { + trie_nodetype type; + unsigned char min, max; /* INCLUSIVE endpoints of the extent range */ + struct trie_node *arr[]; /* dynamic size at allocation time */ +}; + +typedef struct { + TermKey *tk; + +#ifdef HAVE_UNIBILIUM + unibi_term *unibi; /* only valid until first 'start' call */ +#else + char *term; /* only valid until first 'start' call */ +#endif + + struct trie_node *root; + + char *start_string; + char *stop_string; +} TermKeyTI; + +static int insert_seq(TermKeyTI *ti, const char *seq, struct trie_node *node); + +static struct trie_node *new_node_key(TermKeyType type, TermKeySym sym, int modmask, int modset) +{ + struct trie_node_key *n = malloc(sizeof(*n)); + if(!n) + return NULL; + + n->type = TYPE_KEY; + + n->key.type = type; + n->key.sym = sym; + n->key.modifier_mask = modmask; + n->key.modifier_set = modset; + + return (struct trie_node*)n; +} + +static struct trie_node *new_node_arr(unsigned char min, unsigned char max) +{ + struct trie_node_arr *n = malloc(sizeof(*n) + ((int)max-min+1) * sizeof(n->arr[0])); + if(!n) + return NULL; + + n->type = TYPE_ARR; + n->min = min; n->max = max; + + int i; + for(i = min; i <= max; i++) + n->arr[i-min] = NULL; + + return (struct trie_node*)n; +} + +static struct trie_node *lookup_next(struct trie_node *n, unsigned char b) +{ + switch(n->type) { + case TYPE_KEY: + fprintf(stderr, "ABORT: lookup_next within a TYPE_KEY node\n"); + abort(); + case TYPE_ARR: + { + struct trie_node_arr *nar = (struct trie_node_arr*)n; + if(b < nar->min || b > nar->max) + return NULL; + return nar->arr[b - nar->min]; + } + } + + return NULL; // Never reached but keeps compiler happy +} + +static void free_trie(struct trie_node *n) +{ + switch(n->type) { + case TYPE_KEY: + break; + case TYPE_ARR: + { + struct trie_node_arr *nar = (struct trie_node_arr*)n; + int i; + for(i = nar->min; i <= nar->max; i++) + if(nar->arr[i - nar->min]) + free_trie(nar->arr[i - nar->min]); + break; + } + } + + free(n); +} + +static struct trie_node *compress_trie(struct trie_node *n) +{ + if(!n) + return NULL; + + switch(n->type) { + case TYPE_KEY: + return n; + case TYPE_ARR: + { + struct trie_node_arr *nar = (struct trie_node_arr*)n; + unsigned char min, max; + // Find the real bounds + for(min = 0; !nar->arr[min]; min++) + if(min == 255 && !nar->arr[min]) { + free(nar); + return new_node_arr(1, 0); + } + + for(max = 0xff; !nar->arr[max]; max--) + ; + + struct trie_node_arr *new = (struct trie_node_arr*)new_node_arr(min, max); + int i; + for(i = min; i <= max; i++) + new->arr[i - min] = compress_trie(nar->arr[i]); + + free(nar); + return (struct trie_node*)new; + } + } + + return n; +} + +static bool try_load_terminfo_key(TermKeyTI *ti, const char *name, struct keyinfo *info) +{ + const char *value = NULL; + +#ifdef HAVE_UNIBILIUM + if(ti->unibi) + value = unibi_get_str_by_name(ti->unibi, name); +#else + if(ti->term) + value = tigetstr(name); +#endif + + if(ti->tk->ti_getstr_hook) + value = (ti->tk->ti_getstr_hook)(name, value, ti->tk->ti_getstr_hook_data); + + if(!value || value == (char*)-1 || !value[0]) + return false; + + struct trie_node *node = new_node_key(info->type, info->sym, info->modifier_mask, info->modifier_set); + insert_seq(ti, value, node); + + return true; +} + +static int load_terminfo(TermKeyTI *ti) +{ + int i; + +#ifdef HAVE_UNIBILIUM + unibi_term *unibi = ti->unibi; +#else + { + int err; + + /* Have to cast away the const. But it's OK - we know terminfo won't really + * modify term */ + if(setupterm((char*)ti->term, 1, &err) != OK) + return 0; + } +#endif + + ti->root = new_node_arr(0, 0xff); + if(!ti->root) + return 0; + + /* First the regular key strings + */ + for(i = 0; funcs[i].funcname; i++) { + char name[MAX_FUNCNAME + 5 + 1]; + + sprintf(name, "key_%s", funcs[i].funcname); + if(!try_load_terminfo_key(ti, name, &(struct keyinfo){ + .type = funcs[i].type, + .sym = funcs[i].sym, + .modifier_mask = funcs[i].mods, + .modifier_set = funcs[i].mods, + })) + continue; + + /* Maybe it has a shifted version */ + sprintf(name, "key_s%s", funcs[i].funcname); + try_load_terminfo_key(ti, name, &(struct keyinfo){ + .type = funcs[i].type, + .sym = funcs[i].sym, + .modifier_mask = funcs[i].mods | TERMKEY_KEYMOD_SHIFT, + .modifier_set = funcs[i].mods | TERMKEY_KEYMOD_SHIFT, + }); + } + + /* Now the F<digit> keys + */ + for(i = 1; i < 255; i++) { + char name[9]; + sprintf(name, "key_f%d", i); + if(!try_load_terminfo_key(ti, name, &(struct keyinfo){ + .type = TERMKEY_TYPE_FUNCTION, + .sym = i, + .modifier_mask = 0, + .modifier_set = 0, + })) + break; + } + + /* Finally mouse mode */ + { + const char *value = NULL; + +#ifdef HAVE_UNIBILIUM + if(ti->unibi) + value = unibi_get_str_by_name(ti->unibi, "key_mouse"); +#else + if(ti->term) + value = tigetstr("key_mouse"); +#endif + + if(ti->tk->ti_getstr_hook) + value = (ti->tk->ti_getstr_hook)("key_mouse", value, ti->tk->ti_getstr_hook_data); + + /* Some terminfos (e.g. xterm-1006) claim a different key_mouse that won't + * give X10 encoding. We'll only accept this if it's exactly "\e[M" + */ + if(value && streq(value, "\x1b[M")) { + struct trie_node *node = new_node_key(TERMKEY_TYPE_MOUSE, 0, 0, 0); + insert_seq(ti, value, node); + } + } + + /* Take copies of these terminfo strings, in case we build multiple termkey + * instances for multiple different termtypes, and it's different by the + * time we want to use it + */ +#ifdef HAVE_UNIBILIUM + const char *keypad_xmit = unibi ? + unibi_get_str(unibi, unibi_keypad_xmit) : + NULL; +#endif + + if(keypad_xmit) + ti->start_string = strdup(keypad_xmit); + else + ti->start_string = NULL; + +#ifdef HAVE_UNIBILIUM + const char *keypad_local = unibi ? + unibi_get_str(unibi, unibi_keypad_local) : + NULL; +#endif + + if(keypad_local) + ti->stop_string = strdup(keypad_local); + else + ti->stop_string = NULL; + +#ifdef HAVE_UNIBILIUM + if(unibi) + unibi_destroy(unibi); + + ti->unibi = NULL; +#else + if(ti->term) + free(ti->term); + + ti->term = NULL; +#endif + + ti->root = compress_trie(ti->root); + + return 1; +} + +static void *new_driver(TermKey *tk, const char *term) +{ + TermKeyTI *ti = malloc(sizeof *ti); + if(!ti) + return NULL; + + ti->tk = tk; + ti->root = NULL; + ti->start_string = NULL; + ti->stop_string = NULL; + +#ifdef HAVE_UNIBILIUM + ti->unibi = unibi_from_term(term); + int saved_errno = errno; + if(!ti->unibi && saved_errno != ENOENT) { + free(ti); + return NULL; + } + /* ti->unibi may be NULL if errno == ENOENT. That means the terminal wasn't + * known. Lets keep going because if we get getstr hook that might invent + * new strings for us + */ +#else + { + int err; + + ti->term = NULL; + + /* Have to cast away the const. But it's OK - we know terminfo won't really + * modify term */ + if(setupterm((char*)term, 1, &err) == OK) + ti->term = strdup(term); + } +#endif + + return ti; +} + +static int start_driver(TermKey *tk, void *info) +{ + TermKeyTI *ti = info; + struct stat statbuf; + char *start_string; + size_t len; + + if(!ti->root) + load_terminfo(ti); + + start_string = ti->start_string; + + if(tk->fd == -1 || !start_string) + return 1; + + /* The terminfo database will contain keys in application cursor key mode. + * We may need to enable that mode + */ + + /* There's no point trying to write() to a pipe */ + if(fstat(tk->fd, &statbuf) == -1) + return 0; + +#ifndef _WIN32 + if(S_ISFIFO(statbuf.st_mode)) + return 1; +#endif + + // Can't call putp or tputs because they suck and don't give us fd control + len = strlen(start_string); + while(len) { + size_t written = write(tk->fd, start_string, len); + if(written == -1) + return 0; + start_string += written; + len -= written; + } + return 1; +} + +static int stop_driver(TermKey *tk, void *info) +{ + TermKeyTI *ti = info; + struct stat statbuf; + char *stop_string = ti->stop_string; + size_t len; + + if(tk->fd == -1 || !stop_string) + return 1; + + /* There's no point trying to write() to a pipe */ + if(fstat(tk->fd, &statbuf) == -1) + return 0; + +#ifndef _WIN32 + if(S_ISFIFO(statbuf.st_mode)) + return 1; +#endif + + /* The terminfo database will contain keys in application cursor key mode. + * We may need to enable that mode + */ + + // Can't call putp or tputs because they suck and don't give us fd control + len = strlen(stop_string); + while(len) { + size_t written = write(tk->fd, stop_string, len); + if(written == -1) + return 0; + stop_string += written; + len -= written; + } + return 1; +} + +static void free_driver(void *info) +{ + TermKeyTI *ti = info; + + free_trie(ti->root); + + if(ti->start_string) + free(ti->start_string); + + if(ti->stop_string) + free(ti->stop_string); + +#ifdef HAVE_UNIBILIUM + if(ti->unibi) + unibi_destroy(ti->unibi); +#else + if(ti->term) + free(ti->term); +#endif + + free(ti); +} + +#define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) + +static TermKeyResult peekkey(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytep) +{ + TermKeyTI *ti = info; + + if(tk->buffcount == 0) + return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; + + struct trie_node *p = ti->root; + + unsigned int pos = 0; + while(pos < tk->buffcount) { + p = lookup_next(p, CHARAT(pos)); + if(!p) + break; + + pos++; + + if(p->type != TYPE_KEY) + continue; + + struct trie_node_key *nk = (struct trie_node_key*)p; + if(nk->key.type == TERMKEY_TYPE_MOUSE) { + tk->buffstart += pos; + tk->buffcount -= pos; + + TermKeyResult mouse_result = (*tk->method.peekkey_mouse)(tk, key, nbytep); + + tk->buffstart -= pos; + tk->buffcount += pos; + + if(mouse_result == TERMKEY_RES_KEY) + *nbytep += pos; + + return mouse_result; + } + + key->type = nk->key.type; + key->code.sym = nk->key.sym; + key->modifiers = nk->key.modifier_set; + *nbytep = pos; + return TERMKEY_RES_KEY; + } + + // If p is not NULL then we hadn't walked off the end yet, so we have a + // partial match + if(p && !force) + return TERMKEY_RES_AGAIN; + + return TERMKEY_RES_NONE; +} + +static int insert_seq(TermKeyTI *ti, const char *seq, struct trie_node *node) +{ + int pos = 0; + struct trie_node *p = ti->root; + + // Unsigned because we'll be using it as an array subscript + unsigned char b; + + while((b = seq[pos])) { + struct trie_node *next = lookup_next(p, b); + if(!next) + break; + p = next; + pos++; + } + + while((b = seq[pos])) { + struct trie_node *next; + if(seq[pos+1]) + // Intermediate node + next = new_node_arr(0, 0xff); + else + // Final key node + next = node; + + if(!next) + return 0; + + switch(p->type) { + case TYPE_ARR: + { + struct trie_node_arr *nar = (struct trie_node_arr*)p; + if(b < nar->min || b > nar->max) { + fprintf(stderr, "ASSERT FAIL: Trie insert at 0x%02x is outside of extent bounds (0x%02x..0x%02x)\n", + b, nar->min, nar->max); + abort(); + } + nar->arr[b - nar->min] = next; + p = next; + break; + } + case TYPE_KEY: + fprintf(stderr, "ASSERT FAIL: Tried to insert child node in TYPE_KEY\n"); + abort(); + } + + pos++; + } + + return 1; +} + +struct TermKeyDriver termkey_driver_ti = { + .name = "terminfo", + + .new_driver = new_driver, + .free_driver = free_driver, + + .start_driver = start_driver, + .stop_driver = stop_driver, + + .peekkey = peekkey, +}; diff --git a/src/termkey/termkey-internal.h b/src/termkey/termkey-internal.h new file mode 100644 index 0000000000..c300b02616 --- /dev/null +++ b/src/termkey/termkey-internal.h @@ -0,0 +1,112 @@ +#ifndef GUARD_TERMKEY_INTERNAL_H_ +#define GUARD_TERMKEY_INTERNAL_H_ + +#define HAVE_TERMIOS + +#ifdef _WIN32 +# undef HAVE_TERMIOS +#endif + +#include "termkey.h" + +#include <stdint.h> +#ifdef HAVE_TERMIOS +# include <termios.h> +#endif + +#ifdef _MSC_VER +#include <BaseTsd.h> +typedef SSIZE_T ssize_t; +#endif + +struct TermKeyDriver +{ + const char *name; + void *(*new_driver)(TermKey *tk, const char *term); + void (*free_driver)(void *info); + int (*start_driver)(TermKey *tk, void *info); + int (*stop_driver)(TermKey *tk, void *info); + TermKeyResult (*peekkey)(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytes); +}; + +struct keyinfo { + TermKeyType type; + TermKeySym sym; + int modifier_mask; + int modifier_set; +}; + +struct TermKeyDriverNode; +struct TermKeyDriverNode { + struct TermKeyDriver *driver; + void *info; + struct TermKeyDriverNode *next; +}; + +struct TermKey { + int fd; + int flags; + int canonflags; + unsigned char *buffer; + size_t buffstart; // First offset in buffer + size_t buffcount; // NUMBER of entires valid in buffer + size_t buffsize; // Total malloc'ed size + size_t hightide; /* Position beyond buffstart at which peekkey() should next start + * normally 0, but see also termkey_interpret_csi */ + +#ifdef HAVE_TERMIOS + struct termios restore_termios; + char restore_termios_valid; +#endif + + TermKey_Terminfo_Getstr_Hook *ti_getstr_hook; + void *ti_getstr_hook_data; + + int waittime; // msec + + char is_closed; + char is_started; + + int nkeynames; + const char **keynames; + + // There are 32 C0 codes + struct keyinfo c0[32]; + + struct TermKeyDriverNode *drivers; + + // Now some "protected" methods for the driver to call but which we don't + // want exported as real symbols in the library + struct { + void (*emit_codepoint)(TermKey *tk, long codepoint, TermKeyKey *key); + TermKeyResult (*peekkey_simple)(TermKey *tk, TermKeyKey *key, int force, size_t *nbytes); + TermKeyResult (*peekkey_mouse)(TermKey *tk, TermKeyKey *key, size_t *nbytes); + } method; +}; + +static inline void termkey_key_get_linecol(const TermKeyKey *key, int *line, int *col) +{ + if(col) + *col = (unsigned char)key->code.mouse[1] | ((unsigned char)key->code.mouse[3] & 0x0f) << 8; + + if(line) + *line = (unsigned char)key->code.mouse[2] | ((unsigned char)key->code.mouse[3] & 0x70) << 4; +} + +static inline void termkey_key_set_linecol(TermKeyKey *key, int line, int col) +{ + if(line > 0xfff) + line = 0xfff; + + if(col > 0x7ff) + col = 0x7ff; + + key->code.mouse[1] = (line & 0x0ff); + key->code.mouse[2] = (col & 0x0ff); + key->code.mouse[3] = (line & 0xf00) >> 8 | (col & 0x300) >> 4; +} + +extern struct TermKeyDriver termkey_driver_csi; +extern struct TermKeyDriver termkey_driver_ti; + +#endif diff --git a/src/termkey/termkey.c b/src/termkey/termkey.c new file mode 100644 index 0000000000..832e5a9a9e --- /dev/null +++ b/src/termkey/termkey.c @@ -0,0 +1,1606 @@ +#include "termkey.h" +#include "termkey-internal.h" + +#include <ctype.h> +#include <errno.h> +#ifndef _WIN32 +# include <poll.h> +# include <unistd.h> +# include <strings.h> +#else +# include <io.h> +#endif +#include <string.h> + +#include <stdio.h> + +#ifdef _MSC_VER +# define strcaseeq(a,b) (_stricmp(a,b) == 0) +#else +# define strcaseeq(a,b) (strcasecmp(a,b) == 0) +#endif + +void termkey_check_version(int major, int minor) +{ + if(major != TERMKEY_VERSION_MAJOR) { + fprintf(stderr, "libtermkey major version mismatch; %d (wants) != %d (library)\n", + major, TERMKEY_VERSION_MAJOR); + exit(1); + } + + if(minor > TERMKEY_VERSION_MINOR) { + fprintf(stderr, "libtermkey minor version mismatch; %d (wants) > %d (library)\n", + minor, TERMKEY_VERSION_MINOR); + exit(1); + } + + // Happy +} + +static struct TermKeyDriver *drivers[] = { + &termkey_driver_ti, + &termkey_driver_csi, + NULL, +}; + +// Forwards for the "protected" methods +// static void eat_bytes(TermKey *tk, size_t count); +static void emit_codepoint(TermKey *tk, long codepoint, TermKeyKey *key); +static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, size_t *nbytes); +static TermKeyResult peekkey_mouse(TermKey *tk, TermKeyKey *key, size_t *nbytes); + +static TermKeySym register_c0(TermKey *tk, TermKeySym sym, unsigned char ctrl, const char *name); +static TermKeySym register_c0_full(TermKey *tk, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name); + +static struct { + TermKeySym sym; + const char *name; +} keynames[] = { + { TERMKEY_SYM_NONE, "NONE" }, + { TERMKEY_SYM_BACKSPACE, "Backspace" }, + { TERMKEY_SYM_TAB, "Tab" }, + { TERMKEY_SYM_ENTER, "Enter" }, + { TERMKEY_SYM_ESCAPE, "Escape" }, + { TERMKEY_SYM_SPACE, "Space" }, + { TERMKEY_SYM_DEL, "DEL" }, + { TERMKEY_SYM_UP, "Up" }, + { TERMKEY_SYM_DOWN, "Down" }, + { TERMKEY_SYM_LEFT, "Left" }, + { TERMKEY_SYM_RIGHT, "Right" }, + { TERMKEY_SYM_BEGIN, "Begin" }, + { TERMKEY_SYM_FIND, "Find" }, + { TERMKEY_SYM_INSERT, "Insert" }, + { TERMKEY_SYM_DELETE, "Delete" }, + { TERMKEY_SYM_SELECT, "Select" }, + { TERMKEY_SYM_PAGEUP, "PageUp" }, + { TERMKEY_SYM_PAGEDOWN, "PageDown" }, + { TERMKEY_SYM_HOME, "Home" }, + { TERMKEY_SYM_END, "End" }, + { TERMKEY_SYM_CANCEL, "Cancel" }, + { TERMKEY_SYM_CLEAR, "Clear" }, + { TERMKEY_SYM_CLOSE, "Close" }, + { TERMKEY_SYM_COMMAND, "Command" }, + { TERMKEY_SYM_COPY, "Copy" }, + { TERMKEY_SYM_EXIT, "Exit" }, + { TERMKEY_SYM_HELP, "Help" }, + { TERMKEY_SYM_MARK, "Mark" }, + { TERMKEY_SYM_MESSAGE, "Message" }, + { TERMKEY_SYM_MOVE, "Move" }, + { TERMKEY_SYM_OPEN, "Open" }, + { TERMKEY_SYM_OPTIONS, "Options" }, + { TERMKEY_SYM_PRINT, "Print" }, + { TERMKEY_SYM_REDO, "Redo" }, + { TERMKEY_SYM_REFERENCE, "Reference" }, + { TERMKEY_SYM_REFRESH, "Refresh" }, + { TERMKEY_SYM_REPLACE, "Replace" }, + { TERMKEY_SYM_RESTART, "Restart" }, + { TERMKEY_SYM_RESUME, "Resume" }, + { TERMKEY_SYM_SAVE, "Save" }, + { TERMKEY_SYM_SUSPEND, "Suspend" }, + { TERMKEY_SYM_UNDO, "Undo" }, + { TERMKEY_SYM_KP0, "KP0" }, + { TERMKEY_SYM_KP1, "KP1" }, + { TERMKEY_SYM_KP2, "KP2" }, + { TERMKEY_SYM_KP3, "KP3" }, + { TERMKEY_SYM_KP4, "KP4" }, + { TERMKEY_SYM_KP5, "KP5" }, + { TERMKEY_SYM_KP6, "KP6" }, + { TERMKEY_SYM_KP7, "KP7" }, + { TERMKEY_SYM_KP8, "KP8" }, + { TERMKEY_SYM_KP9, "KP9" }, + { TERMKEY_SYM_KPENTER, "KPEnter" }, + { TERMKEY_SYM_KPPLUS, "KPPlus" }, + { TERMKEY_SYM_KPMINUS, "KPMinus" }, + { TERMKEY_SYM_KPMULT, "KPMult" }, + { TERMKEY_SYM_KPDIV, "KPDiv" }, + { TERMKEY_SYM_KPCOMMA, "KPComma" }, + { TERMKEY_SYM_KPPERIOD, "KPPeriod" }, + { TERMKEY_SYM_KPEQUALS, "KPEquals" }, + { 0, NULL }, +}; + +// Mouse event names +static const char *evnames[] = { "Unknown", "Press", "Drag", "Release" }; + +#define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) + +#ifdef DEBUG +/* Some internal debugging functions */ + +static void print_buffer(TermKey *tk) +{ + int i; + for(i = 0; i < tk->buffcount && i < 20; i++) + fprintf(stderr, "%02x ", CHARAT(i)); + if(tk->buffcount > 20) + fprintf(stderr, "..."); +} + +static void print_key(TermKey *tk, TermKeyKey *key) +{ + switch(key->type) { + case TERMKEY_TYPE_UNICODE: + fprintf(stderr, "Unicode codepoint=U+%04lx utf8='%s'", key->code.codepoint, key->utf8); + break; + case TERMKEY_TYPE_FUNCTION: + fprintf(stderr, "Function F%d", key->code.number); + break; + case TERMKEY_TYPE_KEYSYM: + fprintf(stderr, "Keysym sym=%d(%s)", key->code.sym, termkey_get_keyname(tk, key->code.sym)); + break; + case TERMKEY_TYPE_MOUSE: + { + TermKeyMouseEvent ev; + int button, line, col; + termkey_interpret_mouse(tk, key, &ev, &button, &line, &col); + fprintf(stderr, "Mouse ev=%d button=%d pos=(%d,%d)\n", ev, button, line, col); + } + break; + case TERMKEY_TYPE_POSITION: + { + int line, col; + termkey_interpret_position(tk, key, &line, &col); + fprintf(stderr, "Position report pos=(%d,%d)\n", line, col); + } + break; + case TERMKEY_TYPE_MODEREPORT: + { + int initial, mode, value; + termkey_interpret_modereport(tk, key, &initial, &mode, &value); + fprintf(stderr, "Mode report mode=%s %d val=%d\n", initial == '?' ? "DEC" : "ANSI", mode, value); + } + break; + case TERMKEY_TYPE_DCS: + fprintf(stderr, "Device Control String"); + break; + case TERMKEY_TYPE_OSC: + fprintf(stderr, "Operating System Control"); + break; + case TERMKEY_TYPE_UNKNOWN_CSI: + fprintf(stderr, "unknown CSI\n"); + break; + } + + int m = key->modifiers; + fprintf(stderr, " mod=%s%s%s+%02x", + (m & TERMKEY_KEYMOD_CTRL ? "C" : ""), + (m & TERMKEY_KEYMOD_ALT ? "A" : ""), + (m & TERMKEY_KEYMOD_SHIFT ? "S" : ""), + m & ~(TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT|TERMKEY_KEYMOD_SHIFT)); +} + +static const char *res2str(TermKeyResult res) +{ + static char errorbuffer[256]; + + switch(res) { + case TERMKEY_RES_KEY: + return "TERMKEY_RES_KEY"; + case TERMKEY_RES_EOF: + return "TERMKEY_RES_EOF"; + case TERMKEY_RES_AGAIN: + return "TERMKEY_RES_AGAIN"; + case TERMKEY_RES_NONE: + return "TERMKEY_RES_NONE"; + case TERMKEY_RES_ERROR: + snprintf(errorbuffer, sizeof errorbuffer, "TERMKEY_RES_ERROR(errno=%d)\n", errno); + return (const char*)errorbuffer; + } + + return "unknown"; +} +#endif + +/* Similar to snprintf(str, size, "%s", src) except it turns CamelCase into + * space separated values + */ +static int snprint_cameltospaces(char *str, size_t size, const char *src) +{ + int prev_lower = 0; + size_t l = 0; + while(*src && l < size - 1) { + if(isupper(*src) && prev_lower) { + if(str) + str[l++] = ' '; + if(l >= size - 1) + break; + } + prev_lower = islower(*src); + str[l++] = tolower(*src++); + } + str[l] = 0; + /* For consistency with snprintf, return the number of bytes that would have + * been written, excluding '\0' */ + while(*src) { + if(isupper(*src) && prev_lower) { + l++; + } + prev_lower = islower(*src); + src++; l++; + } + return l; +} + +/* Similar to strcmp(str, strcamel, n) except that: + * it compares CamelCase in strcamel with space separated values in str; + * it takes char**s and updates them + * n counts bytes of strcamel, not str + */ +static int strpncmp_camel(const char **strp, const char **strcamelp, size_t n) +{ + const char *str = *strp, *strcamel = *strcamelp; + int prev_lower = 0; + + for( ; (*str || *strcamel) && n; n--) { + char b = tolower(*strcamel); + if(isupper(*strcamel) && prev_lower) { + if(*str != ' ') + break; + str++; + if(*str != b) + break; + } + else + if(*str != b) + break; + + prev_lower = islower(*strcamel); + + str++; + strcamel++; + } + + *strp = str; + *strcamelp = strcamel; + return *str - *strcamel; +} + +static TermKey *termkey_alloc(void) +{ + TermKey *tk = malloc(sizeof(TermKey)); + if(!tk) + return NULL; + + /* Default all the object fields but don't allocate anything */ + + tk->fd = -1; + tk->flags = 0; + tk->canonflags = 0; + + tk->buffer = NULL; + tk->buffstart = 0; + tk->buffcount = 0; + tk->buffsize = 256; /* bytes */ + tk->hightide = 0; + +#ifdef HAVE_TERMIOS + tk->restore_termios_valid = 0; +#endif + + tk->ti_getstr_hook = NULL; + tk->ti_getstr_hook_data = NULL; + + tk->waittime = 50; /* msec */ + + tk->is_closed = 0; + tk->is_started = 0; + + tk->nkeynames = 64; + tk->keynames = NULL; + + for(int i = 0; i < 32; i++) + tk->c0[i].sym = TERMKEY_SYM_NONE; + + tk->drivers = NULL; + + tk->method.emit_codepoint = &emit_codepoint; + tk->method.peekkey_simple = &peekkey_simple; + tk->method.peekkey_mouse = &peekkey_mouse; + + return tk; +} + +static int termkey_init(TermKey *tk, const char *term) +{ + tk->buffer = malloc(tk->buffsize); + if(!tk->buffer) + return 0; + + tk->keynames = malloc(sizeof(tk->keynames[0]) * tk->nkeynames); + if(!tk->keynames) + goto abort_free_buffer; + + int i; + for(i = 0; i < tk->nkeynames; i++) + tk->keynames[i] = NULL; + + for(i = 0; keynames[i].name; i++) + if(termkey_register_keyname(tk, keynames[i].sym, keynames[i].name) == -1) + goto abort_free_keynames; + + register_c0(tk, TERMKEY_SYM_TAB, 0x09, NULL); + register_c0(tk, TERMKEY_SYM_ENTER, 0x0d, NULL); + register_c0(tk, TERMKEY_SYM_ESCAPE, 0x1b, NULL); + + struct TermKeyDriverNode *tail = NULL; + + for(i = 0; drivers[i]; i++) { + void *info = (*drivers[i]->new_driver)(tk, term); + if(!info) + continue; + +#ifdef DEBUG + fprintf(stderr, "Loading the %s driver...\n", drivers[i]->name); +#endif + + struct TermKeyDriverNode *thisdrv = malloc(sizeof(*thisdrv)); + if(!thisdrv) + goto abort_free_drivers; + + thisdrv->driver = drivers[i]; + thisdrv->info = info; + thisdrv->next = NULL; + + if(!tail) + tk->drivers = thisdrv; + else + tail->next = thisdrv; + + tail = thisdrv; + +#ifdef DEBUG + fprintf(stderr, "Loaded %s driver\n", drivers[i]->name); +#endif + } + + if(!tk->drivers) { + errno = ENOENT; + goto abort_free_keynames; + } + + return 1; + +abort_free_drivers: + for(struct TermKeyDriverNode *p = tk->drivers; p; ) { + (*p->driver->free_driver)(p->info); + struct TermKeyDriverNode *next = p->next; + free(p); + p = next; + } + +abort_free_keynames: + free(tk->keynames); + +abort_free_buffer: + free(tk->buffer); + + return 0; +} + +TermKey *termkey_new(int fd, int flags) +{ + TermKey *tk = termkey_alloc(); + if(!tk) + return NULL; + + tk->fd = fd; + + if(!(flags & (TERMKEY_FLAG_RAW|TERMKEY_FLAG_UTF8))) { + char *e; + + /* Most OSes will set .UTF-8. Some will set .utf8. Try to be fairly + * generous in parsing these + */ + if(((e = getenv("LANG")) || (e = getenv("LC_MESSAGES")) || (e = getenv("LC_ALL"))) && + (e = strchr(e, '.')) && e++ && + (strcaseeq(e, "UTF-8") || strcaseeq(e, "UTF8"))) + flags |= TERMKEY_FLAG_UTF8; + else + flags |= TERMKEY_FLAG_RAW; + } + + termkey_set_flags(tk, flags); + + const char *term = getenv("TERM"); + + if(!termkey_init(tk, term)) + goto abort; + + if(!(flags & TERMKEY_FLAG_NOSTART) && !termkey_start(tk)) + goto abort; + + return tk; + +abort: + free(tk); + return NULL; +} + +TermKey *termkey_new_abstract(const char *term, int flags) +{ + TermKey *tk = termkey_alloc(); + if(!tk) + return NULL; + + tk->fd = -1; + + termkey_set_flags(tk, flags); + + if(!termkey_init(tk, term)) { + free(tk); + return NULL; + } + + if(!(flags & TERMKEY_FLAG_NOSTART) && !termkey_start(tk)) + goto abort; + + return tk; + +abort: + free(tk); + return NULL; +} + +void termkey_free(TermKey *tk) +{ + free(tk->buffer); tk->buffer = NULL; + free(tk->keynames); tk->keynames = NULL; + + struct TermKeyDriverNode *p; + for(p = tk->drivers; p; ) { + (*p->driver->free_driver)(p->info); + struct TermKeyDriverNode *next = p->next; + free(p); + p = next; + } + + free(tk); +} + +void termkey_destroy(TermKey *tk) +{ + if(tk->is_started) + termkey_stop(tk); + + termkey_free(tk); +} + +void termkey_hook_terminfo_getstr(TermKey *tk, TermKey_Terminfo_Getstr_Hook *hookfn, void *data) +{ + tk->ti_getstr_hook = hookfn; + tk->ti_getstr_hook_data = data; +} + +int termkey_start(TermKey *tk) +{ + if(tk->is_started) + return 1; + +#ifdef HAVE_TERMIOS + if(tk->fd != -1 && !(tk->flags & TERMKEY_FLAG_NOTERMIOS)) { + struct termios termios; + if(tcgetattr(tk->fd, &termios) == 0) { + tk->restore_termios = termios; + tk->restore_termios_valid = 1; + + termios.c_iflag &= ~(IXON|INLCR|ICRNL); + termios.c_lflag &= ~(ICANON|ECHO +#ifdef IEXTEN + | IEXTEN +#endif + ); + termios.c_cc[VMIN] = 1; + termios.c_cc[VTIME] = 0; + + if(tk->flags & TERMKEY_FLAG_CTRLC) + /* want no signal keys at all, so just disable ISIG */ + termios.c_lflag &= ~ISIG; + else { + /* Disable Ctrl-\==VQUIT and Ctrl-D==VSUSP but leave Ctrl-C as SIGINT */ + termios.c_cc[VQUIT] = _POSIX_VDISABLE; + termios.c_cc[VSUSP] = _POSIX_VDISABLE; + /* Some OSes have Ctrl-Y==VDSUSP */ +#ifdef VDSUSP + termios.c_cc[VDSUSP] = _POSIX_VDISABLE; +#endif + } + +#ifdef DEBUG + fprintf(stderr, "Setting termios(3) flags\n"); +#endif + tcsetattr(tk->fd, TCSANOW, &termios); + } + } +#endif + + struct TermKeyDriverNode *p; + for(p = tk->drivers; p; p = p->next) + if(p->driver->start_driver) + if(!(*p->driver->start_driver)(tk, p->info)) + return 0; + +#ifdef DEBUG + fprintf(stderr, "Drivers started; termkey instance %p is ready\n", tk); +#endif + + tk->is_started = 1; + return 1; +} + +int termkey_stop(TermKey *tk) +{ + if(!tk->is_started) + return 1; + + struct TermKeyDriverNode *p; + for(p = tk->drivers; p; p = p->next) + if(p->driver->stop_driver) + (*p->driver->stop_driver)(tk, p->info); + +#ifdef HAVE_TERMIOS + if(tk->restore_termios_valid) + tcsetattr(tk->fd, TCSANOW, &tk->restore_termios); +#endif + + tk->is_started = 0; + + return 1; +} + +int termkey_is_started(TermKey *tk) +{ + return tk->is_started; +} + +int termkey_get_fd(TermKey *tk) +{ + return tk->fd; +} + +int termkey_get_flags(TermKey *tk) +{ + return tk->flags; +} + +void termkey_set_flags(TermKey *tk, int newflags) +{ + tk->flags = newflags; + + if(tk->flags & TERMKEY_FLAG_SPACESYMBOL) + tk->canonflags |= TERMKEY_CANON_SPACESYMBOL; + else + tk->canonflags &= ~TERMKEY_CANON_SPACESYMBOL; +} + +void termkey_set_waittime(TermKey *tk, int msec) +{ + tk->waittime = msec; +} + +int termkey_get_waittime(TermKey *tk) +{ + return tk->waittime; +} + +int termkey_get_canonflags(TermKey *tk) +{ + return tk->canonflags; +} + +void termkey_set_canonflags(TermKey *tk, int flags) +{ + tk->canonflags = flags; + + if(tk->canonflags & TERMKEY_CANON_SPACESYMBOL) + tk->flags |= TERMKEY_FLAG_SPACESYMBOL; + else + tk->flags &= ~TERMKEY_FLAG_SPACESYMBOL; +} + +size_t termkey_get_buffer_size(TermKey *tk) +{ + return tk->buffsize; +} + +int termkey_set_buffer_size(TermKey *tk, size_t size) +{ + unsigned char *buffer = realloc(tk->buffer, size); + if(!buffer) + return 0; + + tk->buffer = buffer; + tk->buffsize = size; + + return 1; +} + +size_t termkey_get_buffer_remaining(TermKey *tk) +{ + /* Return the total number of free bytes in the buffer, because that's what + * is available to the user. */ + return tk->buffsize - tk->buffcount; +} + +static void eat_bytes(TermKey *tk, size_t count) +{ + if(count >= tk->buffcount) { + tk->buffstart = 0; + tk->buffcount = 0; + return; + } + + tk->buffstart += count; + tk->buffcount -= count; +} + +static inline unsigned int utf8_seqlen(long codepoint) +{ + if(codepoint < 0x0000080) return 1; + if(codepoint < 0x0000800) return 2; + if(codepoint < 0x0010000) return 3; + if(codepoint < 0x0200000) return 4; + if(codepoint < 0x4000000) return 5; + return 6; +} + +static void fill_utf8(TermKeyKey *key) +{ + long codepoint = key->code.codepoint; + int nbytes = utf8_seqlen(codepoint); + + key->utf8[nbytes] = 0; + + // This is easier done backwards + int b = nbytes; + while(b > 1) { + b--; + key->utf8[b] = 0x80 | (codepoint & 0x3f); + codepoint >>= 6; + } + + switch(nbytes) { + case 1: key->utf8[0] = (codepoint & 0x7f); break; + case 2: key->utf8[0] = 0xc0 | (codepoint & 0x1f); break; + case 3: key->utf8[0] = 0xe0 | (codepoint & 0x0f); break; + case 4: key->utf8[0] = 0xf0 | (codepoint & 0x07); break; + case 5: key->utf8[0] = 0xf8 | (codepoint & 0x03); break; + case 6: key->utf8[0] = 0xfc | (codepoint & 0x01); break; + } +} + +#define UTF8_INVALID 0xFFFD +static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, long *cp, size_t *nbytep) +{ + unsigned int nbytes; + + unsigned char b0 = bytes[0]; + + if(b0 < 0x80) { + // Single byte ASCII + *cp = b0; + *nbytep = 1; + return TERMKEY_RES_KEY; + } + else if(b0 < 0xc0) { + // Starts with a continuation byte - that's not right + *cp = UTF8_INVALID; + *nbytep = 1; + return TERMKEY_RES_KEY; + } + else if(b0 < 0xe0) { + nbytes = 2; + *cp = b0 & 0x1f; + } + else if(b0 < 0xf0) { + nbytes = 3; + *cp = b0 & 0x0f; + } + else if(b0 < 0xf8) { + nbytes = 4; + *cp = b0 & 0x07; + } + else if(b0 < 0xfc) { + nbytes = 5; + *cp = b0 & 0x03; + } + else if(b0 < 0xfe) { + nbytes = 6; + *cp = b0 & 0x01; + } + else { + *cp = UTF8_INVALID; + *nbytep = 1; + return TERMKEY_RES_KEY; + } + + for(unsigned int b = 1; b < nbytes; b++) { + unsigned char cb; + + if(b >= len) + return TERMKEY_RES_AGAIN; + + cb = bytes[b]; + if(cb < 0x80 || cb >= 0xc0) { + *cp = UTF8_INVALID; + *nbytep = b; + return TERMKEY_RES_KEY; + } + + *cp <<= 6; + *cp |= cb & 0x3f; + } + + // Check for overlong sequences + if(nbytes > utf8_seqlen(*cp)) + *cp = UTF8_INVALID; + + // Check for UTF-16 surrogates or invalid *cps + if((*cp >= 0xD800 && *cp <= 0xDFFF) || + *cp == 0xFFFE || + *cp == 0xFFFF) + *cp = UTF8_INVALID; + + *nbytep = nbytes; + return TERMKEY_RES_KEY; +} + +static void emit_codepoint(TermKey *tk, long codepoint, TermKeyKey *key) +{ + if(codepoint == 0) { + // ASCII NUL = Ctrl-Space + key->type = TERMKEY_TYPE_KEYSYM; + key->code.sym = TERMKEY_SYM_SPACE; + key->modifiers = TERMKEY_KEYMOD_CTRL; + } + else if(codepoint < 0x20) { + // C0 range + key->code.codepoint = 0; + key->modifiers = 0; + + if(!(tk->flags & TERMKEY_FLAG_NOINTERPRET) && tk->c0[codepoint].sym != TERMKEY_SYM_UNKNOWN) { + key->code.sym = tk->c0[codepoint].sym; + key->modifiers |= tk->c0[codepoint].modifier_set; + } + + if(!key->code.sym) { + key->type = TERMKEY_TYPE_UNICODE; + /* Generically modified Unicode ought not report the SHIFT state, or else + * we get into complications trying to report Shift-; vs : and so on... + * In order to be able to represent Ctrl-Shift-A as CTRL modified + * unicode A, we need to call Ctrl-A simply 'a', lowercase + */ + if(codepoint+0x40 >= 'A' && codepoint+0x40 <= 'Z') + // it's a letter - use lowercase instead + key->code.codepoint = codepoint + 0x60; + else + key->code.codepoint = codepoint + 0x40; + key->modifiers = TERMKEY_KEYMOD_CTRL; + } + else { + key->type = TERMKEY_TYPE_KEYSYM; + } + } + else if(codepoint == 0x7f && !(tk->flags & TERMKEY_FLAG_NOINTERPRET)) { + // ASCII DEL + key->type = TERMKEY_TYPE_KEYSYM; + key->code.sym = TERMKEY_SYM_DEL; + key->modifiers = 0; + } + else if(codepoint >= 0x20 && codepoint < 0x80) { + // ASCII lowbyte range + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = codepoint; + key->modifiers = 0; + } + else if(codepoint >= 0x80 && codepoint < 0xa0) { + // UTF-8 never starts with a C1 byte. So we can be sure of these + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = codepoint - 0x40; + key->modifiers = TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT; + } + else { + // UTF-8 codepoint + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = codepoint; + key->modifiers = 0; + } + + termkey_canonicalise(tk, key); + + if(key->type == TERMKEY_TYPE_UNICODE) + fill_utf8(key); +} + +void termkey_canonicalise(TermKey *tk, TermKeyKey *key) +{ + int flags = tk->canonflags; + + if(flags & TERMKEY_CANON_SPACESYMBOL) { + if(key->type == TERMKEY_TYPE_UNICODE && key->code.codepoint == 0x20) { + key->type = TERMKEY_TYPE_KEYSYM; + key->code.sym = TERMKEY_SYM_SPACE; + } + } + else { + if(key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_SPACE) { + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = 0x20; + fill_utf8(key); + } + } + + if(flags & TERMKEY_CANON_DELBS) { + if(key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_DEL) { + key->code.sym = TERMKEY_SYM_BACKSPACE; + } + } +} + +static TermKeyResult peekkey(TermKey *tk, TermKeyKey *key, int force, size_t *nbytep) +{ + int again = 0; + + if(!tk->is_started) { + errno = EINVAL; + return TERMKEY_RES_ERROR; + } + +#ifdef DEBUG + fprintf(stderr, "getkey(force=%d): buffer ", force); + print_buffer(tk); + fprintf(stderr, "\n"); +#endif + + if(tk->hightide) { + tk->buffstart += tk->hightide; + tk->buffcount -= tk->hightide; + tk->hightide = 0; + } + + TermKeyResult ret; + struct TermKeyDriverNode *p; + for(p = tk->drivers; p; p = p->next) { + ret = (p->driver->peekkey)(tk, p->info, key, force, nbytep); + +#ifdef DEBUG + fprintf(stderr, "Driver %s yields %s\n", p->driver->name, res2str(ret)); +#endif + + switch(ret) { + case TERMKEY_RES_KEY: +#ifdef DEBUG + print_key(tk, key); fprintf(stderr, "\n"); +#endif + // Slide the data down to stop it running away + { + size_t halfsize = tk->buffsize / 2; + + if(tk->buffstart > halfsize) { + memcpy(tk->buffer, tk->buffer + halfsize, halfsize); + tk->buffstart -= halfsize; + } + } + + /* fallthrough */ + case TERMKEY_RES_EOF: + case TERMKEY_RES_ERROR: + return ret; + + case TERMKEY_RES_AGAIN: + if(!force) + again = 1; + + /* fallthrough */ + case TERMKEY_RES_NONE: + break; + } + } + + if(again) + return TERMKEY_RES_AGAIN; + + ret = peekkey_simple(tk, key, force, nbytep); + +#ifdef DEBUG + fprintf(stderr, "getkey_simple(force=%d) yields %s\n", force, res2str(ret)); + if(ret == TERMKEY_RES_KEY) { + print_key(tk, key); fprintf(stderr, "\n"); + } +#endif + + return ret; +} + +static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, size_t *nbytep) +{ + if(tk->buffcount == 0) + return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; + + unsigned char b0 = CHARAT(0); + + if(b0 == 0x1b) { + // Escape-prefixed value? Might therefore be Alt+key + if(tk->buffcount == 1) { + // This might be an <Esc> press, or it may want to be part of a longer + // sequence + if(!force) + return TERMKEY_RES_AGAIN; + + (*tk->method.emit_codepoint)(tk, b0, key); + *nbytep = 1; + return TERMKEY_RES_KEY; + } + + // Try another key there + tk->buffstart++; + tk->buffcount--; + + // Run the full driver + TermKeyResult metakey_result = peekkey(tk, key, force, nbytep); + + tk->buffstart--; + tk->buffcount++; + + switch(metakey_result) { + case TERMKEY_RES_KEY: + key->modifiers |= TERMKEY_KEYMOD_ALT; + (*nbytep)++; + break; + + case TERMKEY_RES_NONE: + case TERMKEY_RES_EOF: + case TERMKEY_RES_AGAIN: + case TERMKEY_RES_ERROR: + break; + } + + return metakey_result; + } + else if(b0 < 0xa0) { + // Single byte C0, G0 or C1 - C1 is never UTF-8 initial byte + (*tk->method.emit_codepoint)(tk, b0, key); + *nbytep = 1; + return TERMKEY_RES_KEY; + } + else if(tk->flags & TERMKEY_FLAG_UTF8) { + // Some UTF-8 + long codepoint; + TermKeyResult res = parse_utf8(tk->buffer + tk->buffstart, tk->buffcount, &codepoint, nbytep); + + if(res == TERMKEY_RES_AGAIN && force) { + /* There weren't enough bytes for a complete UTF-8 sequence but caller + * demands an answer. About the best thing we can do here is eat as many + * bytes as we have, and emit a UTF8_INVALID. If the remaining bytes + * arrive later, they'll be invalid too. + */ + codepoint = UTF8_INVALID; + *nbytep = tk->buffcount; + res = TERMKEY_RES_KEY; + } + + key->type = TERMKEY_TYPE_UNICODE; + key->modifiers = 0; + (*tk->method.emit_codepoint)(tk, codepoint, key); + return res; + } + else { + // Non UTF-8 case - just report the raw byte + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = b0; + key->modifiers = 0; + + key->utf8[0] = key->code.codepoint; + key->utf8[1] = 0; + + *nbytep = 1; + + return TERMKEY_RES_KEY; + } +} + +static TermKeyResult peekkey_mouse(TermKey *tk, TermKeyKey *key, size_t *nbytep) +{ + if(tk->buffcount < 3) + return TERMKEY_RES_AGAIN; + + key->type = TERMKEY_TYPE_MOUSE; + key->code.mouse[0] = CHARAT(0) - 0x20; + key->code.mouse[1] = CHARAT(1) - 0x20; + key->code.mouse[2] = CHARAT(2) - 0x20; + key->code.mouse[3] = 0; + + key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; + key->code.mouse[0] &= ~0x1c; + + *nbytep = 3; + return TERMKEY_RES_KEY; +} + +TermKeyResult termkey_getkey(TermKey *tk, TermKeyKey *key) +{ + size_t nbytes = 0; + TermKeyResult ret = peekkey(tk, key, 0, &nbytes); + + if(ret == TERMKEY_RES_KEY) + eat_bytes(tk, nbytes); + + if(ret == TERMKEY_RES_AGAIN) + /* Call peekkey() again in force mode to obtain whatever it can */ + (void)peekkey(tk, key, 1, &nbytes); + /* Don't eat it yet though */ + + return ret; +} + +TermKeyResult termkey_getkey_force(TermKey *tk, TermKeyKey *key) +{ + size_t nbytes = 0; + TermKeyResult ret = peekkey(tk, key, 1, &nbytes); + + if(ret == TERMKEY_RES_KEY) + eat_bytes(tk, nbytes); + + return ret; +} + +#ifndef _WIN32 +TermKeyResult termkey_waitkey(TermKey *tk, TermKeyKey *key) +{ + if(tk->fd == -1) { + errno = EBADF; + return TERMKEY_RES_ERROR; + } + + while(1) { + TermKeyResult ret = termkey_getkey(tk, key); + + switch(ret) { + case TERMKEY_RES_KEY: + case TERMKEY_RES_EOF: + case TERMKEY_RES_ERROR: + return ret; + + case TERMKEY_RES_NONE: + ret = termkey_advisereadable(tk); + if(ret == TERMKEY_RES_ERROR) + return ret; + break; + + case TERMKEY_RES_AGAIN: + { + if(tk->is_closed) + // We're closed now. Never going to get more bytes so just go with + // what we have + return termkey_getkey_force(tk, key); + + struct pollfd fd; + +retry: + fd.fd = tk->fd; + fd.events = POLLIN; + + int pollret = poll(&fd, 1, tk->waittime); + if(pollret == -1) { + if(errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) + goto retry; + + return TERMKEY_RES_ERROR; + } + + if(fd.revents & (POLLIN|POLLHUP|POLLERR)) + ret = termkey_advisereadable(tk); + else + ret = TERMKEY_RES_NONE; + + if(ret == TERMKEY_RES_ERROR) + return ret; + if(ret == TERMKEY_RES_NONE) + return termkey_getkey_force(tk, key); + } + break; + } + } + + /* UNREACHABLE */ +} +#endif + +TermKeyResult termkey_advisereadable(TermKey *tk) +{ + ssize_t len; + + if(tk->fd == -1) { + errno = EBADF; + return TERMKEY_RES_ERROR; + } + + if(tk->buffstart) { + memmove(tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); + tk->buffstart = 0; + } + + /* Not expecting it ever to be greater but doesn't hurt to handle that */ + if(tk->buffcount >= tk->buffsize) { + errno = ENOMEM; + return TERMKEY_RES_ERROR; + } + +retry: + len = read(tk->fd, tk->buffer + tk->buffcount, tk->buffsize - tk->buffcount); + + if(len == -1) { + if(errno == EAGAIN) + return TERMKEY_RES_NONE; + else if(errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) + goto retry; + else + return TERMKEY_RES_ERROR; + } + else if(len < 1) { + tk->is_closed = 1; + return TERMKEY_RES_NONE; + } + else { + tk->buffcount += len; + return TERMKEY_RES_AGAIN; + } +} + +size_t termkey_push_bytes(TermKey *tk, const char *bytes, size_t len) +{ + if(tk->buffstart) { + memmove(tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); + tk->buffstart = 0; + } + + /* Not expecting it ever to be greater but doesn't hurt to handle that */ + if(tk->buffcount >= tk->buffsize) { + errno = ENOMEM; + return (size_t)-1; + } + + if(len > tk->buffsize - tk->buffcount) + len = tk->buffsize - tk->buffcount; + + // memcpy(), not strncpy() in case of null bytes in input + memcpy(tk->buffer + tk->buffcount, bytes, len); + tk->buffcount += len; + + return len; +} + +TermKeySym termkey_register_keyname(TermKey *tk, TermKeySym sym, const char *name) +{ + if(!sym) + sym = tk->nkeynames; + + if(sym >= tk->nkeynames) { + const char **new_keynames = realloc(tk->keynames, sizeof(new_keynames[0]) * (sym + 1)); + if(!new_keynames) + return -1; + + tk->keynames = new_keynames; + + // Fill in the hole + for(int i = tk->nkeynames; i < sym; i++) + tk->keynames[i] = NULL; + + tk->nkeynames = sym + 1; + } + + tk->keynames[sym] = name; + + return sym; +} + +const char *termkey_get_keyname(TermKey *tk, TermKeySym sym) +{ + if(sym == TERMKEY_SYM_UNKNOWN) + return "UNKNOWN"; + + if(sym < tk->nkeynames) + return tk->keynames[sym]; + + return "UNKNOWN"; +} + +static const char *termkey_lookup_keyname_format(TermKey *tk, const char *str, TermKeySym *sym, TermKeyFormat format) +{ + /* We store an array, so we can't do better than a linear search. Doesn't + * matter because user won't be calling this too often */ + + for(*sym = 0; *sym < tk->nkeynames; (*sym)++) { + const char *thiskey = tk->keynames[*sym]; + if(!thiskey) + continue; + size_t len = strlen(thiskey); + if(format & TERMKEY_FORMAT_LOWERSPACE) { + const char *thisstr = str; + if(strpncmp_camel(&thisstr, &thiskey, len) == 0) + return thisstr; + } + else { + if(strncmp(str, thiskey, len) == 0) + return (char *)str + len; + } + } + + return NULL; +} + +const char *termkey_lookup_keyname(TermKey *tk, const char *str, TermKeySym *sym) +{ + return termkey_lookup_keyname_format(tk, str, sym, 0); +} + +TermKeySym termkey_keyname2sym(TermKey *tk, const char *keyname) +{ + TermKeySym sym; + const char *endp = termkey_lookup_keyname(tk, keyname, &sym); + if(!endp || endp[0]) + return TERMKEY_SYM_UNKNOWN; + return sym; +} + +static TermKeySym register_c0(TermKey *tk, TermKeySym sym, unsigned char ctrl, const char *name) +{ + return register_c0_full(tk, sym, 0, 0, ctrl, name); +} + +static TermKeySym register_c0_full(TermKey *tk, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name) +{ + if(ctrl >= 0x20) { + errno = EINVAL; + return -1; + } + + if(name) + sym = termkey_register_keyname(tk, sym, name); + + tk->c0[ctrl].sym = sym; + tk->c0[ctrl].modifier_set = modifier_set; + tk->c0[ctrl].modifier_mask = modifier_mask; + + return sym; +} + +/* Previous name for this function + * No longer declared in termkey.h but it remains in the compiled library for + * backward-compatibility reasons. + */ +size_t termkey_snprint_key(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format) +{ + return termkey_strfkey(tk, buffer, len, key, format); +} + +static struct modnames { + const char *shift, *alt, *ctrl; +} +modnames[] = { + { "S", "A", "C" }, // 0 + { "Shift", "Alt", "Ctrl" }, // LONGMOD + { "S", "M", "C" }, // ALTISMETA + { "Shift", "Meta", "Ctrl" }, // ALTISMETA+LONGMOD + { "s", "a", "c" }, // LOWERMOD + { "shift", "alt", "ctrl" }, // LOWERMOD+LONGMOD + { "s", "m", "c" }, // LOWERMOD+ALTISMETA + { "shift", "meta", "ctrl" }, // LOWERMOD+ALTISMETA+LONGMOD +}; + +size_t termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format) +{ + size_t pos = 0; + size_t l = 0; + + struct modnames *mods = &modnames[!!(format & TERMKEY_FORMAT_LONGMOD) + + !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + + !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; + + int wrapbracket = (format & TERMKEY_FORMAT_WRAPBRACKET) && + (key->type != TERMKEY_TYPE_UNICODE || key->modifiers != 0); + + char sep = (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'; + + if(format & TERMKEY_FORMAT_CARETCTRL && + key->type == TERMKEY_TYPE_UNICODE && + key->modifiers == TERMKEY_KEYMOD_CTRL) { + long codepoint = key->code.codepoint; + + // Handle some of the special cases first + if(codepoint >= 'a' && codepoint <= 'z') { + l = snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", (char)codepoint - 0x20); + if(l <= 0) return pos; + pos += l; + return pos; + } + else if((codepoint >= '@' && codepoint < 'A') || + (codepoint > 'Z' && codepoint <= '_')) { + l = snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", (char)codepoint); + if(l <= 0) return pos; + pos += l; + return pos; + } + } + + if(wrapbracket) { + l = snprintf(buffer + pos, len - pos, "<"); + if(l <= 0) return pos; + pos += l; + } + + if(key->modifiers & TERMKEY_KEYMOD_ALT) { + l = snprintf(buffer + pos, len - pos, "%s%c", mods->alt, sep); + if(l <= 0) return pos; + pos += l; + } + + if(key->modifiers & TERMKEY_KEYMOD_CTRL) { + l = snprintf(buffer + pos, len - pos, "%s%c", mods->ctrl, sep); + if(l <= 0) return pos; + pos += l; + } + + if(key->modifiers & TERMKEY_KEYMOD_SHIFT) { + l = snprintf(buffer + pos, len - pos, "%s%c", mods->shift, sep); + if(l <= 0) return pos; + pos += l; + } + + switch(key->type) { + case TERMKEY_TYPE_UNICODE: + if(!key->utf8[0]) // In case of user-supplied key structures + fill_utf8(key); + l = snprintf(buffer + pos, len - pos, "%s", key->utf8); + break; + case TERMKEY_TYPE_KEYSYM: + { + const char *name = termkey_get_keyname(tk, key->code.sym); + if(format & TERMKEY_FORMAT_LOWERSPACE) + l = snprint_cameltospaces(buffer + pos, len - pos, name); + else + l = snprintf(buffer + pos, len - pos, "%s", name); + } + break; + case TERMKEY_TYPE_FUNCTION: + l = snprintf(buffer + pos, len - pos, "%c%d", + (format & TERMKEY_FORMAT_LOWERSPACE ? 'f' : 'F'), key->code.number); + break; + case TERMKEY_TYPE_MOUSE: + { + TermKeyMouseEvent ev; + int button; + int line, col; + termkey_interpret_mouse(tk, key, &ev, &button, &line, &col); + + l = snprintf(buffer + pos, len - pos, "Mouse%s(%d)", + evnames[ev], button); + + if(format & TERMKEY_FORMAT_MOUSE_POS) { + if(l <= 0) return pos; + pos += l; + + l = snprintf(buffer + pos, len - pos, " @ (%u,%u)", col, line); + } + } + break; + case TERMKEY_TYPE_POSITION: + l = snprintf(buffer + pos, len - pos, "Position"); + break; + case TERMKEY_TYPE_MODEREPORT: + { + int initial, mode, value; + termkey_interpret_modereport(tk, key, &initial, &mode, &value); + if(initial) + l = snprintf(buffer + pos, len - pos, "Mode(%c%d=%d)", initial, mode, value); + else + l = snprintf(buffer + pos, len - pos, "Mode(%d=%d)", mode, value); + } + case TERMKEY_TYPE_DCS: + l = snprintf(buffer + pos, len - pos, "DCS"); + break; + case TERMKEY_TYPE_OSC: + l = snprintf(buffer + pos, len - pos, "OSC"); + break; + case TERMKEY_TYPE_UNKNOWN_CSI: + l = snprintf(buffer + pos, len - pos, "CSI %c", key->code.number & 0xff); + break; + } + + if(l <= 0) return pos; + pos += l; + + if(wrapbracket) { + l = snprintf(buffer + pos, len - pos, ">"); + if(l <= 0) return pos; + pos += l; + } + + return pos; +} + +const char *termkey_strpkey(TermKey *tk, const char *str, TermKeyKey *key, TermKeyFormat format) +{ + struct modnames *mods = &modnames[!!(format & TERMKEY_FORMAT_LONGMOD) + + !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + + !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; + + key->modifiers = 0; + + if((format & TERMKEY_FORMAT_CARETCTRL) && str[0] == '^' && str[1]) { + str = termkey_strpkey(tk, str+1, key, format & ~TERMKEY_FORMAT_CARETCTRL); + + if(!str || + key->type != TERMKEY_TYPE_UNICODE || + key->code.codepoint < '@' || key->code.codepoint > '_' || + key->modifiers != 0) + return NULL; + + if(key->code.codepoint >= 'A' && key->code.codepoint <= 'Z') + key->code.codepoint += 0x20; + key->modifiers = TERMKEY_KEYMOD_CTRL; + fill_utf8(key); + return (char *)str; + } + + const char *sep_at; + + while((sep_at = strchr(str, (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'))) { + size_t n = sep_at - str; + + if(n == strlen(mods->alt) && strncmp(mods->alt, str, n) == 0) + key->modifiers |= TERMKEY_KEYMOD_ALT; + else if(n == strlen(mods->ctrl) && strncmp(mods->ctrl, str, n) == 0) + key->modifiers |= TERMKEY_KEYMOD_CTRL; + else if(n == strlen(mods->shift) && strncmp(mods->shift, str, n) == 0) + key->modifiers |= TERMKEY_KEYMOD_SHIFT; + + else + break; + + str = sep_at + 1; + } + + size_t nbytes; + ssize_t snbytes; + const char *endstr; + int button; + char event_name[32]; + + if((endstr = termkey_lookup_keyname_format(tk, str, &key->code.sym, format))) { + key->type = TERMKEY_TYPE_KEYSYM; + str = endstr; + } + else if(sscanf(str, "F%d%zn", &key->code.number, &snbytes) == 1) { + key->type = TERMKEY_TYPE_FUNCTION; + str += snbytes; + } + else if(sscanf(str, "Mouse%31[^(](%d)%zn", event_name, &button, &snbytes) == 2) { + str += snbytes; + key->type = TERMKEY_TYPE_MOUSE; + + TermKeyMouseEvent ev = TERMKEY_MOUSE_UNKNOWN; + for(size_t i = 0; i < sizeof(evnames)/sizeof(evnames[0]); i++) { + if(strcmp(evnames[i], event_name) == 0) { + ev = TERMKEY_MOUSE_UNKNOWN + i; + break; + } + } + + int code; + switch(ev) { + case TERMKEY_MOUSE_PRESS: + case TERMKEY_MOUSE_DRAG: + code = button - 1; + if(ev == TERMKEY_MOUSE_DRAG) { + code |= 0x20; + } + break; + case TERMKEY_MOUSE_RELEASE: + code = 3; + break; + default: + code = 128; + break; + } + key->code.mouse[0] = code; + + unsigned int line = 0, col = 0; + if((format & TERMKEY_FORMAT_MOUSE_POS) && sscanf(str, " @ (%u,%u)%zn", &col, &line, &snbytes) == 2) { + str += snbytes; + } + termkey_key_set_linecol(key, col, line); + } + // Unicode must be last + else if(parse_utf8((unsigned const char *)str, strlen(str), &key->code.codepoint, &nbytes) == TERMKEY_RES_KEY) { + key->type = TERMKEY_TYPE_UNICODE; + fill_utf8(key); + str += nbytes; + } + else + return NULL; + + termkey_canonicalise(tk, key); + + return (char *)str; +} + +int termkey_keycmp(TermKey *tk, const TermKeyKey *key1p, const TermKeyKey *key2p) +{ + /* Copy the key structs since we'll be modifying them */ + TermKeyKey key1 = *key1p, key2 = *key2p; + + termkey_canonicalise(tk, &key1); + termkey_canonicalise(tk, &key2); + + if(key1.type != key2.type) + return key1.type - key2.type; + + switch(key1.type) { + case TERMKEY_TYPE_UNICODE: + if(key1.code.codepoint != key2.code.codepoint) + return key1.code.codepoint - key2.code.codepoint; + break; + case TERMKEY_TYPE_KEYSYM: + if(key1.code.sym != key2.code.sym) + return key1.code.sym - key2.code.sym; + break; + case TERMKEY_TYPE_FUNCTION: + case TERMKEY_TYPE_UNKNOWN_CSI: + if(key1.code.number != key2.code.number) + return key1.code.number - key2.code.number; + break; + case TERMKEY_TYPE_MOUSE: + { + int cmp = strncmp(key1.code.mouse, key2.code.mouse, 4); + if(cmp != 0) + return cmp; + } + break; + case TERMKEY_TYPE_POSITION: + { + int line1, col1, line2, col2; + termkey_interpret_position(tk, &key1, &line1, &col1); + termkey_interpret_position(tk, &key2, &line2, &col2); + if(line1 != line2) + return line1 - line2; + return col1 - col2; + } + break; + case TERMKEY_TYPE_DCS: + case TERMKEY_TYPE_OSC: + return key1p - key2p; + case TERMKEY_TYPE_MODEREPORT: + { + int initial1, initial2, mode1, mode2, value1, value2; + termkey_interpret_modereport(tk, &key1, &initial1, &mode1, &value1); + termkey_interpret_modereport(tk, &key2, &initial2, &mode2, &value2); + if(initial1 != initial2) + return initial1 - initial2; + if(mode1 != mode2) + return mode1 - mode2; + return value1 - value2; + } + } + + return key1.modifiers - key2.modifiers; +} diff --git a/src/termkey/termkey.h b/src/termkey/termkey.h new file mode 100644 index 0000000000..8e10fcff0c --- /dev/null +++ b/src/termkey/termkey.h @@ -0,0 +1,249 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GUARD_TERMKEY_H_ +#define GUARD_TERMKEY_H_ + +#include <stdint.h> +#include <stdlib.h> + +#define TERMKEY_VERSION_MAJOR 0 +#define TERMKEY_VERSION_MINOR 22 + +#define TERMKEY_CHECK_VERSION \ + termkey_check_version(TERMKEY_VERSION_MAJOR, TERMKEY_VERSION_MINOR) + +typedef enum { + TERMKEY_SYM_UNKNOWN = -1, + TERMKEY_SYM_NONE = 0, + + /* Special names in C0 */ + TERMKEY_SYM_BACKSPACE, + TERMKEY_SYM_TAB, + TERMKEY_SYM_ENTER, + TERMKEY_SYM_ESCAPE, + + /* Special names in G0 */ + TERMKEY_SYM_SPACE, + TERMKEY_SYM_DEL, + + /* Special keys */ + TERMKEY_SYM_UP, + TERMKEY_SYM_DOWN, + TERMKEY_SYM_LEFT, + TERMKEY_SYM_RIGHT, + TERMKEY_SYM_BEGIN, + TERMKEY_SYM_FIND, + TERMKEY_SYM_INSERT, + TERMKEY_SYM_DELETE, + TERMKEY_SYM_SELECT, + TERMKEY_SYM_PAGEUP, + TERMKEY_SYM_PAGEDOWN, + TERMKEY_SYM_HOME, + TERMKEY_SYM_END, + + /* Special keys from terminfo */ + TERMKEY_SYM_CANCEL, + TERMKEY_SYM_CLEAR, + TERMKEY_SYM_CLOSE, + TERMKEY_SYM_COMMAND, + TERMKEY_SYM_COPY, + TERMKEY_SYM_EXIT, + TERMKEY_SYM_HELP, + TERMKEY_SYM_MARK, + TERMKEY_SYM_MESSAGE, + TERMKEY_SYM_MOVE, + TERMKEY_SYM_OPEN, + TERMKEY_SYM_OPTIONS, + TERMKEY_SYM_PRINT, + TERMKEY_SYM_REDO, + TERMKEY_SYM_REFERENCE, + TERMKEY_SYM_REFRESH, + TERMKEY_SYM_REPLACE, + TERMKEY_SYM_RESTART, + TERMKEY_SYM_RESUME, + TERMKEY_SYM_SAVE, + TERMKEY_SYM_SUSPEND, + TERMKEY_SYM_UNDO, + + /* Numeric keypad special keys */ + TERMKEY_SYM_KP0, + TERMKEY_SYM_KP1, + TERMKEY_SYM_KP2, + TERMKEY_SYM_KP3, + TERMKEY_SYM_KP4, + TERMKEY_SYM_KP5, + TERMKEY_SYM_KP6, + TERMKEY_SYM_KP7, + TERMKEY_SYM_KP8, + TERMKEY_SYM_KP9, + TERMKEY_SYM_KPENTER, + TERMKEY_SYM_KPPLUS, + TERMKEY_SYM_KPMINUS, + TERMKEY_SYM_KPMULT, + TERMKEY_SYM_KPDIV, + TERMKEY_SYM_KPCOMMA, + TERMKEY_SYM_KPPERIOD, + TERMKEY_SYM_KPEQUALS, + + /* et cetera ad nauseum */ + TERMKEY_N_SYMS +} TermKeySym; + +typedef enum { + TERMKEY_TYPE_UNICODE, + TERMKEY_TYPE_FUNCTION, + TERMKEY_TYPE_KEYSYM, + TERMKEY_TYPE_MOUSE, + TERMKEY_TYPE_POSITION, + TERMKEY_TYPE_MODEREPORT, + TERMKEY_TYPE_DCS, + TERMKEY_TYPE_OSC, + /* add other recognised types here */ + + TERMKEY_TYPE_UNKNOWN_CSI = -1 +} TermKeyType; + +typedef enum { + TERMKEY_RES_NONE, + TERMKEY_RES_KEY, + TERMKEY_RES_EOF, + TERMKEY_RES_AGAIN, + TERMKEY_RES_ERROR +} TermKeyResult; + +typedef enum { + TERMKEY_MOUSE_UNKNOWN, + TERMKEY_MOUSE_PRESS, + TERMKEY_MOUSE_DRAG, + TERMKEY_MOUSE_RELEASE +} TermKeyMouseEvent; + +enum { + TERMKEY_KEYMOD_SHIFT = 1 << 0, + TERMKEY_KEYMOD_ALT = 1 << 1, + TERMKEY_KEYMOD_CTRL = 1 << 2 +}; + +typedef struct { + TermKeyType type; + union { + long codepoint; /* TERMKEY_TYPE_UNICODE */ + int number; /* TERMKEY_TYPE_FUNCTION */ + TermKeySym sym; /* TERMKEY_TYPE_KEYSYM */ + char mouse[4]; /* TERMKEY_TYPE_MOUSE */ + /* opaque. see termkey_interpret_mouse */ + } code; + + int modifiers; + + /* Any Unicode character can be UTF-8 encoded in no more than 6 bytes, plus + * terminating NUL */ + char utf8[7]; +} TermKeyKey; + +typedef struct TermKey TermKey; + +enum { + TERMKEY_FLAG_NOINTERPRET = 1 << 0, /* Do not interpret C0//DEL codes if possible */ + TERMKEY_FLAG_CONVERTKP = 1 << 1, /* Convert KP codes to regular keypresses */ + TERMKEY_FLAG_RAW = 1 << 2, /* Input is raw bytes, not UTF-8 */ + TERMKEY_FLAG_UTF8 = 1 << 3, /* Input is definitely UTF-8 */ + TERMKEY_FLAG_NOTERMIOS = 1 << 4, /* Do not make initial termios calls on construction */ + TERMKEY_FLAG_SPACESYMBOL = 1 << 5, /* Sets TERMKEY_CANON_SPACESYMBOL */ + TERMKEY_FLAG_CTRLC = 1 << 6, /* Allow Ctrl-C to be read as normal, disabling SIGINT */ + TERMKEY_FLAG_EINTR = 1 << 7, /* Return ERROR on signal (EINTR) rather than retry */ + TERMKEY_FLAG_NOSTART = 1 << 8 /* Do not call termkey_start() in constructor */ +}; + +enum { + TERMKEY_CANON_SPACESYMBOL = 1 << 0, /* Space is symbolic rather than Unicode */ + TERMKEY_CANON_DELBS = 1 << 1 /* Del is converted to Backspace */ +}; + +void termkey_check_version(int major, int minor); + +TermKey *termkey_new(int fd, int flags); +TermKey *termkey_new_abstract(const char *term, int flags); +void termkey_free(TermKey *tk); +void termkey_destroy(TermKey *tk); + +/* Mostly-undocumented hooks for doing evil evil things */ +typedef const char *TermKey_Terminfo_Getstr_Hook(const char *name, const char *value, void *data); +void termkey_hook_terminfo_getstr(TermKey *tk, TermKey_Terminfo_Getstr_Hook *hookfn, void *data); + +int termkey_start(TermKey *tk); +int termkey_stop(TermKey *tk); +int termkey_is_started(TermKey *tk); + +int termkey_get_fd(TermKey *tk); + +int termkey_get_flags(TermKey *tk); +void termkey_set_flags(TermKey *tk, int newflags); + +int termkey_get_waittime(TermKey *tk); +void termkey_set_waittime(TermKey *tk, int msec); + +int termkey_get_canonflags(TermKey *tk); +void termkey_set_canonflags(TermKey *tk, int); + +size_t termkey_get_buffer_size(TermKey *tk); +int termkey_set_buffer_size(TermKey *tk, size_t size); + +size_t termkey_get_buffer_remaining(TermKey *tk); + +void termkey_canonicalise(TermKey *tk, TermKeyKey *key); + +TermKeyResult termkey_getkey(TermKey *tk, TermKeyKey *key); +TermKeyResult termkey_getkey_force(TermKey *tk, TermKeyKey *key); +TermKeyResult termkey_waitkey(TermKey *tk, TermKeyKey *key); + +TermKeyResult termkey_advisereadable(TermKey *tk); + +size_t termkey_push_bytes(TermKey *tk, const char *bytes, size_t len); + +TermKeySym termkey_register_keyname(TermKey *tk, TermKeySym sym, const char *name); +const char *termkey_get_keyname(TermKey *tk, TermKeySym sym); +const char *termkey_lookup_keyname(TermKey *tk, const char *str, TermKeySym *sym); + +TermKeySym termkey_keyname2sym(TermKey *tk, const char *keyname); + +TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKeyMouseEvent *event, int *button, int *line, int *col); + +TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int *line, int *col); + +TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial, int *mode, int *value); + +TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long args[], size_t *nargs, unsigned long *cmd); + +TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp); + +typedef enum { + TERMKEY_FORMAT_LONGMOD = 1 << 0, /* Shift-... instead of S-... */ + TERMKEY_FORMAT_CARETCTRL = 1 << 1, /* ^X instead of C-X */ + TERMKEY_FORMAT_ALTISMETA = 1 << 2, /* Meta- or M- instead of Alt- or A- */ + TERMKEY_FORMAT_WRAPBRACKET = 1 << 3, /* Wrap special keys in brackets like <Escape> */ + TERMKEY_FORMAT_SPACEMOD = 1 << 4, /* M Foo instead of M-Foo */ + TERMKEY_FORMAT_LOWERMOD = 1 << 5, /* meta or m instead of Meta or M */ + TERMKEY_FORMAT_LOWERSPACE = 1 << 6, /* page down instead of PageDown */ + + TERMKEY_FORMAT_MOUSE_POS = 1 << 8 /* Include mouse position if relevant; @ col,line */ +} TermKeyFormat; + +/* Some useful combinations */ + +#define TERMKEY_FORMAT_VIM (TermKeyFormat)(TERMKEY_FORMAT_ALTISMETA|TERMKEY_FORMAT_WRAPBRACKET) +#define TERMKEY_FORMAT_URWID (TermKeyFormat)(TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_ALTISMETA| \ + TERMKEY_FORMAT_LOWERMOD|TERMKEY_FORMAT_SPACEMOD|TERMKEY_FORMAT_LOWERSPACE) + +size_t termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format); +const char *termkey_strpkey(TermKey *tk, const char *str, TermKeyKey *key, TermKeyFormat format); + +int termkey_keycmp(TermKey *tk, const TermKeyKey *key1, const TermKeyKey *key2); + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg index ffd4955829..aaac6e4426 100644 --- a/src/uncrustify.cfg +++ b/src/uncrustify.cfg @@ -3709,6 +3709,7 @@ set CLASS_COLON FUNC_API_FAST set CLASS_COLON FUNC_API_LUA_ONLY set CLASS_COLON FUNC_API_NOEXPORT set CLASS_COLON FUNC_API_REMOTE_ONLY +set CLASS_COLON FUNC_API_RET_ALLOC set CLASS_COLON FUNC_API_SINCE set CLASS_COLON FUNC_API_TEXTLOCK set CLASS_COLON FUNC_API_TEXTLOCK_ALLOW_CMDWIN diff --git a/src/unicode/CaseFolding.txt b/src/unicode/CaseFolding.txt index 65aa0fcd6b..69c5c64b4c 100644 --- a/src/unicode/CaseFolding.txt +++ b/src/unicode/CaseFolding.txt @@ -1,6 +1,6 @@ -# CaseFolding-15.0.0.txt -# Date: 2022-02-02, 23:35:35 GMT -# © 2022 Unicode®, Inc. +# CaseFolding-15.1.0.txt +# Date: 2023-05-12, 21:53:10 GMT +# © 2023 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # For terms of use, see https://www.unicode.org/terms_of_use.html # @@ -929,6 +929,7 @@ 1FCC; S; 1FC3; # GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI 1FD2; F; 03B9 0308 0300; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA 1FD3; F; 03B9 0308 0301; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA +1FD3; S; 0390; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA 1FD6; F; 03B9 0342; # GREEK SMALL LETTER IOTA WITH PERISPOMENI 1FD7; F; 03B9 0308 0342; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI 1FD8; C; 1FD0; # GREEK CAPITAL LETTER IOTA WITH VRACHY @@ -937,6 +938,7 @@ 1FDB; C; 1F77; # GREEK CAPITAL LETTER IOTA WITH OXIA 1FE2; F; 03C5 0308 0300; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA 1FE3; F; 03C5 0308 0301; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA +1FE3; S; 03B0; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA 1FE4; F; 03C1 0313; # GREEK SMALL LETTER RHO WITH PSILI 1FE6; F; 03C5 0342; # GREEK SMALL LETTER UPSILON WITH PERISPOMENI 1FE7; F; 03C5 0308 0342; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI @@ -1328,6 +1330,7 @@ FB02; F; 0066 006C; # LATIN SMALL LIGATURE FL FB03; F; 0066 0066 0069; # LATIN SMALL LIGATURE FFI FB04; F; 0066 0066 006C; # LATIN SMALL LIGATURE FFL FB05; F; 0073 0074; # LATIN SMALL LIGATURE LONG S T +FB05; S; FB06; # LATIN SMALL LIGATURE LONG S T FB06; F; 0073 0074; # LATIN SMALL LIGATURE ST FB13; F; 0574 0576; # ARMENIAN SMALL LIGATURE MEN NOW FB14; F; 0574 0565; # ARMENIAN SMALL LIGATURE MEN ECH diff --git a/src/unicode/EastAsianWidth.txt b/src/unicode/EastAsianWidth.txt index 38b7076c02..02df4df475 100644 --- a/src/unicode/EastAsianWidth.txt +++ b/src/unicode/EastAsianWidth.txt @@ -1,11 +1,11 @@ -# EastAsianWidth-15.0.0.txt -# Date: 2022-05-24, 17:40:20 GMT [KW, LI] -# © 2022 Unicode®, Inc. +# EastAsianWidth-15.1.0.txt +# Date: 2023-07-28, 23:34:08 GMT +# © 2023 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # For terms of use, see https://www.unicode.org/terms_of_use.html # # Unicode Character Database -# For documentation, see https://www.unicode.org/reports/tr44/ +# For documentation, see https://www.unicode.org/reports/tr44/ # # East_Asian_Width Property # @@ -30,2590 +30,2592 @@ # Character ranges are specified as for other property files in the # Unicode Character Database. # -# For legacy reasons, there are no spaces before or after the semicolon -# which separates the two fields. The comments following the number sign -# "#" list the General_Category property value or the L& alias of the -# derived value LC, the Unicode character name or names, and, in lines -# with ranges of code points, the code point count in square brackets. +# The comments following the number sign "#" list the General_Category +# property value or the L& alias of the derived value LC, the Unicode +# character name or names, and, in lines with ranges of code points, +# the code point count in square brackets. # # For more information, see UAX #11: East Asian Width, # at https://www.unicode.org/reports/tr11/ # # @missing: 0000..10FFFF; N -0000..001F;N # Cc [32] <control-0000>..<control-001F> -0020;Na # Zs SPACE -0021..0023;Na # Po [3] EXCLAMATION MARK..NUMBER SIGN -0024;Na # Sc DOLLAR SIGN -0025..0027;Na # Po [3] PERCENT SIGN..APOSTROPHE -0028;Na # Ps LEFT PARENTHESIS -0029;Na # Pe RIGHT PARENTHESIS -002A;Na # Po ASTERISK -002B;Na # Sm PLUS SIGN -002C;Na # Po COMMA -002D;Na # Pd HYPHEN-MINUS -002E..002F;Na # Po [2] FULL STOP..SOLIDUS -0030..0039;Na # Nd [10] DIGIT ZERO..DIGIT NINE -003A..003B;Na # Po [2] COLON..SEMICOLON -003C..003E;Na # Sm [3] LESS-THAN SIGN..GREATER-THAN SIGN -003F..0040;Na # Po [2] QUESTION MARK..COMMERCIAL AT -0041..005A;Na # Lu [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z -005B;Na # Ps LEFT SQUARE BRACKET -005C;Na # Po REVERSE SOLIDUS -005D;Na # Pe RIGHT SQUARE BRACKET -005E;Na # Sk CIRCUMFLEX ACCENT -005F;Na # Pc LOW LINE -0060;Na # Sk GRAVE ACCENT -0061..007A;Na # Ll [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z -007B;Na # Ps LEFT CURLY BRACKET -007C;Na # Sm VERTICAL LINE -007D;Na # Pe RIGHT CURLY BRACKET -007E;Na # Sm TILDE -007F;N # Cc <control-007F> -0080..009F;N # Cc [32] <control-0080>..<control-009F> -00A0;N # Zs NO-BREAK SPACE -00A1;A # Po INVERTED EXCLAMATION MARK -00A2..00A3;Na # Sc [2] CENT SIGN..POUND SIGN -00A4;A # Sc CURRENCY SIGN -00A5;Na # Sc YEN SIGN -00A6;Na # So BROKEN BAR -00A7;A # Po SECTION SIGN -00A8;A # Sk DIAERESIS -00A9;N # So COPYRIGHT SIGN -00AA;A # Lo FEMININE ORDINAL INDICATOR -00AB;N # Pi LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -00AC;Na # Sm NOT SIGN -00AD;A # Cf SOFT HYPHEN -00AE;A # So REGISTERED SIGN -00AF;Na # Sk MACRON -00B0;A # So DEGREE SIGN -00B1;A # Sm PLUS-MINUS SIGN -00B2..00B3;A # No [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE -00B4;A # Sk ACUTE ACCENT -00B5;N # Ll MICRO SIGN -00B6..00B7;A # Po [2] PILCROW SIGN..MIDDLE DOT -00B8;A # Sk CEDILLA -00B9;A # No SUPERSCRIPT ONE -00BA;A # Lo MASCULINE ORDINAL INDICATOR -00BB;N # Pf RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -00BC..00BE;A # No [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS -00BF;A # Po INVERTED QUESTION MARK -00C0..00C5;N # Lu [6] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER A WITH RING ABOVE -00C6;A # Lu LATIN CAPITAL LETTER AE -00C7..00CF;N # Lu [9] LATIN CAPITAL LETTER C WITH CEDILLA..LATIN CAPITAL LETTER I WITH DIAERESIS -00D0;A # Lu LATIN CAPITAL LETTER ETH -00D1..00D6;N # Lu [6] LATIN CAPITAL LETTER N WITH TILDE..LATIN CAPITAL LETTER O WITH DIAERESIS -00D7;A # Sm MULTIPLICATION SIGN -00D8;A # Lu LATIN CAPITAL LETTER O WITH STROKE -00D9..00DD;N # Lu [5] LATIN CAPITAL LETTER U WITH GRAVE..LATIN CAPITAL LETTER Y WITH ACUTE -00DE..00E1;A # L& [4] LATIN CAPITAL LETTER THORN..LATIN SMALL LETTER A WITH ACUTE -00E2..00E5;N # Ll [4] LATIN SMALL LETTER A WITH CIRCUMFLEX..LATIN SMALL LETTER A WITH RING ABOVE -00E6;A # Ll LATIN SMALL LETTER AE -00E7;N # Ll LATIN SMALL LETTER C WITH CEDILLA -00E8..00EA;A # Ll [3] LATIN SMALL LETTER E WITH GRAVE..LATIN SMALL LETTER E WITH CIRCUMFLEX -00EB;N # Ll LATIN SMALL LETTER E WITH DIAERESIS -00EC..00ED;A # Ll [2] LATIN SMALL LETTER I WITH GRAVE..LATIN SMALL LETTER I WITH ACUTE -00EE..00EF;N # Ll [2] LATIN SMALL LETTER I WITH CIRCUMFLEX..LATIN SMALL LETTER I WITH DIAERESIS -00F0;A # Ll LATIN SMALL LETTER ETH -00F1;N # Ll LATIN SMALL LETTER N WITH TILDE -00F2..00F3;A # Ll [2] LATIN SMALL LETTER O WITH GRAVE..LATIN SMALL LETTER O WITH ACUTE -00F4..00F6;N # Ll [3] LATIN SMALL LETTER O WITH CIRCUMFLEX..LATIN SMALL LETTER O WITH DIAERESIS -00F7;A # Sm DIVISION SIGN -00F8..00FA;A # Ll [3] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER U WITH ACUTE -00FB;N # Ll LATIN SMALL LETTER U WITH CIRCUMFLEX -00FC;A # Ll LATIN SMALL LETTER U WITH DIAERESIS -00FD;N # Ll LATIN SMALL LETTER Y WITH ACUTE -00FE;A # Ll LATIN SMALL LETTER THORN -00FF;N # Ll LATIN SMALL LETTER Y WITH DIAERESIS -0100;N # Lu LATIN CAPITAL LETTER A WITH MACRON -0101;A # Ll LATIN SMALL LETTER A WITH MACRON -0102..0110;N # L& [15] LATIN CAPITAL LETTER A WITH BREVE..LATIN CAPITAL LETTER D WITH STROKE -0111;A # Ll LATIN SMALL LETTER D WITH STROKE -0112;N # Lu LATIN CAPITAL LETTER E WITH MACRON -0113;A # Ll LATIN SMALL LETTER E WITH MACRON -0114..011A;N # L& [7] LATIN CAPITAL LETTER E WITH BREVE..LATIN CAPITAL LETTER E WITH CARON -011B;A # Ll LATIN SMALL LETTER E WITH CARON -011C..0125;N # L& [10] LATIN CAPITAL LETTER G WITH CIRCUMFLEX..LATIN SMALL LETTER H WITH CIRCUMFLEX -0126..0127;A # L& [2] LATIN CAPITAL LETTER H WITH STROKE..LATIN SMALL LETTER H WITH STROKE -0128..012A;N # L& [3] LATIN CAPITAL LETTER I WITH TILDE..LATIN CAPITAL LETTER I WITH MACRON -012B;A # Ll LATIN SMALL LETTER I WITH MACRON -012C..0130;N # L& [5] LATIN CAPITAL LETTER I WITH BREVE..LATIN CAPITAL LETTER I WITH DOT ABOVE -0131..0133;A # L& [3] LATIN SMALL LETTER DOTLESS I..LATIN SMALL LIGATURE IJ -0134..0137;N # L& [4] LATIN CAPITAL LETTER J WITH CIRCUMFLEX..LATIN SMALL LETTER K WITH CEDILLA -0138;A # Ll LATIN SMALL LETTER KRA -0139..013E;N # L& [6] LATIN CAPITAL LETTER L WITH ACUTE..LATIN SMALL LETTER L WITH CARON -013F..0142;A # L& [4] LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATIN SMALL LETTER L WITH STROKE -0143;N # Lu LATIN CAPITAL LETTER N WITH ACUTE -0144;A # Ll LATIN SMALL LETTER N WITH ACUTE -0145..0147;N # L& [3] LATIN CAPITAL LETTER N WITH CEDILLA..LATIN CAPITAL LETTER N WITH CARON -0148..014B;A # L& [4] LATIN SMALL LETTER N WITH CARON..LATIN SMALL LETTER ENG -014C;N # Lu LATIN CAPITAL LETTER O WITH MACRON -014D;A # Ll LATIN SMALL LETTER O WITH MACRON -014E..0151;N # L& [4] LATIN CAPITAL LETTER O WITH BREVE..LATIN SMALL LETTER O WITH DOUBLE ACUTE -0152..0153;A # L& [2] LATIN CAPITAL LIGATURE OE..LATIN SMALL LIGATURE OE -0154..0165;N # L& [18] LATIN CAPITAL LETTER R WITH ACUTE..LATIN SMALL LETTER T WITH CARON -0166..0167;A # L& [2] LATIN CAPITAL LETTER T WITH STROKE..LATIN SMALL LETTER T WITH STROKE -0168..016A;N # L& [3] LATIN CAPITAL LETTER U WITH TILDE..LATIN CAPITAL LETTER U WITH MACRON -016B;A # Ll LATIN SMALL LETTER U WITH MACRON -016C..017F;N # L& [20] LATIN CAPITAL LETTER U WITH BREVE..LATIN SMALL LETTER LONG S -0180..01BA;N # L& [59] LATIN SMALL LETTER B WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL -01BB;N # Lo LATIN LETTER TWO WITH STROKE -01BC..01BF;N # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN -01C0..01C3;N # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK -01C4..01CD;N # L& [10] LATIN CAPITAL LETTER DZ WITH CARON..LATIN CAPITAL LETTER A WITH CARON -01CE;A # Ll LATIN SMALL LETTER A WITH CARON -01CF;N # Lu LATIN CAPITAL LETTER I WITH CARON -01D0;A # Ll LATIN SMALL LETTER I WITH CARON -01D1;N # Lu LATIN CAPITAL LETTER O WITH CARON -01D2;A # Ll LATIN SMALL LETTER O WITH CARON -01D3;N # Lu LATIN CAPITAL LETTER U WITH CARON -01D4;A # Ll LATIN SMALL LETTER U WITH CARON -01D5;N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON -01D6;A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND MACRON -01D7;N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE -01D8;A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE -01D9;N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON -01DA;A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND CARON -01DB;N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE -01DC;A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE -01DD..024F;N # L& [115] LATIN SMALL LETTER TURNED E..LATIN SMALL LETTER Y WITH STROKE -0250;N # Ll LATIN SMALL LETTER TURNED A -0251;A # Ll LATIN SMALL LETTER ALPHA -0252..0260;N # Ll [15] LATIN SMALL LETTER TURNED ALPHA..LATIN SMALL LETTER G WITH HOOK -0261;A # Ll LATIN SMALL LETTER SCRIPT G -0262..0293;N # Ll [50] LATIN LETTER SMALL CAPITAL G..LATIN SMALL LETTER EZH WITH CURL -0294;N # Lo LATIN LETTER GLOTTAL STOP -0295..02AF;N # Ll [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL -02B0..02C1;N # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP -02C2..02C3;N # Sk [2] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER RIGHT ARROWHEAD -02C4;A # Sk MODIFIER LETTER UP ARROWHEAD -02C5;N # Sk MODIFIER LETTER DOWN ARROWHEAD -02C6;N # Lm MODIFIER LETTER CIRCUMFLEX ACCENT -02C7;A # Lm CARON -02C8;N # Lm MODIFIER LETTER VERTICAL LINE -02C9..02CB;A # Lm [3] MODIFIER LETTER MACRON..MODIFIER LETTER GRAVE ACCENT -02CC;N # Lm MODIFIER LETTER LOW VERTICAL LINE -02CD;A # Lm MODIFIER LETTER LOW MACRON -02CE..02CF;N # Lm [2] MODIFIER LETTER LOW GRAVE ACCENT..MODIFIER LETTER LOW ACUTE ACCENT -02D0;A # Lm MODIFIER LETTER TRIANGULAR COLON -02D1;N # Lm MODIFIER LETTER HALF TRIANGULAR COLON -02D2..02D7;N # Sk [6] MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER MINUS SIGN -02D8..02DB;A # Sk [4] BREVE..OGONEK -02DC;N # Sk SMALL TILDE -02DD;A # Sk DOUBLE ACUTE ACCENT -02DE;N # Sk MODIFIER LETTER RHOTIC HOOK -02DF;A # Sk MODIFIER LETTER CROSS ACCENT -02E0..02E4;N # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP -02E5..02EB;N # Sk [7] MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER YANG DEPARTING TONE MARK -02EC;N # Lm MODIFIER LETTER VOICING -02ED;N # Sk MODIFIER LETTER UNASPIRATED -02EE;N # Lm MODIFIER LETTER DOUBLE APOSTROPHE -02EF..02FF;N # Sk [17] MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW -0300..036F;A # Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X -0370..0373;N # L& [4] GREEK CAPITAL LETTER HETA..GREEK SMALL LETTER ARCHAIC SAMPI -0374;N # Lm GREEK NUMERAL SIGN -0375;N # Sk GREEK LOWER NUMERAL SIGN -0376..0377;N # L& [2] GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA..GREEK SMALL LETTER PAMPHYLIAN DIGAMMA -037A;N # Lm GREEK YPOGEGRAMMENI -037B..037D;N # Ll [3] GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL -037E;N # Po GREEK QUESTION MARK -037F;N # Lu GREEK CAPITAL LETTER YOT -0384..0385;N # Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS -0386;N # Lu GREEK CAPITAL LETTER ALPHA WITH TONOS -0387;N # Po GREEK ANO TELEIA -0388..038A;N # Lu [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS -038C;N # Lu GREEK CAPITAL LETTER OMICRON WITH TONOS -038E..0390;N # L& [3] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS -0391..03A1;A # Lu [17] GREEK CAPITAL LETTER ALPHA..GREEK CAPITAL LETTER RHO -03A3..03A9;A # Lu [7] GREEK CAPITAL LETTER SIGMA..GREEK CAPITAL LETTER OMEGA -03AA..03B0;N # L& [7] GREEK CAPITAL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS -03B1..03C1;A # Ll [17] GREEK SMALL LETTER ALPHA..GREEK SMALL LETTER RHO -03C2;N # Ll GREEK SMALL LETTER FINAL SIGMA -03C3..03C9;A # Ll [7] GREEK SMALL LETTER SIGMA..GREEK SMALL LETTER OMEGA -03CA..03F5;N # L& [44] GREEK SMALL LETTER IOTA WITH DIALYTIKA..GREEK LUNATE EPSILON SYMBOL -03F6;N # Sm GREEK REVERSED LUNATE EPSILON SYMBOL -03F7..03FF;N # L& [9] GREEK CAPITAL LETTER SHO..GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL -0400;N # Lu CYRILLIC CAPITAL LETTER IE WITH GRAVE -0401;A # Lu CYRILLIC CAPITAL LETTER IO -0402..040F;N # Lu [14] CYRILLIC CAPITAL LETTER DJE..CYRILLIC CAPITAL LETTER DZHE -0410..044F;A # L& [64] CYRILLIC CAPITAL LETTER A..CYRILLIC SMALL LETTER YA -0450;N # Ll CYRILLIC SMALL LETTER IE WITH GRAVE -0451;A # Ll CYRILLIC SMALL LETTER IO -0452..0481;N # L& [48] CYRILLIC SMALL LETTER DJE..CYRILLIC SMALL LETTER KOPPA -0482;N # So CYRILLIC THOUSANDS SIGN -0483..0487;N # Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE -0488..0489;N # Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN -048A..04FF;N # L& [118] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER HA WITH STROKE -0500..052F;N # L& [48] CYRILLIC CAPITAL LETTER KOMI DE..CYRILLIC SMALL LETTER EL WITH DESCENDER -0531..0556;N # Lu [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH -0559;N # Lm ARMENIAN MODIFIER LETTER LEFT HALF RING -055A..055F;N # Po [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK -0560..0588;N # Ll [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE -0589;N # Po ARMENIAN FULL STOP -058A;N # Pd ARMENIAN HYPHEN -058D..058E;N # So [2] RIGHT-FACING ARMENIAN ETERNITY SIGN..LEFT-FACING ARMENIAN ETERNITY SIGN -058F;N # Sc ARMENIAN DRAM SIGN -0591..05BD;N # Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG -05BE;N # Pd HEBREW PUNCTUATION MAQAF -05BF;N # Mn HEBREW POINT RAFE -05C0;N # Po HEBREW PUNCTUATION PASEQ -05C1..05C2;N # Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT -05C3;N # Po HEBREW PUNCTUATION SOF PASUQ -05C4..05C5;N # Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT -05C6;N # Po HEBREW PUNCTUATION NUN HAFUKHA -05C7;N # Mn HEBREW POINT QAMATS QATAN -05D0..05EA;N # Lo [27] HEBREW LETTER ALEF..HEBREW LETTER TAV -05EF..05F2;N # Lo [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD -05F3..05F4;N # Po [2] HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATION GERSHAYIM -0600..0605;N # Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE -0606..0608;N # Sm [3] ARABIC-INDIC CUBE ROOT..ARABIC RAY -0609..060A;N # Po [2] ARABIC-INDIC PER MILLE SIGN..ARABIC-INDIC PER TEN THOUSAND SIGN -060B;N # Sc AFGHANI SIGN -060C..060D;N # Po [2] ARABIC COMMA..ARABIC DATE SEPARATOR -060E..060F;N # So [2] ARABIC POETIC VERSE SIGN..ARABIC SIGN MISRA -0610..061A;N # Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA -061B;N # Po ARABIC SEMICOLON -061C;N # Cf ARABIC LETTER MARK -061D..061F;N # Po [3] ARABIC END OF TEXT MARK..ARABIC QUESTION MARK -0620..063F;N # Lo [32] ARABIC LETTER KASHMIRI YEH..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE -0640;N # Lm ARABIC TATWEEL -0641..064A;N # Lo [10] ARABIC LETTER FEH..ARABIC LETTER YEH -064B..065F;N # Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW -0660..0669;N # Nd [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE -066A..066D;N # Po [4] ARABIC PERCENT SIGN..ARABIC FIVE POINTED STAR -066E..066F;N # Lo [2] ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF -0670;N # Mn ARABIC LETTER SUPERSCRIPT ALEF -0671..06D3;N # Lo [99] ARABIC LETTER ALEF WASLA..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE -06D4;N # Po ARABIC FULL STOP -06D5;N # Lo ARABIC LETTER AE -06D6..06DC;N # Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN -06DD;N # Cf ARABIC END OF AYAH -06DE;N # So ARABIC START OF RUB EL HIZB -06DF..06E4;N # Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA -06E5..06E6;N # Lm [2] ARABIC SMALL WAW..ARABIC SMALL YEH -06E7..06E8;N # Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON -06E9;N # So ARABIC PLACE OF SAJDAH -06EA..06ED;N # Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM -06EE..06EF;N # Lo [2] ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V -06F0..06F9;N # Nd [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE -06FA..06FC;N # Lo [3] ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC LETTER GHAIN WITH DOT BELOW -06FD..06FE;N # So [2] ARABIC SIGN SINDHI AMPERSAND..ARABIC SIGN SINDHI POSTPOSITION MEN -06FF;N # Lo ARABIC LETTER HEH WITH INVERTED V -0700..070D;N # Po [14] SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN ASTERISCUS -070F;N # Cf SYRIAC ABBREVIATION MARK -0710;N # Lo SYRIAC LETTER ALAPH -0711;N # Mn SYRIAC LETTER SUPERSCRIPT ALAPH -0712..072F;N # Lo [30] SYRIAC LETTER BETH..SYRIAC LETTER PERSIAN DHALATH -0730..074A;N # Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH -074D..074F;N # Lo [3] SYRIAC LETTER SOGDIAN ZHAIN..SYRIAC LETTER SOGDIAN FE -0750..077F;N # Lo [48] ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS ABOVE -0780..07A5;N # Lo [38] THAANA LETTER HAA..THAANA LETTER WAAVU -07A6..07B0;N # Mn [11] THAANA ABAFILI..THAANA SUKUN -07B1;N # Lo THAANA LETTER NAA -07C0..07C9;N # Nd [10] NKO DIGIT ZERO..NKO DIGIT NINE -07CA..07EA;N # Lo [33] NKO LETTER A..NKO LETTER JONA RA -07EB..07F3;N # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE -07F4..07F5;N # Lm [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE -07F6;N # So NKO SYMBOL OO DENNEN -07F7..07F9;N # Po [3] NKO SYMBOL GBAKURUNEN..NKO EXCLAMATION MARK -07FA;N # Lm NKO LAJANYALAN -07FD;N # Mn NKO DANTAYALAN -07FE..07FF;N # Sc [2] NKO DOROME SIGN..NKO TAMAN SIGN -0800..0815;N # Lo [22] SAMARITAN LETTER ALAF..SAMARITAN LETTER TAAF -0816..0819;N # Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH -081A;N # Lm SAMARITAN MODIFIER LETTER EPENTHETIC YUT -081B..0823;N # Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A -0824;N # Lm SAMARITAN MODIFIER LETTER SHORT A -0825..0827;N # Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U -0828;N # Lm SAMARITAN MODIFIER LETTER I -0829..082D;N # Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA -0830..083E;N # Po [15] SAMARITAN PUNCTUATION NEQUDAA..SAMARITAN PUNCTUATION ANNAAU -0840..0858;N # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN -0859..085B;N # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK -085E;N # Po MANDAIC PUNCTUATION -0860..086A;N # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA -0870..0887;N # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT -0888;N # Sk ARABIC RAISED ROUND DOT -0889..088E;N # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL -0890..0891;N # Cf [2] ARABIC POUND MARK ABOVE..ARABIC PIASTRE MARK ABOVE -0898..089F;N # Mn [8] ARABIC SMALL HIGH WORD AL-JUZ..ARABIC HALF MADDA OVER MADDA -08A0..08C8;N # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF -08C9;N # Lm ARABIC SMALL FARSI YEH -08CA..08E1;N # Mn [24] ARABIC SMALL HIGH FARSI YEH..ARABIC SMALL HIGH SIGN SAFHA -08E2;N # Cf ARABIC DISPUTED END OF AYAH -08E3..08FF;N # Mn [29] ARABIC TURNED DAMMA BELOW..ARABIC MARK SIDEWAYS NOON GHUNNA -0900..0902;N # Mn [3] DEVANAGARI SIGN INVERTED CANDRABINDU..DEVANAGARI SIGN ANUSVARA -0903;N # Mc DEVANAGARI SIGN VISARGA -0904..0939;N # Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA -093A;N # Mn DEVANAGARI VOWEL SIGN OE -093B;N # Mc DEVANAGARI VOWEL SIGN OOE -093C;N # Mn DEVANAGARI SIGN NUKTA -093D;N # Lo DEVANAGARI SIGN AVAGRAHA -093E..0940;N # Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II -0941..0948;N # Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI -0949..094C;N # Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU -094D;N # Mn DEVANAGARI SIGN VIRAMA -094E..094F;N # Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW -0950;N # Lo DEVANAGARI OM -0951..0957;N # Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE -0958..0961;N # Lo [10] DEVANAGARI LETTER QA..DEVANAGARI LETTER VOCALIC LL -0962..0963;N # Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL -0964..0965;N # Po [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA -0966..096F;N # Nd [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE -0970;N # Po DEVANAGARI ABBREVIATION SIGN -0971;N # Lm DEVANAGARI SIGN HIGH SPACING DOT -0972..097F;N # Lo [14] DEVANAGARI LETTER CANDRA A..DEVANAGARI LETTER BBA -0980;N # Lo BENGALI ANJI -0981;N # Mn BENGALI SIGN CANDRABINDU -0982..0983;N # Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA -0985..098C;N # Lo [8] BENGALI LETTER A..BENGALI LETTER VOCALIC L -098F..0990;N # Lo [2] BENGALI LETTER E..BENGALI LETTER AI -0993..09A8;N # Lo [22] BENGALI LETTER O..BENGALI LETTER NA -09AA..09B0;N # Lo [7] BENGALI LETTER PA..BENGALI LETTER RA -09B2;N # Lo BENGALI LETTER LA -09B6..09B9;N # Lo [4] BENGALI LETTER SHA..BENGALI LETTER HA -09BC;N # Mn BENGALI SIGN NUKTA -09BD;N # Lo BENGALI SIGN AVAGRAHA -09BE..09C0;N # Mc [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II -09C1..09C4;N # Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR -09C7..09C8;N # Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI -09CB..09CC;N # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU -09CD;N # Mn BENGALI SIGN VIRAMA -09CE;N # Lo BENGALI LETTER KHANDA TA -09D7;N # Mc BENGALI AU LENGTH MARK -09DC..09DD;N # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA -09DF..09E1;N # Lo [3] BENGALI LETTER YYA..BENGALI LETTER VOCALIC LL -09E2..09E3;N # Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL -09E6..09EF;N # Nd [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE -09F0..09F1;N # Lo [2] BENGALI LETTER RA WITH MIDDLE DIAGONAL..BENGALI LETTER RA WITH LOWER DIAGONAL -09F2..09F3;N # Sc [2] BENGALI RUPEE MARK..BENGALI RUPEE SIGN -09F4..09F9;N # No [6] BENGALI CURRENCY NUMERATOR ONE..BENGALI CURRENCY DENOMINATOR SIXTEEN -09FA;N # So BENGALI ISSHAR -09FB;N # Sc BENGALI GANDA MARK -09FC;N # Lo BENGALI LETTER VEDIC ANUSVARA -09FD;N # Po BENGALI ABBREVIATION SIGN -09FE;N # Mn BENGALI SANDHI MARK -0A01..0A02;N # Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI -0A03;N # Mc GURMUKHI SIGN VISARGA -0A05..0A0A;N # Lo [6] GURMUKHI LETTER A..GURMUKHI LETTER UU -0A0F..0A10;N # Lo [2] GURMUKHI LETTER EE..GURMUKHI LETTER AI -0A13..0A28;N # Lo [22] GURMUKHI LETTER OO..GURMUKHI LETTER NA -0A2A..0A30;N # Lo [7] GURMUKHI LETTER PA..GURMUKHI LETTER RA -0A32..0A33;N # Lo [2] GURMUKHI LETTER LA..GURMUKHI LETTER LLA -0A35..0A36;N # Lo [2] GURMUKHI LETTER VA..GURMUKHI LETTER SHA -0A38..0A39;N # Lo [2] GURMUKHI LETTER SA..GURMUKHI LETTER HA -0A3C;N # Mn GURMUKHI SIGN NUKTA -0A3E..0A40;N # Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II -0A41..0A42;N # Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU -0A47..0A48;N # Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI -0A4B..0A4D;N # Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA -0A51;N # Mn GURMUKHI SIGN UDAAT -0A59..0A5C;N # Lo [4] GURMUKHI LETTER KHHA..GURMUKHI LETTER RRA -0A5E;N # Lo GURMUKHI LETTER FA -0A66..0A6F;N # Nd [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE -0A70..0A71;N # Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK -0A72..0A74;N # Lo [3] GURMUKHI IRI..GURMUKHI EK ONKAR -0A75;N # Mn GURMUKHI SIGN YAKASH -0A76;N # Po GURMUKHI ABBREVIATION SIGN -0A81..0A82;N # Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA -0A83;N # Mc GUJARATI SIGN VISARGA -0A85..0A8D;N # Lo [9] GUJARATI LETTER A..GUJARATI VOWEL CANDRA E -0A8F..0A91;N # Lo [3] GUJARATI LETTER E..GUJARATI VOWEL CANDRA O -0A93..0AA8;N # Lo [22] GUJARATI LETTER O..GUJARATI LETTER NA -0AAA..0AB0;N # Lo [7] GUJARATI LETTER PA..GUJARATI LETTER RA -0AB2..0AB3;N # Lo [2] GUJARATI LETTER LA..GUJARATI LETTER LLA -0AB5..0AB9;N # Lo [5] GUJARATI LETTER VA..GUJARATI LETTER HA -0ABC;N # Mn GUJARATI SIGN NUKTA -0ABD;N # Lo GUJARATI SIGN AVAGRAHA -0ABE..0AC0;N # Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II -0AC1..0AC5;N # Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E -0AC7..0AC8;N # Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI -0AC9;N # Mc GUJARATI VOWEL SIGN CANDRA O -0ACB..0ACC;N # Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU -0ACD;N # Mn GUJARATI SIGN VIRAMA -0AD0;N # Lo GUJARATI OM -0AE0..0AE1;N # Lo [2] GUJARATI LETTER VOCALIC RR..GUJARATI LETTER VOCALIC LL -0AE2..0AE3;N # Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL -0AE6..0AEF;N # Nd [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE -0AF0;N # Po GUJARATI ABBREVIATION SIGN -0AF1;N # Sc GUJARATI RUPEE SIGN -0AF9;N # Lo GUJARATI LETTER ZHA -0AFA..0AFF;N # Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE -0B01;N # Mn ORIYA SIGN CANDRABINDU -0B02..0B03;N # Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA -0B05..0B0C;N # Lo [8] ORIYA LETTER A..ORIYA LETTER VOCALIC L -0B0F..0B10;N # Lo [2] ORIYA LETTER E..ORIYA LETTER AI -0B13..0B28;N # Lo [22] ORIYA LETTER O..ORIYA LETTER NA -0B2A..0B30;N # Lo [7] ORIYA LETTER PA..ORIYA LETTER RA -0B32..0B33;N # Lo [2] ORIYA LETTER LA..ORIYA LETTER LLA -0B35..0B39;N # Lo [5] ORIYA LETTER VA..ORIYA LETTER HA -0B3C;N # Mn ORIYA SIGN NUKTA -0B3D;N # Lo ORIYA SIGN AVAGRAHA -0B3E;N # Mc ORIYA VOWEL SIGN AA -0B3F;N # Mn ORIYA VOWEL SIGN I -0B40;N # Mc ORIYA VOWEL SIGN II -0B41..0B44;N # Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR -0B47..0B48;N # Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI -0B4B..0B4C;N # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU -0B4D;N # Mn ORIYA SIGN VIRAMA -0B55..0B56;N # Mn [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK -0B57;N # Mc ORIYA AU LENGTH MARK -0B5C..0B5D;N # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA -0B5F..0B61;N # Lo [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL -0B62..0B63;N # Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL -0B66..0B6F;N # Nd [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE -0B70;N # So ORIYA ISSHAR -0B71;N # Lo ORIYA LETTER WA -0B72..0B77;N # No [6] ORIYA FRACTION ONE QUARTER..ORIYA FRACTION THREE SIXTEENTHS -0B82;N # Mn TAMIL SIGN ANUSVARA -0B83;N # Lo TAMIL SIGN VISARGA -0B85..0B8A;N # Lo [6] TAMIL LETTER A..TAMIL LETTER UU -0B8E..0B90;N # Lo [3] TAMIL LETTER E..TAMIL LETTER AI -0B92..0B95;N # Lo [4] TAMIL LETTER O..TAMIL LETTER KA -0B99..0B9A;N # Lo [2] TAMIL LETTER NGA..TAMIL LETTER CA -0B9C;N # Lo TAMIL LETTER JA -0B9E..0B9F;N # Lo [2] TAMIL LETTER NYA..TAMIL LETTER TTA -0BA3..0BA4;N # Lo [2] TAMIL LETTER NNA..TAMIL LETTER TA -0BA8..0BAA;N # Lo [3] TAMIL LETTER NA..TAMIL LETTER PA -0BAE..0BB9;N # Lo [12] TAMIL LETTER MA..TAMIL LETTER HA -0BBE..0BBF;N # Mc [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I -0BC0;N # Mn TAMIL VOWEL SIGN II -0BC1..0BC2;N # Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU -0BC6..0BC8;N # Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI -0BCA..0BCC;N # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU -0BCD;N # Mn TAMIL SIGN VIRAMA -0BD0;N # Lo TAMIL OM -0BD7;N # Mc TAMIL AU LENGTH MARK -0BE6..0BEF;N # Nd [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE -0BF0..0BF2;N # No [3] TAMIL NUMBER TEN..TAMIL NUMBER ONE THOUSAND -0BF3..0BF8;N # So [6] TAMIL DAY SIGN..TAMIL AS ABOVE SIGN -0BF9;N # Sc TAMIL RUPEE SIGN -0BFA;N # So TAMIL NUMBER SIGN -0C00;N # Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE -0C01..0C03;N # Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA -0C04;N # Mn TELUGU SIGN COMBINING ANUSVARA ABOVE -0C05..0C0C;N # Lo [8] TELUGU LETTER A..TELUGU LETTER VOCALIC L -0C0E..0C10;N # Lo [3] TELUGU LETTER E..TELUGU LETTER AI -0C12..0C28;N # Lo [23] TELUGU LETTER O..TELUGU LETTER NA -0C2A..0C39;N # Lo [16] TELUGU LETTER PA..TELUGU LETTER HA -0C3C;N # Mn TELUGU SIGN NUKTA -0C3D;N # Lo TELUGU SIGN AVAGRAHA -0C3E..0C40;N # Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II -0C41..0C44;N # Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR -0C46..0C48;N # Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI -0C4A..0C4D;N # Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA -0C55..0C56;N # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK -0C58..0C5A;N # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA -0C5D;N # Lo TELUGU LETTER NAKAARA POLLU -0C60..0C61;N # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL -0C62..0C63;N # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL -0C66..0C6F;N # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE -0C77;N # Po TELUGU SIGN SIDDHAM -0C78..0C7E;N # No [7] TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR -0C7F;N # So TELUGU SIGN TUUMU -0C80;N # Lo KANNADA SIGN SPACING CANDRABINDU -0C81;N # Mn KANNADA SIGN CANDRABINDU -0C82..0C83;N # Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA -0C84;N # Po KANNADA SIGN SIDDHAM -0C85..0C8C;N # Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L -0C8E..0C90;N # Lo [3] KANNADA LETTER E..KANNADA LETTER AI -0C92..0CA8;N # Lo [23] KANNADA LETTER O..KANNADA LETTER NA -0CAA..0CB3;N # Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA -0CB5..0CB9;N # Lo [5] KANNADA LETTER VA..KANNADA LETTER HA -0CBC;N # Mn KANNADA SIGN NUKTA -0CBD;N # Lo KANNADA SIGN AVAGRAHA -0CBE;N # Mc KANNADA VOWEL SIGN AA -0CBF;N # Mn KANNADA VOWEL SIGN I -0CC0..0CC4;N # Mc [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR -0CC6;N # Mn KANNADA VOWEL SIGN E -0CC7..0CC8;N # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI -0CCA..0CCB;N # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO -0CCC..0CCD;N # Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA -0CD5..0CD6;N # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK -0CDD..0CDE;N # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA -0CE0..0CE1;N # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL -0CE2..0CE3;N # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL -0CE6..0CEF;N # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE -0CF1..0CF2;N # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA -0CF3;N # Mc KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT -0D00..0D01;N # Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU -0D02..0D03;N # Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA -0D04..0D0C;N # Lo [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L -0D0E..0D10;N # Lo [3] MALAYALAM LETTER E..MALAYALAM LETTER AI -0D12..0D3A;N # Lo [41] MALAYALAM LETTER O..MALAYALAM LETTER TTTA -0D3B..0D3C;N # Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA -0D3D;N # Lo MALAYALAM SIGN AVAGRAHA -0D3E..0D40;N # Mc [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II -0D41..0D44;N # Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR -0D46..0D48;N # Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI -0D4A..0D4C;N # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU -0D4D;N # Mn MALAYALAM SIGN VIRAMA -0D4E;N # Lo MALAYALAM LETTER DOT REPH -0D4F;N # So MALAYALAM SIGN PARA -0D54..0D56;N # Lo [3] MALAYALAM LETTER CHILLU M..MALAYALAM LETTER CHILLU LLL -0D57;N # Mc MALAYALAM AU LENGTH MARK -0D58..0D5E;N # No [7] MALAYALAM FRACTION ONE ONE-HUNDRED-AND-SIXTIETH..MALAYALAM FRACTION ONE FIFTH -0D5F..0D61;N # Lo [3] MALAYALAM LETTER ARCHAIC II..MALAYALAM LETTER VOCALIC LL -0D62..0D63;N # Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL -0D66..0D6F;N # Nd [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE -0D70..0D78;N # No [9] MALAYALAM NUMBER TEN..MALAYALAM FRACTION THREE SIXTEENTHS -0D79;N # So MALAYALAM DATE MARK -0D7A..0D7F;N # Lo [6] MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER CHILLU K -0D81;N # Mn SINHALA SIGN CANDRABINDU -0D82..0D83;N # Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA -0D85..0D96;N # Lo [18] SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA -0D9A..0DB1;N # Lo [24] SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA -0DB3..0DBB;N # Lo [9] SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA -0DBD;N # Lo SINHALA LETTER DANTAJA LAYANNA -0DC0..0DC6;N # Lo [7] SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA -0DCA;N # Mn SINHALA SIGN AL-LAKUNA -0DCF..0DD1;N # Mc [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA -0DD2..0DD4;N # Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA -0DD6;N # Mn SINHALA VOWEL SIGN DIGA PAA-PILLA -0DD8..0DDF;N # Mc [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA -0DE6..0DEF;N # Nd [10] SINHALA LITH DIGIT ZERO..SINHALA LITH DIGIT NINE -0DF2..0DF3;N # Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA -0DF4;N # Po SINHALA PUNCTUATION KUNDDALIYA -0E01..0E30;N # Lo [48] THAI CHARACTER KO KAI..THAI CHARACTER SARA A -0E31;N # Mn THAI CHARACTER MAI HAN-AKAT -0E32..0E33;N # Lo [2] THAI CHARACTER SARA AA..THAI CHARACTER SARA AM -0E34..0E3A;N # Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU -0E3F;N # Sc THAI CURRENCY SYMBOL BAHT -0E40..0E45;N # Lo [6] THAI CHARACTER SARA E..THAI CHARACTER LAKKHANGYAO -0E46;N # Lm THAI CHARACTER MAIYAMOK -0E47..0E4E;N # Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN -0E4F;N # Po THAI CHARACTER FONGMAN -0E50..0E59;N # Nd [10] THAI DIGIT ZERO..THAI DIGIT NINE -0E5A..0E5B;N # Po [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT -0E81..0E82;N # Lo [2] LAO LETTER KO..LAO LETTER KHO SUNG -0E84;N # Lo LAO LETTER KHO TAM -0E86..0E8A;N # Lo [5] LAO LETTER PALI GHA..LAO LETTER SO TAM -0E8C..0EA3;N # Lo [24] LAO LETTER PALI JHA..LAO LETTER LO LING -0EA5;N # Lo LAO LETTER LO LOOT -0EA7..0EB0;N # Lo [10] LAO LETTER WO..LAO VOWEL SIGN A -0EB1;N # Mn LAO VOWEL SIGN MAI KAN -0EB2..0EB3;N # Lo [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM -0EB4..0EBC;N # Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO -0EBD;N # Lo LAO SEMIVOWEL SIGN NYO -0EC0..0EC4;N # Lo [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI -0EC6;N # Lm LAO KO LA -0EC8..0ECE;N # Mn [7] LAO TONE MAI EK..LAO YAMAKKAN -0ED0..0ED9;N # Nd [10] LAO DIGIT ZERO..LAO DIGIT NINE -0EDC..0EDF;N # Lo [4] LAO HO NO..LAO LETTER KHMU NYO -0F00;N # Lo TIBETAN SYLLABLE OM -0F01..0F03;N # So [3] TIBETAN MARK GTER YIG MGO TRUNCATED A..TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA -0F04..0F12;N # Po [15] TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK RGYA GRAM SHAD -0F13;N # So TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN -0F14;N # Po TIBETAN MARK GTER TSHEG -0F15..0F17;N # So [3] TIBETAN LOGOTYPE SIGN CHAD RTAGS..TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS -0F18..0F19;N # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS -0F1A..0F1F;N # So [6] TIBETAN SIGN RDEL DKAR GCIG..TIBETAN SIGN RDEL DKAR RDEL NAG -0F20..0F29;N # Nd [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE -0F2A..0F33;N # No [10] TIBETAN DIGIT HALF ONE..TIBETAN DIGIT HALF ZERO -0F34;N # So TIBETAN MARK BSDUS RTAGS -0F35;N # Mn TIBETAN MARK NGAS BZUNG NYI ZLA -0F36;N # So TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN -0F37;N # Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS -0F38;N # So TIBETAN MARK CHE MGO -0F39;N # Mn TIBETAN MARK TSA -PHRU -0F3A;N # Ps TIBETAN MARK GUG RTAGS GYON -0F3B;N # Pe TIBETAN MARK GUG RTAGS GYAS -0F3C;N # Ps TIBETAN MARK ANG KHANG GYON -0F3D;N # Pe TIBETAN MARK ANG KHANG GYAS -0F3E..0F3F;N # Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES -0F40..0F47;N # Lo [8] TIBETAN LETTER KA..TIBETAN LETTER JA -0F49..0F6C;N # Lo [36] TIBETAN LETTER NYA..TIBETAN LETTER RRA -0F71..0F7E;N # Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO -0F7F;N # Mc TIBETAN SIGN RNAM BCAD -0F80..0F84;N # Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA -0F85;N # Po TIBETAN MARK PALUTA -0F86..0F87;N # Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS -0F88..0F8C;N # Lo [5] TIBETAN SIGN LCE TSA CAN..TIBETAN SIGN INVERTED MCHU CAN -0F8D..0F97;N # Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA -0F99..0FBC;N # Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA -0FBE..0FC5;N # So [8] TIBETAN KU RU KHA..TIBETAN SYMBOL RDO RJE -0FC6;N # Mn TIBETAN SYMBOL PADMA GDAN -0FC7..0FCC;N # So [6] TIBETAN SYMBOL RDO RJE RGYA GRAM..TIBETAN SYMBOL NOR BU BZHI -KHYIL -0FCE..0FCF;N # So [2] TIBETAN SIGN RDEL NAG RDEL DKAR..TIBETAN SIGN RDEL NAG GSUM -0FD0..0FD4;N # Po [5] TIBETAN MARK BSKA- SHOG GI MGO RGYAN..TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA -0FD5..0FD8;N # So [4] RIGHT-FACING SVASTI SIGN..LEFT-FACING SVASTI SIGN WITH DOTS -0FD9..0FDA;N # Po [2] TIBETAN MARK LEADING MCHAN RTAGS..TIBETAN MARK TRAILING MCHAN RTAGS -1000..102A;N # Lo [43] MYANMAR LETTER KA..MYANMAR LETTER AU -102B..102C;N # Mc [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA -102D..1030;N # Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU -1031;N # Mc MYANMAR VOWEL SIGN E -1032..1037;N # Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW -1038;N # Mc MYANMAR SIGN VISARGA -1039..103A;N # Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT -103B..103C;N # Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA -103D..103E;N # Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA -103F;N # Lo MYANMAR LETTER GREAT SA -1040..1049;N # Nd [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE -104A..104F;N # Po [6] MYANMAR SIGN LITTLE SECTION..MYANMAR SYMBOL GENITIVE -1050..1055;N # Lo [6] MYANMAR LETTER SHA..MYANMAR LETTER VOCALIC LL -1056..1057;N # Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR -1058..1059;N # Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL -105A..105D;N # Lo [4] MYANMAR LETTER MON NGA..MYANMAR LETTER MON BBE -105E..1060;N # Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA -1061;N # Lo MYANMAR LETTER SGAW KAREN SHA -1062..1064;N # Mc [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO -1065..1066;N # Lo [2] MYANMAR LETTER WESTERN PWO KAREN THA..MYANMAR LETTER WESTERN PWO KAREN PWA -1067..106D;N # Mc [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5 -106E..1070;N # Lo [3] MYANMAR LETTER EASTERN PWO KAREN NNA..MYANMAR LETTER EASTERN PWO KAREN GHWA -1071..1074;N # Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE -1075..1081;N # Lo [13] MYANMAR LETTER SHAN KA..MYANMAR LETTER SHAN HA -1082;N # Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA -1083..1084;N # Mc [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E -1085..1086;N # Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y -1087..108C;N # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3 -108D;N # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE -108E;N # Lo MYANMAR LETTER RUMAI PALAUNG FA -108F;N # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5 -1090..1099;N # Nd [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE -109A..109C;N # Mc [3] MYANMAR SIGN KHAMTI TONE-1..MYANMAR VOWEL SIGN AITON A -109D;N # Mn MYANMAR VOWEL SIGN AITON AI -109E..109F;N # So [2] MYANMAR SYMBOL SHAN ONE..MYANMAR SYMBOL SHAN EXCLAMATION -10A0..10C5;N # Lu [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE -10C7;N # Lu GEORGIAN CAPITAL LETTER YN -10CD;N # Lu GEORGIAN CAPITAL LETTER AEN -10D0..10FA;N # Ll [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN -10FB;N # Po GEORGIAN PARAGRAPH SEPARATOR -10FC;N # Lm MODIFIER LETTER GEORGIAN NAR -10FD..10FF;N # Ll [3] GEORGIAN LETTER AEN..GEORGIAN LETTER LABIAL SIGN -1100..115F;W # Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER -1160..11FF;N # Lo [160] HANGUL JUNGSEONG FILLER..HANGUL JONGSEONG SSANGNIEUN -1200..1248;N # Lo [73] ETHIOPIC SYLLABLE HA..ETHIOPIC SYLLABLE QWA -124A..124D;N # Lo [4] ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE -1250..1256;N # Lo [7] ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO -1258;N # Lo ETHIOPIC SYLLABLE QHWA -125A..125D;N # Lo [4] ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE -1260..1288;N # Lo [41] ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XWA -128A..128D;N # Lo [4] ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE -1290..12B0;N # Lo [33] ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KWA -12B2..12B5;N # Lo [4] ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE -12B8..12BE;N # Lo [7] ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO -12C0;N # Lo ETHIOPIC SYLLABLE KXWA -12C2..12C5;N # Lo [4] ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE -12C8..12D6;N # Lo [15] ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE PHARYNGEAL O -12D8..1310;N # Lo [57] ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE GWA -1312..1315;N # Lo [4] ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE -1318..135A;N # Lo [67] ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE FYA -135D..135F;N # Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK -1360..1368;N # Po [9] ETHIOPIC SECTION MARK..ETHIOPIC PARAGRAPH SEPARATOR -1369..137C;N # No [20] ETHIOPIC DIGIT ONE..ETHIOPIC NUMBER TEN THOUSAND -1380..138F;N # Lo [16] ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE -1390..1399;N # So [10] ETHIOPIC TONAL MARK YIZET..ETHIOPIC TONAL MARK KURT -13A0..13F5;N # Lu [86] CHEROKEE LETTER A..CHEROKEE LETTER MV -13F8..13FD;N # Ll [6] CHEROKEE SMALL LETTER YE..CHEROKEE SMALL LETTER MV -1400;N # Pd CANADIAN SYLLABICS HYPHEN -1401..166C;N # Lo [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA -166D;N # So CANADIAN SYLLABICS CHI SIGN -166E;N # Po CANADIAN SYLLABICS FULL STOP -166F..167F;N # Lo [17] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS BLACKFOOT W -1680;N # Zs OGHAM SPACE MARK -1681..169A;N # Lo [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH -169B;N # Ps OGHAM FEATHER MARK -169C;N # Pe OGHAM REVERSED FEATHER MARK -16A0..16EA;N # Lo [75] RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X -16EB..16ED;N # Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION -16EE..16F0;N # Nl [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL -16F1..16F8;N # Lo [8] RUNIC LETTER K..RUNIC LETTER FRANKS CASKET AESC -1700..1711;N # Lo [18] TAGALOG LETTER A..TAGALOG LETTER HA -1712..1714;N # Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA -1715;N # Mc TAGALOG SIGN PAMUDPOD -171F;N # Lo TAGALOG LETTER ARCHAIC RA -1720..1731;N # Lo [18] HANUNOO LETTER A..HANUNOO LETTER HA -1732..1733;N # Mn [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U -1734;N # Mc HANUNOO SIGN PAMUDPOD -1735..1736;N # Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION -1740..1751;N # Lo [18] BUHID LETTER A..BUHID LETTER HA -1752..1753;N # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U -1760..176C;N # Lo [13] TAGBANWA LETTER A..TAGBANWA LETTER YA -176E..1770;N # Lo [3] TAGBANWA LETTER LA..TAGBANWA LETTER SA -1772..1773;N # Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U -1780..17B3;N # Lo [52] KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU -17B4..17B5;N # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA -17B6;N # Mc KHMER VOWEL SIGN AA -17B7..17BD;N # Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA -17BE..17C5;N # Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU -17C6;N # Mn KHMER SIGN NIKAHIT -17C7..17C8;N # Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU -17C9..17D3;N # Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT -17D4..17D6;N # Po [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH -17D7;N # Lm KHMER SIGN LEK TOO -17D8..17DA;N # Po [3] KHMER SIGN BEYYAL..KHMER SIGN KOOMUUT -17DB;N # Sc KHMER CURRENCY SYMBOL RIEL -17DC;N # Lo KHMER SIGN AVAKRAHASANYA -17DD;N # Mn KHMER SIGN ATTHACAN -17E0..17E9;N # Nd [10] KHMER DIGIT ZERO..KHMER DIGIT NINE -17F0..17F9;N # No [10] KHMER SYMBOL LEK ATTAK SON..KHMER SYMBOL LEK ATTAK PRAM-BUON -1800..1805;N # Po [6] MONGOLIAN BIRGA..MONGOLIAN FOUR DOTS -1806;N # Pd MONGOLIAN TODO SOFT HYPHEN -1807..180A;N # Po [4] MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU -180B..180D;N # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE -180E;N # Cf MONGOLIAN VOWEL SEPARATOR -180F;N # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR -1810..1819;N # Nd [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE -1820..1842;N # Lo [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI -1843;N # Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN -1844..1878;N # Lo [53] MONGOLIAN LETTER TODO E..MONGOLIAN LETTER CHA WITH TWO DOTS -1880..1884;N # Lo [5] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER ALI GALI INVERTED UBADAMA -1885..1886;N # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA -1887..18A8;N # Lo [34] MONGOLIAN LETTER ALI GALI A..MONGOLIAN LETTER MANCHU ALI GALI BHA -18A9;N # Mn MONGOLIAN LETTER ALI GALI DAGALGA -18AA;N # Lo MONGOLIAN LETTER MANCHU ALI GALI LHA -18B0..18F5;N # Lo [70] CANADIAN SYLLABICS OY..CANADIAN SYLLABICS CARRIER DENTAL S -1900..191E;N # Lo [31] LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER TRA -1920..1922;N # Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U -1923..1926;N # Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU -1927..1928;N # Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O -1929..192B;N # Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA -1930..1931;N # Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA -1932;N # Mn LIMBU SMALL LETTER ANUSVARA -1933..1938;N # Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA -1939..193B;N # Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I -1940;N # So LIMBU SIGN LOO -1944..1945;N # Po [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK -1946..194F;N # Nd [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE -1950..196D;N # Lo [30] TAI LE LETTER KA..TAI LE LETTER AI -1970..1974;N # Lo [5] TAI LE LETTER TONE-2..TAI LE LETTER TONE-6 -1980..19AB;N # Lo [44] NEW TAI LUE LETTER HIGH QA..NEW TAI LUE LETTER LOW SUA -19B0..19C9;N # Lo [26] NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE TONE MARK-2 -19D0..19D9;N # Nd [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE -19DA;N # No NEW TAI LUE THAM DIGIT ONE -19DE..19DF;N # So [2] NEW TAI LUE SIGN LAE..NEW TAI LUE SIGN LAEV -19E0..19FF;N # So [32] KHMER SYMBOL PATHAMASAT..KHMER SYMBOL DAP-PRAM ROC -1A00..1A16;N # Lo [23] BUGINESE LETTER KA..BUGINESE LETTER HA -1A17..1A18;N # Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U -1A19..1A1A;N # Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O -1A1B;N # Mn BUGINESE VOWEL SIGN AE -1A1E..1A1F;N # Po [2] BUGINESE PALLAWA..BUGINESE END OF SECTION -1A20..1A54;N # Lo [53] TAI THAM LETTER HIGH KA..TAI THAM LETTER GREAT SA -1A55;N # Mc TAI THAM CONSONANT SIGN MEDIAL RA -1A56;N # Mn TAI THAM CONSONANT SIGN MEDIAL LA -1A57;N # Mc TAI THAM CONSONANT SIGN LA TANG LAI -1A58..1A5E;N # Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA -1A60;N # Mn TAI THAM SIGN SAKOT -1A61;N # Mc TAI THAM VOWEL SIGN A -1A62;N # Mn TAI THAM VOWEL SIGN MAI SAT -1A63..1A64;N # Mc [2] TAI THAM VOWEL SIGN AA..TAI THAM VOWEL SIGN TALL AA -1A65..1A6C;N # Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW -1A6D..1A72;N # Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI -1A73..1A7C;N # Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN -1A7F;N # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT -1A80..1A89;N # Nd [10] TAI THAM HORA DIGIT ZERO..TAI THAM HORA DIGIT NINE -1A90..1A99;N # Nd [10] TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE -1AA0..1AA6;N # Po [7] TAI THAM SIGN WIANG..TAI THAM SIGN REVERSED ROTATED RANA -1AA7;N # Lm TAI THAM SIGN MAI YAMOK -1AA8..1AAD;N # Po [6] TAI THAM SIGN KAAN..TAI THAM SIGN CAANG -1AB0..1ABD;N # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW -1ABE;N # Me COMBINING PARENTHESES OVERLAY -1ABF..1ACE;N # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T -1B00..1B03;N # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG -1B04;N # Mc BALINESE SIGN BISAH -1B05..1B33;N # Lo [47] BALINESE LETTER AKARA..BALINESE LETTER HA -1B34;N # Mn BALINESE SIGN REREKAN -1B35;N # Mc BALINESE VOWEL SIGN TEDUNG -1B36..1B3A;N # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA -1B3B;N # Mc BALINESE VOWEL SIGN RA REPA TEDUNG -1B3C;N # Mn BALINESE VOWEL SIGN LA LENGA -1B3D..1B41;N # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG -1B42;N # Mn BALINESE VOWEL SIGN PEPET -1B43..1B44;N # Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG -1B45..1B4C;N # Lo [8] BALINESE LETTER KAF SASAK..BALINESE LETTER ARCHAIC JNYA -1B50..1B59;N # Nd [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE -1B5A..1B60;N # Po [7] BALINESE PANTI..BALINESE PAMENENG -1B61..1B6A;N # So [10] BALINESE MUSICAL SYMBOL DONG..BALINESE MUSICAL SYMBOL DANG GEDE -1B6B..1B73;N # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG -1B74..1B7C;N # So [9] BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING -1B7D..1B7E;N # Po [2] BALINESE PANTI LANTANG..BALINESE PAMADA LANTANG -1B80..1B81;N # Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR -1B82;N # Mc SUNDANESE SIGN PANGWISAD -1B83..1BA0;N # Lo [30] SUNDANESE LETTER A..SUNDANESE LETTER HA -1BA1;N # Mc SUNDANESE CONSONANT SIGN PAMINGKAL -1BA2..1BA5;N # Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU -1BA6..1BA7;N # Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG -1BA8..1BA9;N # Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG -1BAA;N # Mc SUNDANESE SIGN PAMAAEH -1BAB..1BAD;N # Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA -1BAE..1BAF;N # Lo [2] SUNDANESE LETTER KHA..SUNDANESE LETTER SYA -1BB0..1BB9;N # Nd [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE -1BBA..1BBF;N # Lo [6] SUNDANESE AVAGRAHA..SUNDANESE LETTER FINAL M -1BC0..1BE5;N # Lo [38] BATAK LETTER A..BATAK LETTER U -1BE6;N # Mn BATAK SIGN TOMPI -1BE7;N # Mc BATAK VOWEL SIGN E -1BE8..1BE9;N # Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE -1BEA..1BEC;N # Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O -1BED;N # Mn BATAK VOWEL SIGN KARO O -1BEE;N # Mc BATAK VOWEL SIGN U -1BEF..1BF1;N # Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H -1BF2..1BF3;N # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN -1BFC..1BFF;N # Po [4] BATAK SYMBOL BINDU NA METEK..BATAK SYMBOL BINDU PANGOLAT -1C00..1C23;N # Lo [36] LEPCHA LETTER KA..LEPCHA LETTER A -1C24..1C2B;N # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU -1C2C..1C33;N # Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T -1C34..1C35;N # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG -1C36..1C37;N # Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA -1C3B..1C3F;N # Po [5] LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION TSHOOK -1C40..1C49;N # Nd [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE -1C4D..1C4F;N # Lo [3] LEPCHA LETTER TTA..LEPCHA LETTER DDA -1C50..1C59;N # Nd [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE -1C5A..1C77;N # Lo [30] OL CHIKI LETTER LA..OL CHIKI LETTER OH -1C78..1C7D;N # Lm [6] OL CHIKI MU TTUDDAG..OL CHIKI AHAD -1C7E..1C7F;N # Po [2] OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD -1C80..1C88;N # Ll [9] CYRILLIC SMALL LETTER ROUNDED VE..CYRILLIC SMALL LETTER UNBLENDED UK -1C90..1CBA;N # Lu [43] GEORGIAN MTAVRULI CAPITAL LETTER AN..GEORGIAN MTAVRULI CAPITAL LETTER AIN -1CBD..1CBF;N # Lu [3] GEORGIAN MTAVRULI CAPITAL LETTER AEN..GEORGIAN MTAVRULI CAPITAL LETTER LABIAL SIGN -1CC0..1CC7;N # Po [8] SUNDANESE PUNCTUATION BINDU SURYA..SUNDANESE PUNCTUATION BINDU BA SATANGA -1CD0..1CD2;N # Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA -1CD3;N # Po VEDIC SIGN NIHSHVASA -1CD4..1CE0;N # Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA -1CE1;N # Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA -1CE2..1CE8;N # Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL -1CE9..1CEC;N # Lo [4] VEDIC SIGN ANUSVARA ANTARGOMUKHA..VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TAIL -1CED;N # Mn VEDIC SIGN TIRYAK -1CEE..1CF3;N # Lo [6] VEDIC SIGN HEXIFORM LONG ANUSVARA..VEDIC SIGN ROTATED ARDHAVISARGA -1CF4;N # Mn VEDIC TONE CANDRA ABOVE -1CF5..1CF6;N # Lo [2] VEDIC SIGN JIHVAMULIYA..VEDIC SIGN UPADHMANIYA -1CF7;N # Mc VEDIC SIGN ATIKRAMA -1CF8..1CF9;N # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE -1CFA;N # Lo VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA -1D00..1D2B;N # Ll [44] LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL -1D2C..1D6A;N # Lm [63] MODIFIER LETTER CAPITAL A..GREEK SUBSCRIPT SMALL LETTER CHI -1D6B..1D77;N # Ll [13] LATIN SMALL LETTER UE..LATIN SMALL LETTER TURNED G -1D78;N # Lm MODIFIER LETTER CYRILLIC EN -1D79..1D7F;N # Ll [7] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER UPSILON WITH STROKE -1D80..1D9A;N # Ll [27] LATIN SMALL LETTER B WITH PALATAL HOOK..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK -1D9B..1DBF;N # Lm [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA -1DC0..1DFF;N # Mn [64] COMBINING DOTTED GRAVE ACCENT..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW -1E00..1EFF;N # L& [256] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER Y WITH LOOP -1F00..1F15;N # L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA -1F18..1F1D;N # Lu [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA -1F20..1F45;N # L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA -1F48..1F4D;N # Lu [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA -1F50..1F57;N # Ll [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI -1F59;N # Lu GREEK CAPITAL LETTER UPSILON WITH DASIA -1F5B;N # Lu GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA -1F5D;N # Lu GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA -1F5F..1F7D;N # L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA -1F80..1FB4;N # L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI -1FB6..1FBC;N # L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI -1FBD;N # Sk GREEK KORONIS -1FBE;N # Ll GREEK PROSGEGRAMMENI -1FBF..1FC1;N # Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI -1FC2..1FC4;N # Ll [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI -1FC6..1FCC;N # L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI -1FCD..1FCF;N # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI -1FD0..1FD3;N # Ll [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA -1FD6..1FDB;N # L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA -1FDD..1FDF;N # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI -1FE0..1FEC;N # L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA -1FED..1FEF;N # Sk [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA -1FF2..1FF4;N # Ll [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI -1FF6..1FFC;N # L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI -1FFD..1FFE;N # Sk [2] GREEK OXIA..GREEK DASIA -2000..200A;N # Zs [11] EN QUAD..HAIR SPACE -200B..200F;N # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK -2010;A # Pd HYPHEN -2011..2012;N # Pd [2] NON-BREAKING HYPHEN..FIGURE DASH -2013..2015;A # Pd [3] EN DASH..HORIZONTAL BAR -2016;A # Po DOUBLE VERTICAL LINE -2017;N # Po DOUBLE LOW LINE -2018;A # Pi LEFT SINGLE QUOTATION MARK -2019;A # Pf RIGHT SINGLE QUOTATION MARK -201A;N # Ps SINGLE LOW-9 QUOTATION MARK -201B;N # Pi SINGLE HIGH-REVERSED-9 QUOTATION MARK -201C;A # Pi LEFT DOUBLE QUOTATION MARK -201D;A # Pf RIGHT DOUBLE QUOTATION MARK -201E;N # Ps DOUBLE LOW-9 QUOTATION MARK -201F;N # Pi DOUBLE HIGH-REVERSED-9 QUOTATION MARK -2020..2022;A # Po [3] DAGGER..BULLET -2023;N # Po TRIANGULAR BULLET -2024..2027;A # Po [4] ONE DOT LEADER..HYPHENATION POINT -2028;N # Zl LINE SEPARATOR -2029;N # Zp PARAGRAPH SEPARATOR -202A..202E;N # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE -202F;N # Zs NARROW NO-BREAK SPACE -2030;A # Po PER MILLE SIGN -2031;N # Po PER TEN THOUSAND SIGN -2032..2033;A # Po [2] PRIME..DOUBLE PRIME -2034;N # Po TRIPLE PRIME -2035;A # Po REVERSED PRIME -2036..2038;N # Po [3] REVERSED DOUBLE PRIME..CARET -2039;N # Pi SINGLE LEFT-POINTING ANGLE QUOTATION MARK -203A;N # Pf SINGLE RIGHT-POINTING ANGLE QUOTATION MARK -203B;A # Po REFERENCE MARK -203C..203D;N # Po [2] DOUBLE EXCLAMATION MARK..INTERROBANG -203E;A # Po OVERLINE -203F..2040;N # Pc [2] UNDERTIE..CHARACTER TIE -2041..2043;N # Po [3] CARET INSERTION POINT..HYPHEN BULLET -2044;N # Sm FRACTION SLASH -2045;N # Ps LEFT SQUARE BRACKET WITH QUILL -2046;N # Pe RIGHT SQUARE BRACKET WITH QUILL -2047..2051;N # Po [11] DOUBLE QUESTION MARK..TWO ASTERISKS ALIGNED VERTICALLY -2052;N # Sm COMMERCIAL MINUS SIGN -2053;N # Po SWUNG DASH -2054;N # Pc INVERTED UNDERTIE -2055..205E;N # Po [10] FLOWER PUNCTUATION MARK..VERTICAL FOUR DOTS -205F;N # Zs MEDIUM MATHEMATICAL SPACE -2060..2064;N # Cf [5] WORD JOINER..INVISIBLE PLUS -2066..206F;N # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES -2070;N # No SUPERSCRIPT ZERO -2071;N # Lm SUPERSCRIPT LATIN SMALL LETTER I -2074;A # No SUPERSCRIPT FOUR -2075..2079;N # No [5] SUPERSCRIPT FIVE..SUPERSCRIPT NINE -207A..207C;N # Sm [3] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT EQUALS SIGN -207D;N # Ps SUPERSCRIPT LEFT PARENTHESIS -207E;N # Pe SUPERSCRIPT RIGHT PARENTHESIS -207F;A # Lm SUPERSCRIPT LATIN SMALL LETTER N -2080;N # No SUBSCRIPT ZERO -2081..2084;A # No [4] SUBSCRIPT ONE..SUBSCRIPT FOUR -2085..2089;N # No [5] SUBSCRIPT FIVE..SUBSCRIPT NINE -208A..208C;N # Sm [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN -208D;N # Ps SUBSCRIPT LEFT PARENTHESIS -208E;N # Pe SUBSCRIPT RIGHT PARENTHESIS -2090..209C;N # Lm [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T -20A0..20A8;N # Sc [9] EURO-CURRENCY SIGN..RUPEE SIGN -20A9;H # Sc WON SIGN -20AA..20AB;N # Sc [2] NEW SHEQEL SIGN..DONG SIGN -20AC;A # Sc EURO SIGN -20AD..20C0;N # Sc [20] KIP SIGN..SOM SIGN -20D0..20DC;N # Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE -20DD..20E0;N # Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH -20E1;N # Mn COMBINING LEFT RIGHT ARROW ABOVE -20E2..20E4;N # Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE -20E5..20F0;N # Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE -2100..2101;N # So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT -2102;N # Lu DOUBLE-STRUCK CAPITAL C -2103;A # So DEGREE CELSIUS -2104;N # So CENTRE LINE SYMBOL -2105;A # So CARE OF -2106;N # So CADA UNA -2107;N # Lu EULER CONSTANT -2108;N # So SCRUPLE -2109;A # So DEGREE FAHRENHEIT -210A..2112;N # L& [9] SCRIPT SMALL G..SCRIPT CAPITAL L -2113;A # Ll SCRIPT SMALL L -2114;N # So L B BAR SYMBOL -2115;N # Lu DOUBLE-STRUCK CAPITAL N -2116;A # So NUMERO SIGN -2117;N # So SOUND RECORDING COPYRIGHT -2118;N # Sm SCRIPT CAPITAL P -2119..211D;N # Lu [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R -211E..2120;N # So [3] PRESCRIPTION TAKE..SERVICE MARK -2121..2122;A # So [2] TELEPHONE SIGN..TRADE MARK SIGN -2123;N # So VERSICLE -2124;N # Lu DOUBLE-STRUCK CAPITAL Z -2125;N # So OUNCE SIGN -2126;A # Lu OHM SIGN -2127;N # So INVERTED OHM SIGN -2128;N # Lu BLACK-LETTER CAPITAL Z -2129;N # So TURNED GREEK SMALL LETTER IOTA -212A;N # Lu KELVIN SIGN -212B;A # Lu ANGSTROM SIGN -212C..212D;N # Lu [2] SCRIPT CAPITAL B..BLACK-LETTER CAPITAL C -212E;N # So ESTIMATED SYMBOL -212F..2134;N # L& [6] SCRIPT SMALL E..SCRIPT SMALL O -2135..2138;N # Lo [4] ALEF SYMBOL..DALET SYMBOL -2139;N # Ll INFORMATION SOURCE -213A..213B;N # So [2] ROTATED CAPITAL Q..FACSIMILE SIGN -213C..213F;N # L& [4] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK CAPITAL PI -2140..2144;N # Sm [5] DOUBLE-STRUCK N-ARY SUMMATION..TURNED SANS-SERIF CAPITAL Y -2145..2149;N # L& [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J -214A;N # So PROPERTY LINE -214B;N # Sm TURNED AMPERSAND -214C..214D;N # So [2] PER SIGN..AKTIESELSKAB -214E;N # Ll TURNED SMALL F -214F;N # So SYMBOL FOR SAMARITAN SOURCE -2150..2152;N # No [3] VULGAR FRACTION ONE SEVENTH..VULGAR FRACTION ONE TENTH -2153..2154;A # No [2] VULGAR FRACTION ONE THIRD..VULGAR FRACTION TWO THIRDS -2155..215A;N # No [6] VULGAR FRACTION ONE FIFTH..VULGAR FRACTION FIVE SIXTHS -215B..215E;A # No [4] VULGAR FRACTION ONE EIGHTH..VULGAR FRACTION SEVEN EIGHTHS -215F;N # No FRACTION NUMERATOR ONE -2160..216B;A # Nl [12] ROMAN NUMERAL ONE..ROMAN NUMERAL TWELVE -216C..216F;N # Nl [4] ROMAN NUMERAL FIFTY..ROMAN NUMERAL ONE THOUSAND -2170..2179;A # Nl [10] SMALL ROMAN NUMERAL ONE..SMALL ROMAN NUMERAL TEN -217A..2182;N # Nl [9] SMALL ROMAN NUMERAL ELEVEN..ROMAN NUMERAL TEN THOUSAND -2183..2184;N # L& [2] ROMAN NUMERAL REVERSED ONE HUNDRED..LATIN SMALL LETTER REVERSED C -2185..2188;N # Nl [4] ROMAN NUMERAL SIX LATE FORM..ROMAN NUMERAL ONE HUNDRED THOUSAND -2189;A # No VULGAR FRACTION ZERO THIRDS -218A..218B;N # So [2] TURNED DIGIT TWO..TURNED DIGIT THREE -2190..2194;A # Sm [5] LEFTWARDS ARROW..LEFT RIGHT ARROW -2195..2199;A # So [5] UP DOWN ARROW..SOUTH WEST ARROW -219A..219B;N # Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE -219C..219F;N # So [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW -21A0;N # Sm RIGHTWARDS TWO HEADED ARROW -21A1..21A2;N # So [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL -21A3;N # Sm RIGHTWARDS ARROW WITH TAIL -21A4..21A5;N # So [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR -21A6;N # Sm RIGHTWARDS ARROW FROM BAR -21A7..21AD;N # So [7] DOWNWARDS ARROW FROM BAR..LEFT RIGHT WAVE ARROW -21AE;N # Sm LEFT RIGHT ARROW WITH STROKE -21AF..21B7;N # So [9] DOWNWARDS ZIGZAG ARROW..CLOCKWISE TOP SEMICIRCLE ARROW -21B8..21B9;A # So [2] NORTH WEST ARROW TO LONG BAR..LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR -21BA..21CD;N # So [20] ANTICLOCKWISE OPEN CIRCLE ARROW..LEFTWARDS DOUBLE ARROW WITH STROKE -21CE..21CF;N # Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE -21D0..21D1;N # So [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW -21D2;A # Sm RIGHTWARDS DOUBLE ARROW -21D3;N # So DOWNWARDS DOUBLE ARROW -21D4;A # Sm LEFT RIGHT DOUBLE ARROW -21D5..21E6;N # So [18] UP DOWN DOUBLE ARROW..LEFTWARDS WHITE ARROW -21E7;A # So UPWARDS WHITE ARROW -21E8..21F3;N # So [12] RIGHTWARDS WHITE ARROW..UP DOWN WHITE ARROW -21F4..21FF;N # Sm [12] RIGHT ARROW WITH SMALL CIRCLE..LEFT RIGHT OPEN-HEADED ARROW -2200;A # Sm FOR ALL -2201;N # Sm COMPLEMENT -2202..2203;A # Sm [2] PARTIAL DIFFERENTIAL..THERE EXISTS -2204..2206;N # Sm [3] THERE DOES NOT EXIST..INCREMENT -2207..2208;A # Sm [2] NABLA..ELEMENT OF -2209..220A;N # Sm [2] NOT AN ELEMENT OF..SMALL ELEMENT OF -220B;A # Sm CONTAINS AS MEMBER -220C..220E;N # Sm [3] DOES NOT CONTAIN AS MEMBER..END OF PROOF -220F;A # Sm N-ARY PRODUCT -2210;N # Sm N-ARY COPRODUCT -2211;A # Sm N-ARY SUMMATION -2212..2214;N # Sm [3] MINUS SIGN..DOT PLUS -2215;A # Sm DIVISION SLASH -2216..2219;N # Sm [4] SET MINUS..BULLET OPERATOR -221A;A # Sm SQUARE ROOT -221B..221C;N # Sm [2] CUBE ROOT..FOURTH ROOT -221D..2220;A # Sm [4] PROPORTIONAL TO..ANGLE -2221..2222;N # Sm [2] MEASURED ANGLE..SPHERICAL ANGLE -2223;A # Sm DIVIDES -2224;N # Sm DOES NOT DIVIDE -2225;A # Sm PARALLEL TO -2226;N # Sm NOT PARALLEL TO -2227..222C;A # Sm [6] LOGICAL AND..DOUBLE INTEGRAL -222D;N # Sm TRIPLE INTEGRAL -222E;A # Sm CONTOUR INTEGRAL -222F..2233;N # Sm [5] SURFACE INTEGRAL..ANTICLOCKWISE CONTOUR INTEGRAL -2234..2237;A # Sm [4] THEREFORE..PROPORTION -2238..223B;N # Sm [4] DOT MINUS..HOMOTHETIC -223C..223D;A # Sm [2] TILDE OPERATOR..REVERSED TILDE -223E..2247;N # Sm [10] INVERTED LAZY S..NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO -2248;A # Sm ALMOST EQUAL TO -2249..224B;N # Sm [3] NOT ALMOST EQUAL TO..TRIPLE TILDE -224C;A # Sm ALL EQUAL TO -224D..2251;N # Sm [5] EQUIVALENT TO..GEOMETRICALLY EQUAL TO -2252;A # Sm APPROXIMATELY EQUAL TO OR THE IMAGE OF -2253..225F;N # Sm [13] IMAGE OF OR APPROXIMATELY EQUAL TO..QUESTIONED EQUAL TO -2260..2261;A # Sm [2] NOT EQUAL TO..IDENTICAL TO -2262..2263;N # Sm [2] NOT IDENTICAL TO..STRICTLY EQUIVALENT TO -2264..2267;A # Sm [4] LESS-THAN OR EQUAL TO..GREATER-THAN OVER EQUAL TO -2268..2269;N # Sm [2] LESS-THAN BUT NOT EQUAL TO..GREATER-THAN BUT NOT EQUAL TO -226A..226B;A # Sm [2] MUCH LESS-THAN..MUCH GREATER-THAN -226C..226D;N # Sm [2] BETWEEN..NOT EQUIVALENT TO -226E..226F;A # Sm [2] NOT LESS-THAN..NOT GREATER-THAN -2270..2281;N # Sm [18] NEITHER LESS-THAN NOR EQUAL TO..DOES NOT SUCCEED -2282..2283;A # Sm [2] SUBSET OF..SUPERSET OF -2284..2285;N # Sm [2] NOT A SUBSET OF..NOT A SUPERSET OF -2286..2287;A # Sm [2] SUBSET OF OR EQUAL TO..SUPERSET OF OR EQUAL TO -2288..2294;N # Sm [13] NEITHER A SUBSET OF NOR EQUAL TO..SQUARE CUP -2295;A # Sm CIRCLED PLUS -2296..2298;N # Sm [3] CIRCLED MINUS..CIRCLED DIVISION SLASH -2299;A # Sm CIRCLED DOT OPERATOR -229A..22A4;N # Sm [11] CIRCLED RING OPERATOR..DOWN TACK -22A5;A # Sm UP TACK -22A6..22BE;N # Sm [25] ASSERTION..RIGHT ANGLE WITH ARC -22BF;A # Sm RIGHT TRIANGLE -22C0..22FF;N # Sm [64] N-ARY LOGICAL AND..Z NOTATION BAG MEMBERSHIP -2300..2307;N # So [8] DIAMETER SIGN..WAVY LINE -2308;N # Ps LEFT CEILING -2309;N # Pe RIGHT CEILING -230A;N # Ps LEFT FLOOR -230B;N # Pe RIGHT FLOOR -230C..2311;N # So [6] BOTTOM RIGHT CROP..SQUARE LOZENGE -2312;A # So ARC -2313..2319;N # So [7] SEGMENT..TURNED NOT SIGN -231A..231B;W # So [2] WATCH..HOURGLASS -231C..231F;N # So [4] TOP LEFT CORNER..BOTTOM RIGHT CORNER -2320..2321;N # Sm [2] TOP HALF INTEGRAL..BOTTOM HALF INTEGRAL -2322..2328;N # So [7] FROWN..KEYBOARD -2329;W # Ps LEFT-POINTING ANGLE BRACKET -232A;W # Pe RIGHT-POINTING ANGLE BRACKET -232B..237B;N # So [81] ERASE TO THE LEFT..NOT CHECK MARK -237C;N # Sm RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW -237D..239A;N # So [30] SHOULDERED OPEN BOX..CLEAR SCREEN SYMBOL -239B..23B3;N # Sm [25] LEFT PARENTHESIS UPPER HOOK..SUMMATION BOTTOM -23B4..23DB;N # So [40] TOP SQUARE BRACKET..FUSE -23DC..23E1;N # Sm [6] TOP PARENTHESIS..BOTTOM TORTOISE SHELL BRACKET -23E2..23E8;N # So [7] WHITE TRAPEZIUM..DECIMAL EXPONENT SYMBOL -23E9..23EC;W # So [4] BLACK RIGHT-POINTING DOUBLE TRIANGLE..BLACK DOWN-POINTING DOUBLE TRIANGLE -23ED..23EF;N # So [3] BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR..BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR -23F0;W # So ALARM CLOCK -23F1..23F2;N # So [2] STOPWATCH..TIMER CLOCK -23F3;W # So HOURGLASS WITH FLOWING SAND -23F4..23FF;N # So [12] BLACK MEDIUM LEFT-POINTING TRIANGLE..OBSERVER EYE SYMBOL -2400..2426;N # So [39] SYMBOL FOR NULL..SYMBOL FOR SUBSTITUTE FORM TWO -2440..244A;N # So [11] OCR HOOK..OCR DOUBLE BACKSLASH -2460..249B;A # No [60] CIRCLED DIGIT ONE..NUMBER TWENTY FULL STOP -249C..24E9;A # So [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z -24EA;N # No CIRCLED DIGIT ZERO -24EB..24FF;A # No [21] NEGATIVE CIRCLED NUMBER ELEVEN..NEGATIVE CIRCLED DIGIT ZERO -2500..254B;A # So [76] BOX DRAWINGS LIGHT HORIZONTAL..BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL -254C..254F;N # So [4] BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL..BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL -2550..2573;A # So [36] BOX DRAWINGS DOUBLE HORIZONTAL..BOX DRAWINGS LIGHT DIAGONAL CROSS -2574..257F;N # So [12] BOX DRAWINGS LIGHT LEFT..BOX DRAWINGS HEAVY UP AND LIGHT DOWN -2580..258F;A # So [16] UPPER HALF BLOCK..LEFT ONE EIGHTH BLOCK -2590..2591;N # So [2] RIGHT HALF BLOCK..LIGHT SHADE -2592..2595;A # So [4] MEDIUM SHADE..RIGHT ONE EIGHTH BLOCK -2596..259F;N # So [10] QUADRANT LOWER LEFT..QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT -25A0..25A1;A # So [2] BLACK SQUARE..WHITE SQUARE -25A2;N # So WHITE SQUARE WITH ROUNDED CORNERS -25A3..25A9;A # So [7] WHITE SQUARE CONTAINING BLACK SMALL SQUARE..SQUARE WITH DIAGONAL CROSSHATCH FILL -25AA..25B1;N # So [8] BLACK SMALL SQUARE..WHITE PARALLELOGRAM -25B2..25B3;A # So [2] BLACK UP-POINTING TRIANGLE..WHITE UP-POINTING TRIANGLE -25B4..25B5;N # So [2] BLACK UP-POINTING SMALL TRIANGLE..WHITE UP-POINTING SMALL TRIANGLE -25B6;A # So BLACK RIGHT-POINTING TRIANGLE -25B7;A # Sm WHITE RIGHT-POINTING TRIANGLE -25B8..25BB;N # So [4] BLACK RIGHT-POINTING SMALL TRIANGLE..WHITE RIGHT-POINTING POINTER -25BC..25BD;A # So [2] BLACK DOWN-POINTING TRIANGLE..WHITE DOWN-POINTING TRIANGLE -25BE..25BF;N # So [2] BLACK DOWN-POINTING SMALL TRIANGLE..WHITE DOWN-POINTING SMALL TRIANGLE -25C0;A # So BLACK LEFT-POINTING TRIANGLE -25C1;A # Sm WHITE LEFT-POINTING TRIANGLE -25C2..25C5;N # So [4] BLACK LEFT-POINTING SMALL TRIANGLE..WHITE LEFT-POINTING POINTER -25C6..25C8;A # So [3] BLACK DIAMOND..WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND -25C9..25CA;N # So [2] FISHEYE..LOZENGE -25CB;A # So WHITE CIRCLE -25CC..25CD;N # So [2] DOTTED CIRCLE..CIRCLE WITH VERTICAL FILL -25CE..25D1;A # So [4] BULLSEYE..CIRCLE WITH RIGHT HALF BLACK -25D2..25E1;N # So [16] CIRCLE WITH LOWER HALF BLACK..LOWER HALF CIRCLE -25E2..25E5;A # So [4] BLACK LOWER RIGHT TRIANGLE..BLACK UPPER RIGHT TRIANGLE -25E6..25EE;N # So [9] WHITE BULLET..UP-POINTING TRIANGLE WITH RIGHT HALF BLACK -25EF;A # So LARGE CIRCLE -25F0..25F7;N # So [8] WHITE SQUARE WITH UPPER LEFT QUADRANT..WHITE CIRCLE WITH UPPER RIGHT QUADRANT -25F8..25FC;N # Sm [5] UPPER LEFT TRIANGLE..BLACK MEDIUM SQUARE -25FD..25FE;W # Sm [2] WHITE MEDIUM SMALL SQUARE..BLACK MEDIUM SMALL SQUARE -25FF;N # Sm LOWER RIGHT TRIANGLE -2600..2604;N # So [5] BLACK SUN WITH RAYS..COMET -2605..2606;A # So [2] BLACK STAR..WHITE STAR -2607..2608;N # So [2] LIGHTNING..THUNDERSTORM -2609;A # So SUN -260A..260D;N # So [4] ASCENDING NODE..OPPOSITION -260E..260F;A # So [2] BLACK TELEPHONE..WHITE TELEPHONE -2610..2613;N # So [4] BALLOT BOX..SALTIRE -2614..2615;W # So [2] UMBRELLA WITH RAIN DROPS..HOT BEVERAGE -2616..261B;N # So [6] WHITE SHOGI PIECE..BLACK RIGHT POINTING INDEX -261C;A # So WHITE LEFT POINTING INDEX -261D;N # So WHITE UP POINTING INDEX -261E;A # So WHITE RIGHT POINTING INDEX -261F..263F;N # So [33] WHITE DOWN POINTING INDEX..MERCURY -2640;A # So FEMALE SIGN -2641;N # So EARTH -2642;A # So MALE SIGN -2643..2647;N # So [5] JUPITER..PLUTO -2648..2653;W # So [12] ARIES..PISCES -2654..265F;N # So [12] WHITE CHESS KING..BLACK CHESS PAWN -2660..2661;A # So [2] BLACK SPADE SUIT..WHITE HEART SUIT -2662;N # So WHITE DIAMOND SUIT -2663..2665;A # So [3] BLACK CLUB SUIT..BLACK HEART SUIT -2666;N # So BLACK DIAMOND SUIT -2667..266A;A # So [4] WHITE CLUB SUIT..EIGHTH NOTE -266B;N # So BEAMED EIGHTH NOTES -266C..266D;A # So [2] BEAMED SIXTEENTH NOTES..MUSIC FLAT SIGN -266E;N # So MUSIC NATURAL SIGN -266F;A # Sm MUSIC SHARP SIGN -2670..267E;N # So [15] WEST SYRIAC CROSS..PERMANENT PAPER SIGN -267F;W # So WHEELCHAIR SYMBOL -2680..2692;N # So [19] DIE FACE-1..HAMMER AND PICK -2693;W # So ANCHOR -2694..269D;N # So [10] CROSSED SWORDS..OUTLINED WHITE STAR -269E..269F;A # So [2] THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT -26A0;N # So WARNING SIGN -26A1;W # So HIGH VOLTAGE SIGN -26A2..26A9;N # So [8] DOUBLED FEMALE SIGN..HORIZONTAL MALE WITH STROKE SIGN -26AA..26AB;W # So [2] MEDIUM WHITE CIRCLE..MEDIUM BLACK CIRCLE -26AC..26BC;N # So [17] MEDIUM SMALL WHITE CIRCLE..SESQUIQUADRATE -26BD..26BE;W # So [2] SOCCER BALL..BASEBALL -26BF;A # So SQUARED KEY -26C0..26C3;N # So [4] WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING -26C4..26C5;W # So [2] SNOWMAN WITHOUT SNOW..SUN BEHIND CLOUD -26C6..26CD;A # So [8] RAIN..DISABLED CAR -26CE;W # So OPHIUCHUS -26CF..26D3;A # So [5] PICK..CHAINS -26D4;W # So NO ENTRY -26D5..26E1;A # So [13] ALTERNATE ONE-WAY LEFT WAY TRAFFIC..RESTRICTED LEFT ENTRY-2 -26E2;N # So ASTRONOMICAL SYMBOL FOR URANUS -26E3;A # So HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE -26E4..26E7;N # So [4] PENTAGRAM..INVERTED PENTAGRAM -26E8..26E9;A # So [2] BLACK CROSS ON SHIELD..SHINTO SHRINE -26EA;W # So CHURCH -26EB..26F1;A # So [7] CASTLE..UMBRELLA ON GROUND -26F2..26F3;W # So [2] FOUNTAIN..FLAG IN HOLE -26F4;A # So FERRY -26F5;W # So SAILBOAT -26F6..26F9;A # So [4] SQUARE FOUR CORNERS..PERSON WITH BALL -26FA;W # So TENT -26FB..26FC;A # So [2] JAPANESE BANK SYMBOL..HEADSTONE GRAVEYARD SYMBOL -26FD;W # So FUEL PUMP -26FE..26FF;A # So [2] CUP ON BLACK SQUARE..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE -2700..2704;N # So [5] BLACK SAFETY SCISSORS..WHITE SCISSORS -2705;W # So WHITE HEAVY CHECK MARK -2706..2709;N # So [4] TELEPHONE LOCATION SIGN..ENVELOPE -270A..270B;W # So [2] RAISED FIST..RAISED HAND -270C..2727;N # So [28] VICTORY HAND..WHITE FOUR POINTED STAR -2728;W # So SPARKLES -2729..273C;N # So [20] STRESS OUTLINED WHITE STAR..OPEN CENTRE TEARDROP-SPOKED ASTERISK -273D;A # So HEAVY TEARDROP-SPOKED ASTERISK -273E..274B;N # So [14] SIX PETALLED BLACK AND WHITE FLORETTE..HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK -274C;W # So CROSS MARK -274D;N # So SHADOWED WHITE CIRCLE -274E;W # So NEGATIVE SQUARED CROSS MARK -274F..2752;N # So [4] LOWER RIGHT DROP-SHADOWED WHITE SQUARE..UPPER RIGHT SHADOWED WHITE SQUARE -2753..2755;W # So [3] BLACK QUESTION MARK ORNAMENT..WHITE EXCLAMATION MARK ORNAMENT -2756;N # So BLACK DIAMOND MINUS WHITE X -2757;W # So HEAVY EXCLAMATION MARK SYMBOL -2758..2767;N # So [16] LIGHT VERTICAL BAR..ROTATED FLORAL HEART BULLET -2768;N # Ps MEDIUM LEFT PARENTHESIS ORNAMENT -2769;N # Pe MEDIUM RIGHT PARENTHESIS ORNAMENT -276A;N # Ps MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT -276B;N # Pe MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT -276C;N # Ps MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT -276D;N # Pe MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT -276E;N # Ps HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT -276F;N # Pe HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT -2770;N # Ps HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT -2771;N # Pe HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT -2772;N # Ps LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT -2773;N # Pe LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT -2774;N # Ps MEDIUM LEFT CURLY BRACKET ORNAMENT -2775;N # Pe MEDIUM RIGHT CURLY BRACKET ORNAMENT -2776..277F;A # No [10] DINGBAT NEGATIVE CIRCLED DIGIT ONE..DINGBAT NEGATIVE CIRCLED NUMBER TEN -2780..2793;N # No [20] DINGBAT CIRCLED SANS-SERIF DIGIT ONE..DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN -2794;N # So HEAVY WIDE-HEADED RIGHTWARDS ARROW -2795..2797;W # So [3] HEAVY PLUS SIGN..HEAVY DIVISION SIGN -2798..27AF;N # So [24] HEAVY SOUTH EAST ARROW..NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW -27B0;W # So CURLY LOOP -27B1..27BE;N # So [14] NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW..OPEN-OUTLINED RIGHTWARDS ARROW -27BF;W # So DOUBLE CURLY LOOP -27C0..27C4;N # Sm [5] THREE DIMENSIONAL ANGLE..OPEN SUPERSET -27C5;N # Ps LEFT S-SHAPED BAG DELIMITER -27C6;N # Pe RIGHT S-SHAPED BAG DELIMITER -27C7..27E5;N # Sm [31] OR WITH DOT INSIDE..WHITE SQUARE WITH RIGHTWARDS TICK -27E6;Na # Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET -27E7;Na # Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET -27E8;Na # Ps MATHEMATICAL LEFT ANGLE BRACKET -27E9;Na # Pe MATHEMATICAL RIGHT ANGLE BRACKET -27EA;Na # Ps MATHEMATICAL LEFT DOUBLE ANGLE BRACKET -27EB;Na # Pe MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET -27EC;Na # Ps MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET -27ED;Na # Pe MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET -27EE;N # Ps MATHEMATICAL LEFT FLATTENED PARENTHESIS -27EF;N # Pe MATHEMATICAL RIGHT FLATTENED PARENTHESIS -27F0..27FF;N # Sm [16] UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW -2800..28FF;N # So [256] BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678 -2900..297F;N # Sm [128] RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..DOWN FISH TAIL -2980..2982;N # Sm [3] TRIPLE VERTICAL BAR DELIMITER..Z NOTATION TYPE COLON -2983;N # Ps LEFT WHITE CURLY BRACKET -2984;N # Pe RIGHT WHITE CURLY BRACKET -2985;Na # Ps LEFT WHITE PARENTHESIS -2986;Na # Pe RIGHT WHITE PARENTHESIS -2987;N # Ps Z NOTATION LEFT IMAGE BRACKET -2988;N # Pe Z NOTATION RIGHT IMAGE BRACKET -2989;N # Ps Z NOTATION LEFT BINDING BRACKET -298A;N # Pe Z NOTATION RIGHT BINDING BRACKET -298B;N # Ps LEFT SQUARE BRACKET WITH UNDERBAR -298C;N # Pe RIGHT SQUARE BRACKET WITH UNDERBAR -298D;N # Ps LEFT SQUARE BRACKET WITH TICK IN TOP CORNER -298E;N # Pe RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER -298F;N # Ps LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER -2990;N # Pe RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER -2991;N # Ps LEFT ANGLE BRACKET WITH DOT -2992;N # Pe RIGHT ANGLE BRACKET WITH DOT -2993;N # Ps LEFT ARC LESS-THAN BRACKET -2994;N # Pe RIGHT ARC GREATER-THAN BRACKET -2995;N # Ps DOUBLE LEFT ARC GREATER-THAN BRACKET -2996;N # Pe DOUBLE RIGHT ARC LESS-THAN BRACKET -2997;N # Ps LEFT BLACK TORTOISE SHELL BRACKET -2998;N # Pe RIGHT BLACK TORTOISE SHELL BRACKET -2999..29D7;N # Sm [63] DOTTED FENCE..BLACK HOURGLASS -29D8;N # Ps LEFT WIGGLY FENCE -29D9;N # Pe RIGHT WIGGLY FENCE -29DA;N # Ps LEFT DOUBLE WIGGLY FENCE -29DB;N # Pe RIGHT DOUBLE WIGGLY FENCE -29DC..29FB;N # Sm [32] INCOMPLETE INFINITY..TRIPLE PLUS -29FC;N # Ps LEFT-POINTING CURVED ANGLE BRACKET -29FD;N # Pe RIGHT-POINTING CURVED ANGLE BRACKET -29FE..29FF;N # Sm [2] TINY..MINY -2A00..2AFF;N # Sm [256] N-ARY CIRCLED DOT OPERATOR..N-ARY WHITE VERTICAL BAR -2B00..2B1A;N # So [27] NORTH EAST WHITE ARROW..DOTTED SQUARE -2B1B..2B1C;W # So [2] BLACK LARGE SQUARE..WHITE LARGE SQUARE -2B1D..2B2F;N # So [19] BLACK VERY SMALL SQUARE..WHITE VERTICAL ELLIPSE -2B30..2B44;N # Sm [21] LEFT ARROW WITH SMALL CIRCLE..RIGHTWARDS ARROW THROUGH SUPERSET -2B45..2B46;N # So [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW -2B47..2B4C;N # Sm [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR -2B4D..2B4F;N # So [3] DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW..SHORT BACKSLANTED SOUTH ARROW -2B50;W # So WHITE MEDIUM STAR -2B51..2B54;N # So [4] BLACK SMALL STAR..WHITE RIGHT-POINTING PENTAGON -2B55;W # So HEAVY LARGE CIRCLE -2B56..2B59;A # So [4] HEAVY OVAL WITH OVAL INSIDE..HEAVY CIRCLED SALTIRE -2B5A..2B73;N # So [26] SLANTED NORTH ARROW WITH HOOKED HEAD..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR -2B76..2B95;N # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW -2B97..2BFF;N # So [105] SYMBOL FOR TYPE A ELECTRONICS..HELLSCHREIBER PAUSE SYMBOL -2C00..2C5F;N # L& [96] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC SMALL LETTER CAUDATE CHRIVI -2C60..2C7B;N # L& [28] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN LETTER SMALL CAPITAL TURNED E -2C7C..2C7D;N # Lm [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V -2C7E..2C7F;N # Lu [2] LATIN CAPITAL LETTER S WITH SWASH TAIL..LATIN CAPITAL LETTER Z WITH SWASH TAIL -2C80..2CE4;N # L& [101] COPTIC CAPITAL LETTER ALFA..COPTIC SYMBOL KAI -2CE5..2CEA;N # So [6] COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA -2CEB..2CEE;N # L& [4] COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI..COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA -2CEF..2CF1;N # Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS -2CF2..2CF3;N # L& [2] COPTIC CAPITAL LETTER BOHAIRIC KHEI..COPTIC SMALL LETTER BOHAIRIC KHEI -2CF9..2CFC;N # Po [4] COPTIC OLD NUBIAN FULL STOP..COPTIC OLD NUBIAN VERSE DIVIDER -2CFD;N # No COPTIC FRACTION ONE HALF -2CFE..2CFF;N # Po [2] COPTIC FULL STOP..COPTIC MORPHOLOGICAL DIVIDER -2D00..2D25;N # Ll [38] GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE -2D27;N # Ll GEORGIAN SMALL LETTER YN -2D2D;N # Ll GEORGIAN SMALL LETTER AEN -2D30..2D67;N # Lo [56] TIFINAGH LETTER YA..TIFINAGH LETTER YO -2D6F;N # Lm TIFINAGH MODIFIER LETTER LABIALIZATION MARK -2D70;N # Po TIFINAGH SEPARATOR MARK -2D7F;N # Mn TIFINAGH CONSONANT JOINER -2D80..2D96;N # Lo [23] ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE -2DA0..2DA6;N # Lo [7] ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO -2DA8..2DAE;N # Lo [7] ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO -2DB0..2DB6;N # Lo [7] ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO -2DB8..2DBE;N # Lo [7] ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO -2DC0..2DC6;N # Lo [7] ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO -2DC8..2DCE;N # Lo [7] ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO -2DD0..2DD6;N # Lo [7] ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO -2DD8..2DDE;N # Lo [7] ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO -2DE0..2DFF;N # Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS -2E00..2E01;N # Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER -2E02;N # Pi LEFT SUBSTITUTION BRACKET -2E03;N # Pf RIGHT SUBSTITUTION BRACKET -2E04;N # Pi LEFT DOTTED SUBSTITUTION BRACKET -2E05;N # Pf RIGHT DOTTED SUBSTITUTION BRACKET -2E06..2E08;N # Po [3] RAISED INTERPOLATION MARKER..DOTTED TRANSPOSITION MARKER -2E09;N # Pi LEFT TRANSPOSITION BRACKET -2E0A;N # Pf RIGHT TRANSPOSITION BRACKET -2E0B;N # Po RAISED SQUARE -2E0C;N # Pi LEFT RAISED OMISSION BRACKET -2E0D;N # Pf RIGHT RAISED OMISSION BRACKET -2E0E..2E16;N # Po [9] EDITORIAL CORONIS..DOTTED RIGHT-POINTING ANGLE -2E17;N # Pd DOUBLE OBLIQUE HYPHEN -2E18..2E19;N # Po [2] INVERTED INTERROBANG..PALM BRANCH -2E1A;N # Pd HYPHEN WITH DIAERESIS -2E1B;N # Po TILDE WITH RING ABOVE -2E1C;N # Pi LEFT LOW PARAPHRASE BRACKET -2E1D;N # Pf RIGHT LOW PARAPHRASE BRACKET -2E1E..2E1F;N # Po [2] TILDE WITH DOT ABOVE..TILDE WITH DOT BELOW -2E20;N # Pi LEFT VERTICAL BAR WITH QUILL -2E21;N # Pf RIGHT VERTICAL BAR WITH QUILL -2E22;N # Ps TOP LEFT HALF BRACKET -2E23;N # Pe TOP RIGHT HALF BRACKET -2E24;N # Ps BOTTOM LEFT HALF BRACKET -2E25;N # Pe BOTTOM RIGHT HALF BRACKET -2E26;N # Ps LEFT SIDEWAYS U BRACKET -2E27;N # Pe RIGHT SIDEWAYS U BRACKET -2E28;N # Ps LEFT DOUBLE PARENTHESIS -2E29;N # Pe RIGHT DOUBLE PARENTHESIS -2E2A..2E2E;N # Po [5] TWO DOTS OVER ONE DOT PUNCTUATION..REVERSED QUESTION MARK -2E2F;N # Lm VERTICAL TILDE -2E30..2E39;N # Po [10] RING POINT..TOP HALF SECTION SIGN -2E3A..2E3B;N # Pd [2] TWO-EM DASH..THREE-EM DASH -2E3C..2E3F;N # Po [4] STENOGRAPHIC FULL STOP..CAPITULUM -2E40;N # Pd DOUBLE HYPHEN -2E41;N # Po REVERSED COMMA -2E42;N # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK -2E43..2E4F;N # Po [13] DASH WITH LEFT UPTURN..CORNISH VERSE DIVIDER -2E50..2E51;N # So [2] CROSS PATTY WITH RIGHT CROSSBAR..CROSS PATTY WITH LEFT CROSSBAR -2E52..2E54;N # Po [3] TIRONIAN SIGN CAPITAL ET..MEDIEVAL QUESTION MARK -2E55;N # Ps LEFT SQUARE BRACKET WITH STROKE -2E56;N # Pe RIGHT SQUARE BRACKET WITH STROKE -2E57;N # Ps LEFT SQUARE BRACKET WITH DOUBLE STROKE -2E58;N # Pe RIGHT SQUARE BRACKET WITH DOUBLE STROKE -2E59;N # Ps TOP HALF LEFT PARENTHESIS -2E5A;N # Pe TOP HALF RIGHT PARENTHESIS -2E5B;N # Ps BOTTOM HALF LEFT PARENTHESIS -2E5C;N # Pe BOTTOM HALF RIGHT PARENTHESIS -2E5D;N # Pd OBLIQUE HYPHEN -2E80..2E99;W # So [26] CJK RADICAL REPEAT..CJK RADICAL RAP -2E9B..2EF3;W # So [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE -2F00..2FD5;W # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE -2FF0..2FFB;W # So [12] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID -3000;F # Zs IDEOGRAPHIC SPACE -3001..3003;W # Po [3] IDEOGRAPHIC COMMA..DITTO MARK -3004;W # So JAPANESE INDUSTRIAL STANDARD SYMBOL -3005;W # Lm IDEOGRAPHIC ITERATION MARK -3006;W # Lo IDEOGRAPHIC CLOSING MARK -3007;W # Nl IDEOGRAPHIC NUMBER ZERO -3008;W # Ps LEFT ANGLE BRACKET -3009;W # Pe RIGHT ANGLE BRACKET -300A;W # Ps LEFT DOUBLE ANGLE BRACKET -300B;W # Pe RIGHT DOUBLE ANGLE BRACKET -300C;W # Ps LEFT CORNER BRACKET -300D;W # Pe RIGHT CORNER BRACKET -300E;W # Ps LEFT WHITE CORNER BRACKET -300F;W # Pe RIGHT WHITE CORNER BRACKET -3010;W # Ps LEFT BLACK LENTICULAR BRACKET -3011;W # Pe RIGHT BLACK LENTICULAR BRACKET -3012..3013;W # So [2] POSTAL MARK..GETA MARK -3014;W # Ps LEFT TORTOISE SHELL BRACKET -3015;W # Pe RIGHT TORTOISE SHELL BRACKET -3016;W # Ps LEFT WHITE LENTICULAR BRACKET -3017;W # Pe RIGHT WHITE LENTICULAR BRACKET -3018;W # Ps LEFT WHITE TORTOISE SHELL BRACKET -3019;W # Pe RIGHT WHITE TORTOISE SHELL BRACKET -301A;W # Ps LEFT WHITE SQUARE BRACKET -301B;W # Pe RIGHT WHITE SQUARE BRACKET -301C;W # Pd WAVE DASH -301D;W # Ps REVERSED DOUBLE PRIME QUOTATION MARK -301E..301F;W # Pe [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK -3020;W # So POSTAL MARK FACE -3021..3029;W # Nl [9] HANGZHOU NUMERAL ONE..HANGZHOU NUMERAL NINE -302A..302D;W # Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK -302E..302F;W # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK -3030;W # Pd WAVY DASH -3031..3035;W # Lm [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF -3036..3037;W # So [2] CIRCLED POSTAL MARK..IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL -3038..303A;W # Nl [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY -303B;W # Lm VERTICAL IDEOGRAPHIC ITERATION MARK -303C;W # Lo MASU MARK -303D;W # Po PART ALTERNATION MARK -303E;W # So IDEOGRAPHIC VARIATION INDICATOR -303F;N # So IDEOGRAPHIC HALF FILL SPACE -3041..3096;W # Lo [86] HIRAGANA LETTER SMALL A..HIRAGANA LETTER SMALL KE -3099..309A;W # Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK -309B..309C;W # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK -309D..309E;W # Lm [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK -309F;W # Lo HIRAGANA DIGRAPH YORI -30A0;W # Pd KATAKANA-HIRAGANA DOUBLE HYPHEN -30A1..30FA;W # Lo [90] KATAKANA LETTER SMALL A..KATAKANA LETTER VO -30FB;W # Po KATAKANA MIDDLE DOT -30FC..30FE;W # Lm [3] KATAKANA-HIRAGANA PROLONGED SOUND MARK..KATAKANA VOICED ITERATION MARK -30FF;W # Lo KATAKANA DIGRAPH KOTO -3105..312F;W # Lo [43] BOPOMOFO LETTER B..BOPOMOFO LETTER NN -3131..318E;W # Lo [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE -3190..3191;W # So [2] IDEOGRAPHIC ANNOTATION LINKING MARK..IDEOGRAPHIC ANNOTATION REVERSE MARK -3192..3195;W # No [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK -3196..319F;W # So [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK -31A0..31BF;W # Lo [32] BOPOMOFO LETTER BU..BOPOMOFO LETTER AH -31C0..31E3;W # So [36] CJK STROKE T..CJK STROKE Q -31F0..31FF;W # Lo [16] KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO -3200..321E;W # So [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU -3220..3229;W # No [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN -322A..3247;W # So [30] PARENTHESIZED IDEOGRAPH MOON..CIRCLED IDEOGRAPH KOTO -3248..324F;A # No [8] CIRCLED NUMBER TEN ON BLACK SQUARE..CIRCLED NUMBER EIGHTY ON BLACK SQUARE -3250;W # So PARTNERSHIP SIGN -3251..325F;W # No [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE -3260..327F;W # So [32] CIRCLED HANGUL KIYEOK..KOREAN STANDARD SYMBOL -3280..3289;W # No [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN -328A..32B0;W # So [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT -32B1..32BF;W # No [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY -32C0..32FF;W # So [64] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..SQUARE ERA NAME REIWA -3300..33FF;W # So [256] SQUARE APAATO..SQUARE GAL -3400..4DBF;W # Lo [6592] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DBF -4DC0..4DFF;N # So [64] HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION -4E00..9FFF;W # Lo [20992] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FFF -A000..A014;W # Lo [21] YI SYLLABLE IT..YI SYLLABLE E -A015;W # Lm YI SYLLABLE WU -A016..A48C;W # Lo [1143] YI SYLLABLE BIT..YI SYLLABLE YYR -A490..A4C6;W # So [55] YI RADICAL QOT..YI RADICAL KE -A4D0..A4F7;N # Lo [40] LISU LETTER BA..LISU LETTER OE -A4F8..A4FD;N # Lm [6] LISU LETTER TONE MYA TI..LISU LETTER TONE MYA JEU -A4FE..A4FF;N # Po [2] LISU PUNCTUATION COMMA..LISU PUNCTUATION FULL STOP -A500..A60B;N # Lo [268] VAI SYLLABLE EE..VAI SYLLABLE NG -A60C;N # Lm VAI SYLLABLE LENGTHENER -A60D..A60F;N # Po [3] VAI COMMA..VAI QUESTION MARK -A610..A61F;N # Lo [16] VAI SYLLABLE NDOLE FA..VAI SYMBOL JONG -A620..A629;N # Nd [10] VAI DIGIT ZERO..VAI DIGIT NINE -A62A..A62B;N # Lo [2] VAI SYLLABLE NDOLE MA..VAI SYLLABLE NDOLE DO -A640..A66D;N # L& [46] CYRILLIC CAPITAL LETTER ZEMLYA..CYRILLIC SMALL LETTER DOUBLE MONOCULAR O -A66E;N # Lo CYRILLIC LETTER MULTIOCULAR O -A66F;N # Mn COMBINING CYRILLIC VZMET -A670..A672;N # Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN -A673;N # Po SLAVONIC ASTERISK -A674..A67D;N # Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK -A67E;N # Po CYRILLIC KAVYKA -A67F;N # Lm CYRILLIC PAYEROK -A680..A69B;N # L& [28] CYRILLIC CAPITAL LETTER DWE..CYRILLIC SMALL LETTER CROSSED O -A69C..A69D;N # Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN -A69E..A69F;N # Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E -A6A0..A6E5;N # Lo [70] BAMUM LETTER A..BAMUM LETTER KI -A6E6..A6EF;N # Nl [10] BAMUM LETTER MO..BAMUM LETTER KOGHOM -A6F0..A6F1;N # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS -A6F2..A6F7;N # Po [6] BAMUM NJAEMLI..BAMUM QUESTION MARK -A700..A716;N # Sk [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR -A717..A71F;N # Lm [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK -A720..A721;N # Sk [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE -A722..A76F;N # L& [78] LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF..LATIN SMALL LETTER CON -A770;N # Lm MODIFIER LETTER US -A771..A787;N # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T -A788;N # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT -A789..A78A;N # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN -A78B..A78E;N # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT -A78F;N # Lo LATIN LETTER SINOLOGICAL DOT -A790..A7CA;N # L& [59] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY -A7D0..A7D1;N # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D3;N # Ll LATIN SMALL LETTER DOUBLE THORN -A7D5..A7D9;N # L& [5] LATIN SMALL LETTER DOUBLE WYNN..LATIN SMALL LETTER SIGMOID S -A7F2..A7F4;N # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q -A7F5..A7F6;N # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H -A7F7;N # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I -A7F8..A7F9;N # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE -A7FA;N # Ll LATIN LETTER SMALL CAPITAL TURNED M -A7FB..A7FF;N # Lo [5] LATIN EPIGRAPHIC LETTER REVERSED F..LATIN EPIGRAPHIC LETTER ARCHAIC M -A800..A801;N # Lo [2] SYLOTI NAGRI LETTER A..SYLOTI NAGRI LETTER I -A802;N # Mn SYLOTI NAGRI SIGN DVISVARA -A803..A805;N # Lo [3] SYLOTI NAGRI LETTER U..SYLOTI NAGRI LETTER O -A806;N # Mn SYLOTI NAGRI SIGN HASANTA -A807..A80A;N # Lo [4] SYLOTI NAGRI LETTER KO..SYLOTI NAGRI LETTER GHO -A80B;N # Mn SYLOTI NAGRI SIGN ANUSVARA -A80C..A822;N # Lo [23] SYLOTI NAGRI LETTER CO..SYLOTI NAGRI LETTER HO -A823..A824;N # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I -A825..A826;N # Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E -A827;N # Mc SYLOTI NAGRI VOWEL SIGN OO -A828..A82B;N # So [4] SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-4 -A82C;N # Mn SYLOTI NAGRI SIGN ALTERNATE HASANTA -A830..A835;N # No [6] NORTH INDIC FRACTION ONE QUARTER..NORTH INDIC FRACTION THREE SIXTEENTHS -A836..A837;N # So [2] NORTH INDIC QUARTER MARK..NORTH INDIC PLACEHOLDER MARK -A838;N # Sc NORTH INDIC RUPEE MARK -A839;N # So NORTH INDIC QUANTITY MARK -A840..A873;N # Lo [52] PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU -A874..A877;N # Po [4] PHAGS-PA SINGLE HEAD MARK..PHAGS-PA MARK DOUBLE SHAD -A880..A881;N # Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA -A882..A8B3;N # Lo [50] SAURASHTRA LETTER A..SAURASHTRA LETTER LLA -A8B4..A8C3;N # Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU -A8C4..A8C5;N # Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU -A8CE..A8CF;N # Po [2] SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA -A8D0..A8D9;N # Nd [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE -A8E0..A8F1;N # Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA -A8F2..A8F7;N # Lo [6] DEVANAGARI SIGN SPACING CANDRABINDU..DEVANAGARI SIGN CANDRABINDU AVAGRAHA -A8F8..A8FA;N # Po [3] DEVANAGARI SIGN PUSHPIKA..DEVANAGARI CARET -A8FB;N # Lo DEVANAGARI HEADSTROKE -A8FC;N # Po DEVANAGARI SIGN SIDDHAM -A8FD..A8FE;N # Lo [2] DEVANAGARI JAIN OM..DEVANAGARI LETTER AY -A8FF;N # Mn DEVANAGARI VOWEL SIGN AY -A900..A909;N # Nd [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE -A90A..A925;N # Lo [28] KAYAH LI LETTER KA..KAYAH LI LETTER OO -A926..A92D;N # Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU -A92E..A92F;N # Po [2] KAYAH LI SIGN CWI..KAYAH LI SIGN SHYA -A930..A946;N # Lo [23] REJANG LETTER KA..REJANG LETTER A -A947..A951;N # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R -A952..A953;N # Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA -A95F;N # Po REJANG SECTION MARK -A960..A97C;W # Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH -A980..A982;N # Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR -A983;N # Mc JAVANESE SIGN WIGNYAN -A984..A9B2;N # Lo [47] JAVANESE LETTER A..JAVANESE LETTER HA -A9B3;N # Mn JAVANESE SIGN CECAK TELU -A9B4..A9B5;N # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG -A9B6..A9B9;N # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT -A9BA..A9BB;N # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE -A9BC..A9BD;N # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET -A9BE..A9C0;N # Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON -A9C1..A9CD;N # Po [13] JAVANESE LEFT RERENGGAN..JAVANESE TURNED PADA PISELEH -A9CF;N # Lm JAVANESE PANGRANGKEP -A9D0..A9D9;N # Nd [10] JAVANESE DIGIT ZERO..JAVANESE DIGIT NINE -A9DE..A9DF;N # Po [2] JAVANESE PADA TIRTA TUMETES..JAVANESE PADA ISEN-ISEN -A9E0..A9E4;N # Lo [5] MYANMAR LETTER SHAN GHA..MYANMAR LETTER SHAN BHA -A9E5;N # Mn MYANMAR SIGN SHAN SAW -A9E6;N # Lm MYANMAR MODIFIER LETTER SHAN REDUPLICATION -A9E7..A9EF;N # Lo [9] MYANMAR LETTER TAI LAING NYA..MYANMAR LETTER TAI LAING NNA -A9F0..A9F9;N # Nd [10] MYANMAR TAI LAING DIGIT ZERO..MYANMAR TAI LAING DIGIT NINE -A9FA..A9FE;N # Lo [5] MYANMAR LETTER TAI LAING LLA..MYANMAR LETTER TAI LAING BHA -AA00..AA28;N # Lo [41] CHAM LETTER A..CHAM LETTER HA -AA29..AA2E;N # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE -AA2F..AA30;N # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI -AA31..AA32;N # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE -AA33..AA34;N # Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA -AA35..AA36;N # Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA -AA40..AA42;N # Lo [3] CHAM LETTER FINAL K..CHAM LETTER FINAL NG -AA43;N # Mn CHAM CONSONANT SIGN FINAL NG -AA44..AA4B;N # Lo [8] CHAM LETTER FINAL CH..CHAM LETTER FINAL SS -AA4C;N # Mn CHAM CONSONANT SIGN FINAL M -AA4D;N # Mc CHAM CONSONANT SIGN FINAL H -AA50..AA59;N # Nd [10] CHAM DIGIT ZERO..CHAM DIGIT NINE -AA5C..AA5F;N # Po [4] CHAM PUNCTUATION SPIRAL..CHAM PUNCTUATION TRIPLE DANDA -AA60..AA6F;N # Lo [16] MYANMAR LETTER KHAMTI GA..MYANMAR LETTER KHAMTI FA -AA70;N # Lm MYANMAR MODIFIER LETTER KHAMTI REDUPLICATION -AA71..AA76;N # Lo [6] MYANMAR LETTER KHAMTI XA..MYANMAR LOGOGRAM KHAMTI HM -AA77..AA79;N # So [3] MYANMAR SYMBOL AITON EXCLAMATION..MYANMAR SYMBOL AITON TWO -AA7A;N # Lo MYANMAR LETTER AITON RA -AA7B;N # Mc MYANMAR SIGN PAO KAREN TONE -AA7C;N # Mn MYANMAR SIGN TAI LAING TONE-2 -AA7D;N # Mc MYANMAR SIGN TAI LAING TONE-5 -AA7E..AA7F;N # Lo [2] MYANMAR LETTER SHWE PALAUNG CHA..MYANMAR LETTER SHWE PALAUNG SHA -AA80..AAAF;N # Lo [48] TAI VIET LETTER LOW KO..TAI VIET LETTER HIGH O -AAB0;N # Mn TAI VIET MAI KANG -AAB1;N # Lo TAI VIET VOWEL AA -AAB2..AAB4;N # Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U -AAB5..AAB6;N # Lo [2] TAI VIET VOWEL E..TAI VIET VOWEL O -AAB7..AAB8;N # Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA -AAB9..AABD;N # Lo [5] TAI VIET VOWEL UEA..TAI VIET VOWEL AN -AABE..AABF;N # Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK -AAC0;N # Lo TAI VIET TONE MAI NUENG -AAC1;N # Mn TAI VIET TONE MAI THO -AAC2;N # Lo TAI VIET TONE MAI SONG -AADB..AADC;N # Lo [2] TAI VIET SYMBOL KON..TAI VIET SYMBOL NUENG -AADD;N # Lm TAI VIET SYMBOL SAM -AADE..AADF;N # Po [2] TAI VIET SYMBOL HO HOI..TAI VIET SYMBOL KOI KOI -AAE0..AAEA;N # Lo [11] MEETEI MAYEK LETTER E..MEETEI MAYEK LETTER SSA -AAEB;N # Mc MEETEI MAYEK VOWEL SIGN II -AAEC..AAED;N # Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI -AAEE..AAEF;N # Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU -AAF0..AAF1;N # Po [2] MEETEI MAYEK CHEIKHAN..MEETEI MAYEK AHANG KHUDAM -AAF2;N # Lo MEETEI MAYEK ANJI -AAF3..AAF4;N # Lm [2] MEETEI MAYEK SYLLABLE REPETITION MARK..MEETEI MAYEK WORD REPETITION MARK -AAF5;N # Mc MEETEI MAYEK VOWEL SIGN VISARGA -AAF6;N # Mn MEETEI MAYEK VIRAMA -AB01..AB06;N # Lo [6] ETHIOPIC SYLLABLE TTHU..ETHIOPIC SYLLABLE TTHO -AB09..AB0E;N # Lo [6] ETHIOPIC SYLLABLE DDHU..ETHIOPIC SYLLABLE DDHO -AB11..AB16;N # Lo [6] ETHIOPIC SYLLABLE DZU..ETHIOPIC SYLLABLE DZO -AB20..AB26;N # Lo [7] ETHIOPIC SYLLABLE CCHHA..ETHIOPIC SYLLABLE CCHHO -AB28..AB2E;N # Lo [7] ETHIOPIC SYLLABLE BBA..ETHIOPIC SYLLABLE BBO -AB30..AB5A;N # Ll [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMALL LETTER Y WITH SHORT RIGHT LEG -AB5B;N # Sk MODIFIER BREVE WITH INVERTED BREVE -AB5C..AB5F;N # Lm [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK -AB60..AB68;N # Ll [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE -AB69;N # Lm MODIFIER LETTER SMALL TURNED W -AB6A..AB6B;N # Sk [2] MODIFIER LETTER LEFT TACK..MODIFIER LETTER RIGHT TACK -AB70..ABBF;N # Ll [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA -ABC0..ABE2;N # Lo [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM -ABE3..ABE4;N # Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP -ABE5;N # Mn MEETEI MAYEK VOWEL SIGN ANAP -ABE6..ABE7;N # Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP -ABE8;N # Mn MEETEI MAYEK VOWEL SIGN UNAP -ABE9..ABEA;N # Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG -ABEB;N # Po MEETEI MAYEK CHEIKHEI -ABEC;N # Mc MEETEI MAYEK LUM IYEK -ABED;N # Mn MEETEI MAYEK APUN IYEK -ABF0..ABF9;N # Nd [10] MEETEI MAYEK DIGIT ZERO..MEETEI MAYEK DIGIT NINE -AC00..D7A3;W # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH -D7B0..D7C6;N # Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E -D7CB..D7FB;N # Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH -D800..DB7F;N # Cs [896] <surrogate-D800>..<surrogate-DB7F> -DB80..DBFF;N # Cs [128] <surrogate-DB80>..<surrogate-DBFF> -DC00..DFFF;N # Cs [1024] <surrogate-DC00>..<surrogate-DFFF> -E000..F8FF;A # Co [6400] <private-use-E000>..<private-use-F8FF> -F900..FA6D;W # Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA6D -FA6E..FA6F;W # Cn [2] <reserved-FA6E>..<reserved-FA6F> -FA70..FAD9;W # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9 -FADA..FAFF;W # Cn [38] <reserved-FADA>..<reserved-FAFF> -FB00..FB06;N # Ll [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST -FB13..FB17;N # Ll [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH -FB1D;N # Lo HEBREW LETTER YOD WITH HIRIQ -FB1E;N # Mn HEBREW POINT JUDEO-SPANISH VARIKA -FB1F..FB28;N # Lo [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV -FB29;N # Sm HEBREW LETTER ALTERNATIVE PLUS SIGN -FB2A..FB36;N # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH -FB38..FB3C;N # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH -FB3E;N # Lo HEBREW LETTER MEM WITH DAGESH -FB40..FB41;N # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH -FB43..FB44;N # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH -FB46..FB4F;N # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED -FB50..FBB1;N # Lo [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM -FBB2..FBC2;N # Sk [17] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL WASLA ABOVE -FBD3..FD3D;N # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM -FD3E;N # Pe ORNATE LEFT PARENTHESIS -FD3F;N # Ps ORNATE RIGHT PARENTHESIS -FD40..FD4F;N # So [16] ARABIC LIGATURE RAHIMAHU ALLAAH..ARABIC LIGATURE RAHIMAHUM ALLAAH -FD50..FD8F;N # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM -FD92..FDC7;N # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM -FDCF;N # So ARABIC LIGATURE SALAAMUHU ALAYNAA -FDF0..FDFB;N # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU -FDFC;N # Sc RIAL SIGN -FDFD..FDFF;N # So [3] ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM..ARABIC LIGATURE AZZA WA JALL -FE00..FE0F;A # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 -FE10..FE16;W # Po [7] PRESENTATION FORM FOR VERTICAL COMMA..PRESENTATION FORM FOR VERTICAL QUESTION MARK -FE17;W # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET -FE18;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET -FE19;W # Po PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS -FE20..FE2F;N # Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF -FE30;W # Po PRESENTATION FORM FOR VERTICAL TWO DOT LEADER -FE31..FE32;W # Pd [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH -FE33..FE34;W # Pc [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE -FE35;W # Ps PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS -FE36;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS -FE37;W # Ps PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET -FE38;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET -FE39;W # Ps PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET -FE3A;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET -FE3B;W # Ps PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET -FE3C;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET -FE3D;W # Ps PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET -FE3E;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET -FE3F;W # Ps PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET -FE40;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET -FE41;W # Ps PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET -FE42;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET -FE43;W # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET -FE44;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET -FE45..FE46;W # Po [2] SESAME DOT..WHITE SESAME DOT -FE47;W # Ps PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET -FE48;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET -FE49..FE4C;W # Po [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE -FE4D..FE4F;W # Pc [3] DASHED LOW LINE..WAVY LOW LINE -FE50..FE52;W # Po [3] SMALL COMMA..SMALL FULL STOP -FE54..FE57;W # Po [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK -FE58;W # Pd SMALL EM DASH -FE59;W # Ps SMALL LEFT PARENTHESIS -FE5A;W # Pe SMALL RIGHT PARENTHESIS -FE5B;W # Ps SMALL LEFT CURLY BRACKET -FE5C;W # Pe SMALL RIGHT CURLY BRACKET -FE5D;W # Ps SMALL LEFT TORTOISE SHELL BRACKET -FE5E;W # Pe SMALL RIGHT TORTOISE SHELL BRACKET -FE5F..FE61;W # Po [3] SMALL NUMBER SIGN..SMALL ASTERISK -FE62;W # Sm SMALL PLUS SIGN -FE63;W # Pd SMALL HYPHEN-MINUS -FE64..FE66;W # Sm [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN -FE68;W # Po SMALL REVERSE SOLIDUS -FE69;W # Sc SMALL DOLLAR SIGN -FE6A..FE6B;W # Po [2] SMALL PERCENT SIGN..SMALL COMMERCIAL AT -FE70..FE74;N # Lo [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN ISOLATED FORM -FE76..FEFC;N # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM -FEFF;N # Cf ZERO WIDTH NO-BREAK SPACE -FF01..FF03;F # Po [3] FULLWIDTH EXCLAMATION MARK..FULLWIDTH NUMBER SIGN -FF04;F # Sc FULLWIDTH DOLLAR SIGN -FF05..FF07;F # Po [3] FULLWIDTH PERCENT SIGN..FULLWIDTH APOSTROPHE -FF08;F # Ps FULLWIDTH LEFT PARENTHESIS -FF09;F # Pe FULLWIDTH RIGHT PARENTHESIS -FF0A;F # Po FULLWIDTH ASTERISK -FF0B;F # Sm FULLWIDTH PLUS SIGN -FF0C;F # Po FULLWIDTH COMMA -FF0D;F # Pd FULLWIDTH HYPHEN-MINUS -FF0E..FF0F;F # Po [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS -FF10..FF19;F # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE -FF1A..FF1B;F # Po [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON -FF1C..FF1E;F # Sm [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN -FF1F..FF20;F # Po [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT -FF21..FF3A;F # Lu [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z -FF3B;F # Ps FULLWIDTH LEFT SQUARE BRACKET -FF3C;F # Po FULLWIDTH REVERSE SOLIDUS -FF3D;F # Pe FULLWIDTH RIGHT SQUARE BRACKET -FF3E;F # Sk FULLWIDTH CIRCUMFLEX ACCENT -FF3F;F # Pc FULLWIDTH LOW LINE -FF40;F # Sk FULLWIDTH GRAVE ACCENT -FF41..FF5A;F # Ll [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z -FF5B;F # Ps FULLWIDTH LEFT CURLY BRACKET -FF5C;F # Sm FULLWIDTH VERTICAL LINE -FF5D;F # Pe FULLWIDTH RIGHT CURLY BRACKET -FF5E;F # Sm FULLWIDTH TILDE -FF5F;F # Ps FULLWIDTH LEFT WHITE PARENTHESIS -FF60;F # Pe FULLWIDTH RIGHT WHITE PARENTHESIS -FF61;H # Po HALFWIDTH IDEOGRAPHIC FULL STOP -FF62;H # Ps HALFWIDTH LEFT CORNER BRACKET -FF63;H # Pe HALFWIDTH RIGHT CORNER BRACKET -FF64..FF65;H # Po [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDLE DOT -FF66..FF6F;H # Lo [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU -FF70;H # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK -FF71..FF9D;H # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N -FF9E..FF9F;H # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK -FFA0..FFBE;H # Lo [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH -FFC2..FFC7;H # Lo [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E -FFCA..FFCF;H # Lo [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE -FFD2..FFD7;H # Lo [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU -FFDA..FFDC;H # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I -FFE0..FFE1;F # Sc [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN -FFE2;F # Sm FULLWIDTH NOT SIGN -FFE3;F # Sk FULLWIDTH MACRON -FFE4;F # So FULLWIDTH BROKEN BAR -FFE5..FFE6;F # Sc [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN -FFE8;H # So HALFWIDTH FORMS LIGHT VERTICAL -FFE9..FFEC;H # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW -FFED..FFEE;H # So [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE -FFF9..FFFB;N # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR -FFFC;N # So OBJECT REPLACEMENT CHARACTER -FFFD;A # So REPLACEMENT CHARACTER -10000..1000B;N # Lo [12] LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE -1000D..10026;N # Lo [26] LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO -10028..1003A;N # Lo [19] LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO -1003C..1003D;N # Lo [2] LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE -1003F..1004D;N # Lo [15] LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO -10050..1005D;N # Lo [14] LINEAR B SYMBOL B018..LINEAR B SYMBOL B089 -10080..100FA;N # Lo [123] LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305 -10100..10102;N # Po [3] AEGEAN WORD SEPARATOR LINE..AEGEAN CHECK MARK -10107..10133;N # No [45] AEGEAN NUMBER ONE..AEGEAN NUMBER NINETY THOUSAND -10137..1013F;N # So [9] AEGEAN WEIGHT BASE UNIT..AEGEAN MEASURE THIRD SUBUNIT -10140..10174;N # Nl [53] GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ACROPHONIC STRATIAN FIFTY MNAS -10175..10178;N # No [4] GREEK ONE HALF SIGN..GREEK THREE QUARTERS SIGN -10179..10189;N # So [17] GREEK YEAR SIGN..GREEK TRYBLION BASE SIGN -1018A..1018B;N # No [2] GREEK ZERO SIGN..GREEK ONE QUARTER SIGN -1018C..1018E;N # So [3] GREEK SINUSOID SIGN..NOMISMA SIGN -10190..1019C;N # So [13] ROMAN SEXTANS SIGN..ASCIA SYMBOL -101A0;N # So GREEK SYMBOL TAU RHO -101D0..101FC;N # So [45] PHAISTOS DISC SIGN PEDESTRIAN..PHAISTOS DISC SIGN WAVY BAND -101FD;N # Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE -10280..1029C;N # Lo [29] LYCIAN LETTER A..LYCIAN LETTER X -102A0..102D0;N # Lo [49] CARIAN LETTER A..CARIAN LETTER UUU3 -102E0;N # Mn COPTIC EPACT THOUSANDS MARK -102E1..102FB;N # No [27] COPTIC EPACT DIGIT ONE..COPTIC EPACT NUMBER NINE HUNDRED -10300..1031F;N # Lo [32] OLD ITALIC LETTER A..OLD ITALIC LETTER ESS -10320..10323;N # No [4] OLD ITALIC NUMERAL ONE..OLD ITALIC NUMERAL FIFTY -1032D..1032F;N # Lo [3] OLD ITALIC LETTER YE..OLD ITALIC LETTER SOUTHERN TSE -10330..10340;N # Lo [17] GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA -10341;N # Nl GOTHIC LETTER NINETY -10342..10349;N # Lo [8] GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL -1034A;N # Nl GOTHIC LETTER NINE HUNDRED -10350..10375;N # Lo [38] OLD PERMIC LETTER AN..OLD PERMIC LETTER IA -10376..1037A;N # Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII -10380..1039D;N # Lo [30] UGARITIC LETTER ALPA..UGARITIC LETTER SSU -1039F;N # Po UGARITIC WORD DIVIDER -103A0..103C3;N # Lo [36] OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA -103C8..103CF;N # Lo [8] OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH -103D0;N # Po OLD PERSIAN WORD DIVIDER -103D1..103D5;N # Nl [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED -10400..1044F;N # L& [80] DESERET CAPITAL LETTER LONG I..DESERET SMALL LETTER EW -10450..1047F;N # Lo [48] SHAVIAN LETTER PEEP..SHAVIAN LETTER YEW -10480..1049D;N # Lo [30] OSMANYA LETTER ALEF..OSMANYA LETTER OO -104A0..104A9;N # Nd [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE -104B0..104D3;N # Lu [36] OSAGE CAPITAL LETTER A..OSAGE CAPITAL LETTER ZHA -104D8..104FB;N # Ll [36] OSAGE SMALL LETTER A..OSAGE SMALL LETTER ZHA -10500..10527;N # Lo [40] ELBASAN LETTER A..ELBASAN LETTER KHE -10530..10563;N # Lo [52] CAUCASIAN ALBANIAN LETTER ALT..CAUCASIAN ALBANIAN LETTER KIW -1056F;N # Po CAUCASIAN ALBANIAN CITATION MARK -10570..1057A;N # Lu [11] VITHKUQI CAPITAL LETTER A..VITHKUQI CAPITAL LETTER GA -1057C..1058A;N # Lu [15] VITHKUQI CAPITAL LETTER HA..VITHKUQI CAPITAL LETTER RE -1058C..10592;N # Lu [7] VITHKUQI CAPITAL LETTER SE..VITHKUQI CAPITAL LETTER XE -10594..10595;N # Lu [2] VITHKUQI CAPITAL LETTER Y..VITHKUQI CAPITAL LETTER ZE -10597..105A1;N # Ll [11] VITHKUQI SMALL LETTER A..VITHKUQI SMALL LETTER GA -105A3..105B1;N # Ll [15] VITHKUQI SMALL LETTER HA..VITHKUQI SMALL LETTER RE -105B3..105B9;N # Ll [7] VITHKUQI SMALL LETTER SE..VITHKUQI SMALL LETTER XE -105BB..105BC;N # Ll [2] VITHKUQI SMALL LETTER Y..VITHKUQI SMALL LETTER ZE -10600..10736;N # Lo [311] LINEAR A SIGN AB001..LINEAR A SIGN A664 -10740..10755;N # Lo [22] LINEAR A SIGN A701 A..LINEAR A SIGN A732 JE -10760..10767;N # Lo [8] LINEAR A SIGN A800..LINEAR A SIGN A807 -10780..10785;N # Lm [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK -10787..107B0;N # Lm [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK -107B2..107BA;N # Lm [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL -10800..10805;N # Lo [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA -10808;N # Lo CYPRIOT SYLLABLE JO -1080A..10835;N # Lo [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO -10837..10838;N # Lo [2] CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE -1083C;N # Lo CYPRIOT SYLLABLE ZA -1083F;N # Lo CYPRIOT SYLLABLE ZO -10840..10855;N # Lo [22] IMPERIAL ARAMAIC LETTER ALEPH..IMPERIAL ARAMAIC LETTER TAW -10857;N # Po IMPERIAL ARAMAIC SECTION SIGN -10858..1085F;N # No [8] IMPERIAL ARAMAIC NUMBER ONE..IMPERIAL ARAMAIC NUMBER TEN THOUSAND -10860..10876;N # Lo [23] PALMYRENE LETTER ALEPH..PALMYRENE LETTER TAW -10877..10878;N # So [2] PALMYRENE LEFT-POINTING FLEURON..PALMYRENE RIGHT-POINTING FLEURON -10879..1087F;N # No [7] PALMYRENE NUMBER ONE..PALMYRENE NUMBER TWENTY -10880..1089E;N # Lo [31] NABATAEAN LETTER FINAL ALEPH..NABATAEAN LETTER TAW -108A7..108AF;N # No [9] NABATAEAN NUMBER ONE..NABATAEAN NUMBER ONE HUNDRED -108E0..108F2;N # Lo [19] HATRAN LETTER ALEPH..HATRAN LETTER QOPH -108F4..108F5;N # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW -108FB..108FF;N # No [5] HATRAN NUMBER ONE..HATRAN NUMBER ONE HUNDRED -10900..10915;N # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU -10916..1091B;N # No [6] PHOENICIAN NUMBER ONE..PHOENICIAN NUMBER THREE -1091F;N # Po PHOENICIAN WORD SEPARATOR -10920..10939;N # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C -1093F;N # Po LYDIAN TRIANGULAR MARK -10980..1099F;N # Lo [32] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2 -109A0..109B7;N # Lo [24] MEROITIC CURSIVE LETTER A..MEROITIC CURSIVE LETTER DA -109BC..109BD;N # No [2] MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS..MEROITIC CURSIVE FRACTION ONE HALF -109BE..109BF;N # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN -109C0..109CF;N # No [16] MEROITIC CURSIVE NUMBER ONE..MEROITIC CURSIVE NUMBER SEVENTY -109D2..109FF;N # No [46] MEROITIC CURSIVE NUMBER ONE HUNDRED..MEROITIC CURSIVE FRACTION TEN TWELFTHS -10A00;N # Lo KHAROSHTHI LETTER A -10A01..10A03;N # Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R -10A05..10A06;N # Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O -10A0C..10A0F;N # Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA -10A10..10A13;N # Lo [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA -10A15..10A17;N # Lo [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA -10A19..10A35;N # Lo [29] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER VHA -10A38..10A3A;N # Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW -10A3F;N # Mn KHAROSHTHI VIRAMA -10A40..10A48;N # No [9] KHAROSHTHI DIGIT ONE..KHAROSHTHI FRACTION ONE HALF -10A50..10A58;N # Po [9] KHAROSHTHI PUNCTUATION DOT..KHAROSHTHI PUNCTUATION LINES -10A60..10A7C;N # Lo [29] OLD SOUTH ARABIAN LETTER HE..OLD SOUTH ARABIAN LETTER THETH -10A7D..10A7E;N # No [2] OLD SOUTH ARABIAN NUMBER ONE..OLD SOUTH ARABIAN NUMBER FIFTY -10A7F;N # Po OLD SOUTH ARABIAN NUMERIC INDICATOR -10A80..10A9C;N # Lo [29] OLD NORTH ARABIAN LETTER HEH..OLD NORTH ARABIAN LETTER ZAH -10A9D..10A9F;N # No [3] OLD NORTH ARABIAN NUMBER ONE..OLD NORTH ARABIAN NUMBER TWENTY -10AC0..10AC7;N # Lo [8] MANICHAEAN LETTER ALEPH..MANICHAEAN LETTER WAW -10AC8;N # So MANICHAEAN SIGN UD -10AC9..10AE4;N # Lo [28] MANICHAEAN LETTER ZAYIN..MANICHAEAN LETTER TAW -10AE5..10AE6;N # Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW -10AEB..10AEF;N # No [5] MANICHAEAN NUMBER ONE..MANICHAEAN NUMBER ONE HUNDRED -10AF0..10AF6;N # Po [7] MANICHAEAN PUNCTUATION STAR..MANICHAEAN PUNCTUATION LINE FILLER -10B00..10B35;N # Lo [54] AVESTAN LETTER A..AVESTAN LETTER HE -10B39..10B3F;N # Po [7] AVESTAN ABBREVIATION MARK..LARGE ONE RING OVER TWO RINGS PUNCTUATION -10B40..10B55;N # Lo [22] INSCRIPTIONAL PARTHIAN LETTER ALEPH..INSCRIPTIONAL PARTHIAN LETTER TAW -10B58..10B5F;N # No [8] INSCRIPTIONAL PARTHIAN NUMBER ONE..INSCRIPTIONAL PARTHIAN NUMBER ONE THOUSAND -10B60..10B72;N # Lo [19] INSCRIPTIONAL PAHLAVI LETTER ALEPH..INSCRIPTIONAL PAHLAVI LETTER TAW -10B78..10B7F;N # No [8] INSCRIPTIONAL PAHLAVI NUMBER ONE..INSCRIPTIONAL PAHLAVI NUMBER ONE THOUSAND -10B80..10B91;N # Lo [18] PSALTER PAHLAVI LETTER ALEPH..PSALTER PAHLAVI LETTER TAW -10B99..10B9C;N # Po [4] PSALTER PAHLAVI SECTION MARK..PSALTER PAHLAVI FOUR DOTS WITH DOT -10BA9..10BAF;N # No [7] PSALTER PAHLAVI NUMBER ONE..PSALTER PAHLAVI NUMBER ONE HUNDRED -10C00..10C48;N # Lo [73] OLD TURKIC LETTER ORKHON A..OLD TURKIC LETTER ORKHON BASH -10C80..10CB2;N # Lu [51] OLD HUNGARIAN CAPITAL LETTER A..OLD HUNGARIAN CAPITAL LETTER US -10CC0..10CF2;N # Ll [51] OLD HUNGARIAN SMALL LETTER A..OLD HUNGARIAN SMALL LETTER US -10CFA..10CFF;N # No [6] OLD HUNGARIAN NUMBER ONE..OLD HUNGARIAN NUMBER ONE THOUSAND -10D00..10D23;N # Lo [36] HANIFI ROHINGYA LETTER A..HANIFI ROHINGYA MARK NA KHONNA -10D24..10D27;N # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI -10D30..10D39;N # Nd [10] HANIFI ROHINGYA DIGIT ZERO..HANIFI ROHINGYA DIGIT NINE -10E60..10E7E;N # No [31] RUMI DIGIT ONE..RUMI FRACTION TWO THIRDS -10E80..10EA9;N # Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET -10EAB..10EAC;N # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK -10EAD;N # Pd YEZIDI HYPHENATION MARK -10EB0..10EB1;N # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE -10EFD..10EFF;N # Mn [3] ARABIC SMALL LOW WORD SAKTA..ARABIC SMALL LOW WORD MADDA -10F00..10F1C;N # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL -10F1D..10F26;N # No [10] OLD SOGDIAN NUMBER ONE..OLD SOGDIAN FRACTION ONE HALF -10F27;N # Lo OLD SOGDIAN LIGATURE AYIN-DALETH -10F30..10F45;N # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN -10F46..10F50;N # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW -10F51..10F54;N # No [4] SOGDIAN NUMBER ONE..SOGDIAN NUMBER ONE HUNDRED -10F55..10F59;N # Po [5] SOGDIAN PUNCTUATION TWO VERTICAL BARS..SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT -10F70..10F81;N # Lo [18] OLD UYGHUR LETTER ALEPH..OLD UYGHUR LETTER LESH -10F82..10F85;N # Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW -10F86..10F89;N # Po [4] OLD UYGHUR PUNCTUATION BAR..OLD UYGHUR PUNCTUATION FOUR DOTS -10FB0..10FC4;N # Lo [21] CHORASMIAN LETTER ALEPH..CHORASMIAN LETTER TAW -10FC5..10FCB;N # No [7] CHORASMIAN NUMBER ONE..CHORASMIAN NUMBER ONE HUNDRED -10FE0..10FF6;N # Lo [23] ELYMAIC LETTER ALEPH..ELYMAIC LIGATURE ZAYIN-YODH -11000;N # Mc BRAHMI SIGN CANDRABINDU -11001;N # Mn BRAHMI SIGN ANUSVARA -11002;N # Mc BRAHMI SIGN VISARGA -11003..11037;N # Lo [53] BRAHMI SIGN JIHVAMULIYA..BRAHMI LETTER OLD TAMIL NNNA -11038..11046;N # Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA -11047..1104D;N # Po [7] BRAHMI DANDA..BRAHMI PUNCTUATION LOTUS -11052..11065;N # No [20] BRAHMI NUMBER ONE..BRAHMI NUMBER ONE THOUSAND -11066..1106F;N # Nd [10] BRAHMI DIGIT ZERO..BRAHMI DIGIT NINE -11070;N # Mn BRAHMI SIGN OLD TAMIL VIRAMA -11071..11072;N # Lo [2] BRAHMI LETTER OLD TAMIL SHORT E..BRAHMI LETTER OLD TAMIL SHORT O -11073..11074;N # Mn [2] BRAHMI VOWEL SIGN OLD TAMIL SHORT E..BRAHMI VOWEL SIGN OLD TAMIL SHORT O -11075;N # Lo BRAHMI LETTER OLD TAMIL LLA -1107F;N # Mn BRAHMI NUMBER JOINER -11080..11081;N # Mn [2] KAITHI SIGN CANDRABINDU..KAITHI SIGN ANUSVARA -11082;N # Mc KAITHI SIGN VISARGA -11083..110AF;N # Lo [45] KAITHI LETTER A..KAITHI LETTER HA -110B0..110B2;N # Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II -110B3..110B6;N # Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI -110B7..110B8;N # Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU -110B9..110BA;N # Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA -110BB..110BC;N # Po [2] KAITHI ABBREVIATION SIGN..KAITHI ENUMERATION SIGN -110BD;N # Cf KAITHI NUMBER SIGN -110BE..110C1;N # Po [4] KAITHI SECTION MARK..KAITHI DOUBLE DANDA -110C2;N # Mn KAITHI VOWEL SIGN VOCALIC R -110CD;N # Cf KAITHI NUMBER SIGN ABOVE -110D0..110E8;N # Lo [25] SORA SOMPENG LETTER SAH..SORA SOMPENG LETTER MAE -110F0..110F9;N # Nd [10] SORA SOMPENG DIGIT ZERO..SORA SOMPENG DIGIT NINE -11100..11102;N # Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA -11103..11126;N # Lo [36] CHAKMA LETTER AA..CHAKMA LETTER HAA -11127..1112B;N # Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU -1112C;N # Mc CHAKMA VOWEL SIGN E -1112D..11134;N # Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA -11136..1113F;N # Nd [10] CHAKMA DIGIT ZERO..CHAKMA DIGIT NINE -11140..11143;N # Po [4] CHAKMA SECTION MARK..CHAKMA QUESTION MARK -11144;N # Lo CHAKMA LETTER LHAA -11145..11146;N # Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI -11147;N # Lo CHAKMA LETTER VAA -11150..11172;N # Lo [35] MAHAJANI LETTER A..MAHAJANI LETTER RRA -11173;N # Mn MAHAJANI SIGN NUKTA -11174..11175;N # Po [2] MAHAJANI ABBREVIATION SIGN..MAHAJANI SECTION MARK -11176;N # Lo MAHAJANI LIGATURE SHRI -11180..11181;N # Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA -11182;N # Mc SHARADA SIGN VISARGA -11183..111B2;N # Lo [48] SHARADA LETTER A..SHARADA LETTER HA -111B3..111B5;N # Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II -111B6..111BE;N # Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O -111BF..111C0;N # Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA -111C1..111C4;N # Lo [4] SHARADA SIGN AVAGRAHA..SHARADA OM -111C5..111C8;N # Po [4] SHARADA DANDA..SHARADA SEPARATOR -111C9..111CC;N # Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK -111CD;N # Po SHARADA SUTRA MARK -111CE;N # Mc SHARADA VOWEL SIGN PRISHTHAMATRA E -111CF;N # Mn SHARADA SIGN INVERTED CANDRABINDU -111D0..111D9;N # Nd [10] SHARADA DIGIT ZERO..SHARADA DIGIT NINE -111DA;N # Lo SHARADA EKAM -111DB;N # Po SHARADA SIGN SIDDHAM -111DC;N # Lo SHARADA HEADSTROKE -111DD..111DF;N # Po [3] SHARADA CONTINUATION SIGN..SHARADA SECTION MARK-2 -111E1..111F4;N # No [20] SINHALA ARCHAIC DIGIT ONE..SINHALA ARCHAIC NUMBER ONE THOUSAND -11200..11211;N # Lo [18] KHOJKI LETTER A..KHOJKI LETTER JJA -11213..1122B;N # Lo [25] KHOJKI LETTER NYA..KHOJKI LETTER LLA -1122C..1122E;N # Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II -1122F..11231;N # Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI -11232..11233;N # Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU -11234;N # Mn KHOJKI SIGN ANUSVARA -11235;N # Mc KHOJKI SIGN VIRAMA -11236..11237;N # Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA -11238..1123D;N # Po [6] KHOJKI DANDA..KHOJKI ABBREVIATION SIGN -1123E;N # Mn KHOJKI SIGN SUKUN -1123F..11240;N # Lo [2] KHOJKI LETTER QA..KHOJKI LETTER SHORT I -11241;N # Mn KHOJKI VOWEL SIGN VOCALIC R -11280..11286;N # Lo [7] MULTANI LETTER A..MULTANI LETTER GA -11288;N # Lo MULTANI LETTER GHA -1128A..1128D;N # Lo [4] MULTANI LETTER CA..MULTANI LETTER JJA -1128F..1129D;N # Lo [15] MULTANI LETTER NYA..MULTANI LETTER BA -1129F..112A8;N # Lo [10] MULTANI LETTER BHA..MULTANI LETTER RHA -112A9;N # Po MULTANI SECTION MARK -112B0..112DE;N # Lo [47] KHUDAWADI LETTER A..KHUDAWADI LETTER HA -112DF;N # Mn KHUDAWADI SIGN ANUSVARA -112E0..112E2;N # Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II -112E3..112EA;N # Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA -112F0..112F9;N # Nd [10] KHUDAWADI DIGIT ZERO..KHUDAWADI DIGIT NINE -11300..11301;N # Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU -11302..11303;N # Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA -11305..1130C;N # Lo [8] GRANTHA LETTER A..GRANTHA LETTER VOCALIC L -1130F..11310;N # Lo [2] GRANTHA LETTER EE..GRANTHA LETTER AI -11313..11328;N # Lo [22] GRANTHA LETTER OO..GRANTHA LETTER NA -1132A..11330;N # Lo [7] GRANTHA LETTER PA..GRANTHA LETTER RA -11332..11333;N # Lo [2] GRANTHA LETTER LA..GRANTHA LETTER LLA -11335..11339;N # Lo [5] GRANTHA LETTER VA..GRANTHA LETTER HA -1133B..1133C;N # Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA -1133D;N # Lo GRANTHA SIGN AVAGRAHA -1133E..1133F;N # Mc [2] GRANTHA VOWEL SIGN AA..GRANTHA VOWEL SIGN I -11340;N # Mn GRANTHA VOWEL SIGN II -11341..11344;N # Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR -11347..11348;N # Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI -1134B..1134D;N # Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA -11350;N # Lo GRANTHA OM -11357;N # Mc GRANTHA AU LENGTH MARK -1135D..11361;N # Lo [5] GRANTHA SIGN PLUTA..GRANTHA LETTER VOCALIC LL -11362..11363;N # Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL -11366..1136C;N # Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX -11370..11374;N # Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA -11400..11434;N # Lo [53] NEWA LETTER A..NEWA LETTER HA -11435..11437;N # Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II -11438..1143F;N # Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI -11440..11441;N # Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU -11442..11444;N # Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA -11445;N # Mc NEWA SIGN VISARGA -11446;N # Mn NEWA SIGN NUKTA -11447..1144A;N # Lo [4] NEWA SIGN AVAGRAHA..NEWA SIDDHI -1144B..1144F;N # Po [5] NEWA DANDA..NEWA ABBREVIATION SIGN -11450..11459;N # Nd [10] NEWA DIGIT ZERO..NEWA DIGIT NINE -1145A..1145B;N # Po [2] NEWA DOUBLE COMMA..NEWA PLACEHOLDER MARK -1145D;N # Po NEWA INSERTION SIGN -1145E;N # Mn NEWA SANDHI MARK -1145F..11461;N # Lo [3] NEWA LETTER VEDIC ANUSVARA..NEWA SIGN UPADHMANIYA -11480..114AF;N # Lo [48] TIRHUTA ANJI..TIRHUTA LETTER HA -114B0..114B2;N # Mc [3] TIRHUTA VOWEL SIGN AA..TIRHUTA VOWEL SIGN II -114B3..114B8;N # Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL -114B9;N # Mc TIRHUTA VOWEL SIGN E -114BA;N # Mn TIRHUTA VOWEL SIGN SHORT E -114BB..114BE;N # Mc [4] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN AU -114BF..114C0;N # Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA -114C1;N # Mc TIRHUTA SIGN VISARGA -114C2..114C3;N # Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA -114C4..114C5;N # Lo [2] TIRHUTA SIGN AVAGRAHA..TIRHUTA GVANG -114C6;N # Po TIRHUTA ABBREVIATION SIGN -114C7;N # Lo TIRHUTA OM -114D0..114D9;N # Nd [10] TIRHUTA DIGIT ZERO..TIRHUTA DIGIT NINE -11580..115AE;N # Lo [47] SIDDHAM LETTER A..SIDDHAM LETTER HA -115AF..115B1;N # Mc [3] SIDDHAM VOWEL SIGN AA..SIDDHAM VOWEL SIGN II -115B2..115B5;N # Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR -115B8..115BB;N # Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU -115BC..115BD;N # Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA -115BE;N # Mc SIDDHAM SIGN VISARGA -115BF..115C0;N # Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA -115C1..115D7;N # Po [23] SIDDHAM SIGN SIDDHAM..SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES -115D8..115DB;N # Lo [4] SIDDHAM LETTER THREE-CIRCLE ALTERNATE I..SIDDHAM LETTER ALTERNATE U -115DC..115DD;N # Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU -11600..1162F;N # Lo [48] MODI LETTER A..MODI LETTER LLA -11630..11632;N # Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II -11633..1163A;N # Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI -1163B..1163C;N # Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU -1163D;N # Mn MODI SIGN ANUSVARA -1163E;N # Mc MODI SIGN VISARGA -1163F..11640;N # Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA -11641..11643;N # Po [3] MODI DANDA..MODI ABBREVIATION SIGN -11644;N # Lo MODI SIGN HUVA -11650..11659;N # Nd [10] MODI DIGIT ZERO..MODI DIGIT NINE -11660..1166C;N # Po [13] MONGOLIAN BIRGA WITH ORNAMENT..MONGOLIAN TURNED SWIRL BIRGA WITH DOUBLE ORNAMENT -11680..116AA;N # Lo [43] TAKRI LETTER A..TAKRI LETTER RRA -116AB;N # Mn TAKRI SIGN ANUSVARA -116AC;N # Mc TAKRI SIGN VISARGA -116AD;N # Mn TAKRI VOWEL SIGN AA -116AE..116AF;N # Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II -116B0..116B5;N # Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU -116B6;N # Mc TAKRI SIGN VIRAMA -116B7;N # Mn TAKRI SIGN NUKTA -116B8;N # Lo TAKRI LETTER ARCHAIC KHA -116B9;N # Po TAKRI ABBREVIATION SIGN -116C0..116C9;N # Nd [10] TAKRI DIGIT ZERO..TAKRI DIGIT NINE -11700..1171A;N # Lo [27] AHOM LETTER KA..AHOM LETTER ALTERNATE BA -1171D..1171F;N # Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA -11720..11721;N # Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA -11722..11725;N # Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU -11726;N # Mc AHOM VOWEL SIGN E -11727..1172B;N # Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER -11730..11739;N # Nd [10] AHOM DIGIT ZERO..AHOM DIGIT NINE -1173A..1173B;N # No [2] AHOM NUMBER TEN..AHOM NUMBER TWENTY -1173C..1173E;N # Po [3] AHOM SIGN SMALL SECTION..AHOM SIGN RULAI -1173F;N # So AHOM SYMBOL VI -11740..11746;N # Lo [7] AHOM LETTER CA..AHOM LETTER LLA -11800..1182B;N # Lo [44] DOGRA LETTER A..DOGRA LETTER RRA -1182C..1182E;N # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II -1182F..11837;N # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA -11838;N # Mc DOGRA SIGN VISARGA -11839..1183A;N # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA -1183B;N # Po DOGRA ABBREVIATION SIGN -118A0..118DF;N # L& [64] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI SMALL LETTER VIYO -118E0..118E9;N # Nd [10] WARANG CITI DIGIT ZERO..WARANG CITI DIGIT NINE -118EA..118F2;N # No [9] WARANG CITI NUMBER TEN..WARANG CITI NUMBER NINETY -118FF;N # Lo WARANG CITI OM -11900..11906;N # Lo [7] DIVES AKURU LETTER A..DIVES AKURU LETTER E -11909;N # Lo DIVES AKURU LETTER O -1190C..11913;N # Lo [8] DIVES AKURU LETTER KA..DIVES AKURU LETTER JA -11915..11916;N # Lo [2] DIVES AKURU LETTER NYA..DIVES AKURU LETTER TTA -11918..1192F;N # Lo [24] DIVES AKURU LETTER DDA..DIVES AKURU LETTER ZA -11930..11935;N # Mc [6] DIVES AKURU VOWEL SIGN AA..DIVES AKURU VOWEL SIGN E -11937..11938;N # Mc [2] DIVES AKURU VOWEL SIGN AI..DIVES AKURU VOWEL SIGN O -1193B..1193C;N # Mn [2] DIVES AKURU SIGN ANUSVARA..DIVES AKURU SIGN CANDRABINDU -1193D;N # Mc DIVES AKURU SIGN HALANTA -1193E;N # Mn DIVES AKURU VIRAMA -1193F;N # Lo DIVES AKURU PREFIXED NASAL SIGN -11940;N # Mc DIVES AKURU MEDIAL YA -11941;N # Lo DIVES AKURU INITIAL RA -11942;N # Mc DIVES AKURU MEDIAL RA -11943;N # Mn DIVES AKURU SIGN NUKTA -11944..11946;N # Po [3] DIVES AKURU DOUBLE DANDA..DIVES AKURU END OF TEXT MARK -11950..11959;N # Nd [10] DIVES AKURU DIGIT ZERO..DIVES AKURU DIGIT NINE -119A0..119A7;N # Lo [8] NANDINAGARI LETTER A..NANDINAGARI LETTER VOCALIC RR -119AA..119D0;N # Lo [39] NANDINAGARI LETTER E..NANDINAGARI LETTER RRA -119D1..119D3;N # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II -119D4..119D7;N # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR -119DA..119DB;N # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI -119DC..119DF;N # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA -119E0;N # Mn NANDINAGARI SIGN VIRAMA -119E1;N # Lo NANDINAGARI SIGN AVAGRAHA -119E2;N # Po NANDINAGARI SIGN SIDDHAM -119E3;N # Lo NANDINAGARI HEADSTROKE -119E4;N # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E -11A00;N # Lo ZANABAZAR SQUARE LETTER A -11A01..11A0A;N # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK -11A0B..11A32;N # Lo [40] ZANABAZAR SQUARE LETTER KA..ZANABAZAR SQUARE LETTER KSSA -11A33..11A38;N # Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA -11A39;N # Mc ZANABAZAR SQUARE SIGN VISARGA -11A3A;N # Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA -11A3B..11A3E;N # Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA -11A3F..11A46;N # Po [8] ZANABAZAR SQUARE INITIAL HEAD MARK..ZANABAZAR SQUARE CLOSING DOUBLE-LINED HEAD MARK -11A47;N # Mn ZANABAZAR SQUARE SUBJOINER -11A50;N # Lo SOYOMBO LETTER A -11A51..11A56;N # Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE -11A57..11A58;N # Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU -11A59..11A5B;N # Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK -11A5C..11A89;N # Lo [46] SOYOMBO LETTER KA..SOYOMBO CLUSTER-INITIAL LETTER SA -11A8A..11A96;N # Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA -11A97;N # Mc SOYOMBO SIGN VISARGA -11A98..11A99;N # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER -11A9A..11A9C;N # Po [3] SOYOMBO MARK TSHEG..SOYOMBO MARK DOUBLE SHAD -11A9D;N # Lo SOYOMBO MARK PLUTA -11A9E..11AA2;N # Po [5] SOYOMBO HEAD MARK WITH MOON AND SUN AND TRIPLE FLAME..SOYOMBO TERMINAL MARK-2 -11AB0..11ABF;N # Lo [16] CANADIAN SYLLABICS NATTILIK HI..CANADIAN SYLLABICS SPA -11AC0..11AF8;N # Lo [57] PAU CIN HAU LETTER PA..PAU CIN HAU GLOTTAL STOP FINAL -11B00..11B09;N # Po [10] DEVANAGARI HEAD MARK..DEVANAGARI SIGN MINDU -11C00..11C08;N # Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L -11C0A..11C2E;N # Lo [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA -11C2F;N # Mc BHAIKSUKI VOWEL SIGN AA -11C30..11C36;N # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L -11C38..11C3D;N # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA -11C3E;N # Mc BHAIKSUKI SIGN VISARGA -11C3F;N # Mn BHAIKSUKI SIGN VIRAMA -11C40;N # Lo BHAIKSUKI SIGN AVAGRAHA -11C41..11C45;N # Po [5] BHAIKSUKI DANDA..BHAIKSUKI GAP FILLER-2 -11C50..11C59;N # Nd [10] BHAIKSUKI DIGIT ZERO..BHAIKSUKI DIGIT NINE -11C5A..11C6C;N # No [19] BHAIKSUKI NUMBER ONE..BHAIKSUKI HUNDREDS UNIT MARK -11C70..11C71;N # Po [2] MARCHEN HEAD MARK..MARCHEN MARK SHAD -11C72..11C8F;N # Lo [30] MARCHEN LETTER KA..MARCHEN LETTER A -11C92..11CA7;N # Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA -11CA9;N # Mc MARCHEN SUBJOINED LETTER YA -11CAA..11CB0;N # Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA -11CB1;N # Mc MARCHEN VOWEL SIGN I -11CB2..11CB3;N # Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E -11CB4;N # Mc MARCHEN VOWEL SIGN O -11CB5..11CB6;N # Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU -11D00..11D06;N # Lo [7] MASARAM GONDI LETTER A..MASARAM GONDI LETTER E -11D08..11D09;N # Lo [2] MASARAM GONDI LETTER AI..MASARAM GONDI LETTER O -11D0B..11D30;N # Lo [38] MASARAM GONDI LETTER AU..MASARAM GONDI LETTER TRA -11D31..11D36;N # Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R -11D3A;N # Mn MASARAM GONDI VOWEL SIGN E -11D3C..11D3D;N # Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O -11D3F..11D45;N # Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA -11D46;N # Lo MASARAM GONDI REPHA -11D47;N # Mn MASARAM GONDI RA-KARA -11D50..11D59;N # Nd [10] MASARAM GONDI DIGIT ZERO..MASARAM GONDI DIGIT NINE -11D60..11D65;N # Lo [6] GUNJALA GONDI LETTER A..GUNJALA GONDI LETTER UU -11D67..11D68;N # Lo [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI -11D6A..11D89;N # Lo [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA -11D8A..11D8E;N # Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU -11D90..11D91;N # Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI -11D93..11D94;N # Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU -11D95;N # Mn GUNJALA GONDI SIGN ANUSVARA -11D96;N # Mc GUNJALA GONDI SIGN VISARGA -11D97;N # Mn GUNJALA GONDI VIRAMA -11D98;N # Lo GUNJALA GONDI OM -11DA0..11DA9;N # Nd [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE -11EE0..11EF2;N # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA -11EF3..11EF4;N # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U -11EF5..11EF6;N # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O -11EF7..11EF8;N # Po [2] MAKASAR PASSIMBANG..MAKASAR END OF SECTION -11F00..11F01;N # Mn [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA -11F02;N # Lo KAWI SIGN REPHA -11F03;N # Mc KAWI SIGN VISARGA -11F04..11F10;N # Lo [13] KAWI LETTER A..KAWI LETTER O -11F12..11F33;N # Lo [34] KAWI LETTER KA..KAWI LETTER JNYA -11F34..11F35;N # Mc [2] KAWI VOWEL SIGN AA..KAWI VOWEL SIGN ALTERNATE AA -11F36..11F3A;N # Mn [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R -11F3E..11F3F;N # Mc [2] KAWI VOWEL SIGN E..KAWI VOWEL SIGN AI -11F40;N # Mn KAWI VOWEL SIGN EU -11F41;N # Mc KAWI SIGN KILLER -11F42;N # Mn KAWI CONJOINER -11F43..11F4F;N # Po [13] KAWI DANDA..KAWI PUNCTUATION CLOSING SPIRAL -11F50..11F59;N # Nd [10] KAWI DIGIT ZERO..KAWI DIGIT NINE -11FB0;N # Lo LISU LETTER YHA -11FC0..11FD4;N # No [21] TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH..TAMIL FRACTION DOWNSCALING FACTOR KIIZH -11FD5..11FDC;N # So [8] TAMIL SIGN NEL..TAMIL SIGN MUKKURUNI -11FDD..11FE0;N # Sc [4] TAMIL SIGN KAACU..TAMIL SIGN VARAAKAN -11FE1..11FF1;N # So [17] TAMIL SIGN PAARAM..TAMIL SIGN VAKAIYARAA -11FFF;N # Po TAMIL PUNCTUATION END OF TEXT -12000..12399;N # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U -12400..1246E;N # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM -12470..12474;N # Po [5] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON -12480..12543;N # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU -12F90..12FF0;N # Lo [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114 -12FF1..12FF2;N # Po [2] CYPRO-MINOAN SIGN CM301..CYPRO-MINOAN SIGN CM302 -13000..1342F;N # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D -13430..1343F;N # Cf [16] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE -13440;N # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY -13441..13446;N # Lo [6] EGYPTIAN HIEROGLYPH FULL BLANK..EGYPTIAN HIEROGLYPH WIDE LOST SIGN -13447..13455;N # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED -14400..14646;N # Lo [583] ANATOLIAN HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530 -16800..16A38;N # Lo [569] BAMUM LETTER PHASE-A NGKUE MFON..BAMUM LETTER PHASE-F VUEQ -16A40..16A5E;N # Lo [31] MRO LETTER TA..MRO LETTER TEK -16A60..16A69;N # Nd [10] MRO DIGIT ZERO..MRO DIGIT NINE -16A6E..16A6F;N # Po [2] MRO DANDA..MRO DOUBLE DANDA -16A70..16ABE;N # Lo [79] TANGSA LETTER OZ..TANGSA LETTER ZA -16AC0..16AC9;N # Nd [10] TANGSA DIGIT ZERO..TANGSA DIGIT NINE -16AD0..16AED;N # Lo [30] BASSA VAH LETTER ENNI..BASSA VAH LETTER I -16AF0..16AF4;N # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE -16AF5;N # Po BASSA VAH FULL STOP -16B00..16B2F;N # Lo [48] PAHAWH HMONG VOWEL KEEB..PAHAWH HMONG CONSONANT CAU -16B30..16B36;N # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM -16B37..16B3B;N # Po [5] PAHAWH HMONG SIGN VOS THOM..PAHAWH HMONG SIGN VOS FEEM -16B3C..16B3F;N # So [4] PAHAWH HMONG SIGN XYEEM NTXIV..PAHAWH HMONG SIGN XYEEM FAIB -16B40..16B43;N # Lm [4] PAHAWH HMONG SIGN VOS SEEV..PAHAWH HMONG SIGN IB YAM -16B44;N # Po PAHAWH HMONG SIGN XAUS -16B45;N # So PAHAWH HMONG SIGN CIM TSOV ROG -16B50..16B59;N # Nd [10] PAHAWH HMONG DIGIT ZERO..PAHAWH HMONG DIGIT NINE -16B5B..16B61;N # No [7] PAHAWH HMONG NUMBER TENS..PAHAWH HMONG NUMBER TRILLIONS -16B63..16B77;N # Lo [21] PAHAWH HMONG SIGN VOS LUB..PAHAWH HMONG SIGN CIM NRES TOS -16B7D..16B8F;N # Lo [19] PAHAWH HMONG CLAN SIGN TSHEEJ..PAHAWH HMONG CLAN SIGN VWJ -16E40..16E7F;N # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y -16E80..16E96;N # No [23] MEDEFAIDRIN DIGIT ZERO..MEDEFAIDRIN DIGIT THREE ALTERNATE FORM -16E97..16E9A;N # Po [4] MEDEFAIDRIN COMMA..MEDEFAIDRIN EXCLAMATION OH -16F00..16F4A;N # Lo [75] MIAO LETTER PA..MIAO LETTER RTE -16F4F;N # Mn MIAO SIGN CONSONANT MODIFIER BAR -16F50;N # Lo MIAO LETTER NASALIZATION -16F51..16F87;N # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI -16F8F..16F92;N # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW -16F93..16F9F;N # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8 -16FE0..16FE1;W # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK -16FE2;W # Po OLD CHINESE HOOK MARK -16FE3;W # Lm OLD CHINESE ITERATION MARK -16FE4;W # Mn KHITAN SMALL SCRIPT FILLER -16FF0..16FF1;W # Mc [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY -17000..187F7;W # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7 -18800..18AFF;W # Lo [768] TANGUT COMPONENT-001..TANGUT COMPONENT-768 -18B00..18CD5;W # Lo [470] KHITAN SMALL SCRIPT CHARACTER-18B00..KHITAN SMALL SCRIPT CHARACTER-18CD5 -18D00..18D08;W # Lo [9] TANGUT IDEOGRAPH-18D00..TANGUT IDEOGRAPH-18D08 -1AFF0..1AFF3;W # Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5 -1AFF5..1AFFB;W # Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5 -1AFFD..1AFFE;W # Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8 -1B000..1B0FF;W # Lo [256] KATAKANA LETTER ARCHAIC E..HENTAIGANA LETTER RE-2 -1B100..1B122;W # Lo [35] HENTAIGANA LETTER RE-3..KATAKANA LETTER ARCHAIC WU -1B132;W # Lo HIRAGANA LETTER SMALL KO -1B150..1B152;W # Lo [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO -1B155;W # Lo KATAKANA LETTER SMALL KO -1B164..1B167;W # Lo [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N -1B170..1B2FB;W # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB -1BC00..1BC6A;N # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M -1BC70..1BC7C;N # Lo [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK -1BC80..1BC88;N # Lo [9] DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL -1BC90..1BC99;N # Lo [10] DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW -1BC9C;N # So DUPLOYAN SIGN O WITH CROSS -1BC9D..1BC9E;N # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK -1BC9F;N # Po DUPLOYAN PUNCTUATION CHINOOK FULL STOP -1BCA0..1BCA3;N # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP -1CF00..1CF2D;N # Mn [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT -1CF30..1CF46;N # Mn [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG -1CF50..1CFC3;N # So [116] ZNAMENNY NEUME KRYUK..ZNAMENNY NEUME PAUK -1D000..1D0F5;N # So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO -1D100..1D126;N # So [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2 -1D129..1D164;N # So [60] MUSICAL SYMBOL MULTIPLE MEASURE REST..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE -1D165..1D166;N # Mc [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM -1D167..1D169;N # Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 -1D16A..1D16C;N # So [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3 -1D16D..1D172;N # Mc [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5 -1D173..1D17A;N # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE -1D17B..1D182;N # Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE -1D183..1D184;N # So [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN -1D185..1D18B;N # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE -1D18C..1D1A9;N # So [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH -1D1AA..1D1AD;N # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO -1D1AE..1D1EA;N # So [61] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL KORON -1D200..1D241;N # So [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54 -1D242..1D244;N # Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME -1D245;N # So GREEK MUSICAL LEIMMA -1D2C0..1D2D3;N # No [20] KAKTOVIK NUMERAL ZERO..KAKTOVIK NUMERAL NINETEEN -1D2E0..1D2F3;N # No [20] MAYAN NUMERAL ZERO..MAYAN NUMERAL NINETEEN -1D300..1D356;N # So [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING -1D360..1D378;N # No [25] COUNTING ROD UNIT DIGIT ONE..TALLY MARK FIVE -1D400..1D454;N # L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G -1D456..1D49C;N # L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A -1D49E..1D49F;N # Lu [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D -1D4A2;N # Lu MATHEMATICAL SCRIPT CAPITAL G -1D4A5..1D4A6;N # Lu [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K -1D4A9..1D4AC;N # Lu [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q -1D4AE..1D4B9;N # L& [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D -1D4BB;N # Ll MATHEMATICAL SCRIPT SMALL F -1D4BD..1D4C3;N # Ll [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N -1D4C5..1D505;N # L& [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B -1D507..1D50A;N # Lu [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G -1D50D..1D514;N # Lu [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q -1D516..1D51C;N # Lu [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y -1D51E..1D539;N # L& [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B -1D53B..1D53E;N # Lu [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G -1D540..1D544;N # Lu [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M -1D546;N # Lu MATHEMATICAL DOUBLE-STRUCK CAPITAL O -1D54A..1D550;N # Lu [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y -1D552..1D6A5;N # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J -1D6A8..1D6C0;N # Lu [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA -1D6C1;N # Sm MATHEMATICAL BOLD NABLA -1D6C2..1D6DA;N # Ll [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA -1D6DB;N # Sm MATHEMATICAL BOLD PARTIAL DIFFERENTIAL -1D6DC..1D6FA;N # L& [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA -1D6FB;N # Sm MATHEMATICAL ITALIC NABLA -1D6FC..1D714;N # Ll [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA -1D715;N # Sm MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL -1D716..1D734;N # L& [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA -1D735;N # Sm MATHEMATICAL BOLD ITALIC NABLA -1D736..1D74E;N # Ll [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA -1D74F;N # Sm MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL -1D750..1D76E;N # L& [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA -1D76F;N # Sm MATHEMATICAL SANS-SERIF BOLD NABLA -1D770..1D788;N # Ll [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA -1D789;N # Sm MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL -1D78A..1D7A8;N # L& [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA -1D7A9;N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA -1D7AA..1D7C2;N # Ll [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA -1D7C3;N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL -1D7C4..1D7CB;N # L& [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA -1D7CE..1D7FF;N # Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE -1D800..1D9FF;N # So [512] SIGNWRITING HAND-FIST INDEX..SIGNWRITING HEAD -1DA00..1DA36;N # Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN -1DA37..1DA3A;N # So [4] SIGNWRITING AIR BLOW SMALL ROTATIONS..SIGNWRITING BREATH EXHALE -1DA3B..1DA6C;N # Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT -1DA6D..1DA74;N # So [8] SIGNWRITING SHOULDER HIP SPINE..SIGNWRITING TORSO-FLOORPLANE TWISTING -1DA75;N # Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS -1DA76..1DA83;N # So [14] SIGNWRITING LIMB COMBINATION..SIGNWRITING LOCATION DEPTH -1DA84;N # Mn SIGNWRITING LOCATION HEAD NECK -1DA85..1DA86;N # So [2] SIGNWRITING LOCATION TORSO..SIGNWRITING LOCATION LIMBS DIGITS -1DA87..1DA8B;N # Po [5] SIGNWRITING COMMA..SIGNWRITING PARENTHESIS -1DA9B..1DA9F;N # Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6 -1DAA1..1DAAF;N # Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16 -1DF00..1DF09;N # Ll [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK -1DF0A;N # Lo LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK -1DF0B..1DF1E;N # Ll [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL -1DF25..1DF2A;N # Ll [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK -1E000..1E006;N # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE -1E008..1E018;N # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU -1E01B..1E021;N # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI -1E023..1E024;N # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS -1E026..1E02A;N # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA -1E030..1E06D;N # Lm [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE -1E08F;N # Mn COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I -1E100..1E12C;N # Lo [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W -1E130..1E136;N # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D -1E137..1E13D;N # Lm [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER -1E140..1E149;N # Nd [10] NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE -1E14E;N # Lo NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ -1E14F;N # So NYIAKENG PUACHUE HMONG CIRCLED CA -1E290..1E2AD;N # Lo [30] TOTO LETTER PA..TOTO LETTER A -1E2AE;N # Mn TOTO SIGN RISING TONE -1E2C0..1E2EB;N # Lo [44] WANCHO LETTER AA..WANCHO LETTER YIH -1E2EC..1E2EF;N # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI -1E2F0..1E2F9;N # Nd [10] WANCHO DIGIT ZERO..WANCHO DIGIT NINE -1E2FF;N # Sc WANCHO NGUN SIGN -1E4D0..1E4EA;N # Lo [27] NAG MUNDARI LETTER O..NAG MUNDARI LETTER ELL -1E4EB;N # Lm NAG MUNDARI SIGN OJOD -1E4EC..1E4EF;N # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH -1E4F0..1E4F9;N # Nd [10] NAG MUNDARI DIGIT ZERO..NAG MUNDARI DIGIT NINE -1E7E0..1E7E6;N # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO -1E7E8..1E7EB;N # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE -1E7ED..1E7EE;N # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE -1E7F0..1E7FE;N # Lo [15] ETHIOPIC SYLLABLE GURAGE QWI..ETHIOPIC SYLLABLE GURAGE PWEE -1E800..1E8C4;N # Lo [197] MENDE KIKAKUI SYLLABLE M001 KI..MENDE KIKAKUI SYLLABLE M060 NYON -1E8C7..1E8CF;N # No [9] MENDE KIKAKUI DIGIT ONE..MENDE KIKAKUI DIGIT NINE -1E8D0..1E8D6;N # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS -1E900..1E943;N # L& [68] ADLAM CAPITAL LETTER ALIF..ADLAM SMALL LETTER SHA -1E944..1E94A;N # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA -1E94B;N # Lm ADLAM NASALIZATION MARK -1E950..1E959;N # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE -1E95E..1E95F;N # Po [2] ADLAM INITIAL EXCLAMATION MARK..ADLAM INITIAL QUESTION MARK -1EC71..1ECAB;N # No [59] INDIC SIYAQ NUMBER ONE..INDIC SIYAQ NUMBER PREFIXED NINE -1ECAC;N # So INDIC SIYAQ PLACEHOLDER -1ECAD..1ECAF;N # No [3] INDIC SIYAQ FRACTION ONE QUARTER..INDIC SIYAQ FRACTION THREE QUARTERS -1ECB0;N # Sc INDIC SIYAQ RUPEE MARK -1ECB1..1ECB4;N # No [4] INDIC SIYAQ NUMBER ALTERNATE ONE..INDIC SIYAQ ALTERNATE LAKH MARK -1ED01..1ED2D;N # No [45] OTTOMAN SIYAQ NUMBER ONE..OTTOMAN SIYAQ NUMBER NINETY THOUSAND -1ED2E;N # So OTTOMAN SIYAQ MARRATAN -1ED2F..1ED3D;N # No [15] OTTOMAN SIYAQ ALTERNATE NUMBER TWO..OTTOMAN SIYAQ FRACTION ONE SIXTH -1EE00..1EE03;N # Lo [4] ARABIC MATHEMATICAL ALEF..ARABIC MATHEMATICAL DAL -1EE05..1EE1F;N # Lo [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL DOTLESS QAF -1EE21..1EE22;N # Lo [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC MATHEMATICAL INITIAL JEEM -1EE24;N # Lo ARABIC MATHEMATICAL INITIAL HEH -1EE27;N # Lo ARABIC MATHEMATICAL INITIAL HAH -1EE29..1EE32;N # Lo [10] ARABIC MATHEMATICAL INITIAL YEH..ARABIC MATHEMATICAL INITIAL QAF -1EE34..1EE37;N # Lo [4] ARABIC MATHEMATICAL INITIAL SHEEN..ARABIC MATHEMATICAL INITIAL KHAH -1EE39;N # Lo ARABIC MATHEMATICAL INITIAL DAD -1EE3B;N # Lo ARABIC MATHEMATICAL INITIAL GHAIN -1EE42;N # Lo ARABIC MATHEMATICAL TAILED JEEM -1EE47;N # Lo ARABIC MATHEMATICAL TAILED HAH -1EE49;N # Lo ARABIC MATHEMATICAL TAILED YEH -1EE4B;N # Lo ARABIC MATHEMATICAL TAILED LAM -1EE4D..1EE4F;N # Lo [3] ARABIC MATHEMATICAL TAILED NOON..ARABIC MATHEMATICAL TAILED AIN -1EE51..1EE52;N # Lo [2] ARABIC MATHEMATICAL TAILED SAD..ARABIC MATHEMATICAL TAILED QAF -1EE54;N # Lo ARABIC MATHEMATICAL TAILED SHEEN -1EE57;N # Lo ARABIC MATHEMATICAL TAILED KHAH -1EE59;N # Lo ARABIC MATHEMATICAL TAILED DAD -1EE5B;N # Lo ARABIC MATHEMATICAL TAILED GHAIN -1EE5D;N # Lo ARABIC MATHEMATICAL TAILED DOTLESS NOON -1EE5F;N # Lo ARABIC MATHEMATICAL TAILED DOTLESS QAF -1EE61..1EE62;N # Lo [2] ARABIC MATHEMATICAL STRETCHED BEH..ARABIC MATHEMATICAL STRETCHED JEEM -1EE64;N # Lo ARABIC MATHEMATICAL STRETCHED HEH -1EE67..1EE6A;N # Lo [4] ARABIC MATHEMATICAL STRETCHED HAH..ARABIC MATHEMATICAL STRETCHED KAF -1EE6C..1EE72;N # Lo [7] ARABIC MATHEMATICAL STRETCHED MEEM..ARABIC MATHEMATICAL STRETCHED QAF -1EE74..1EE77;N # Lo [4] ARABIC MATHEMATICAL STRETCHED SHEEN..ARABIC MATHEMATICAL STRETCHED KHAH -1EE79..1EE7C;N # Lo [4] ARABIC MATHEMATICAL STRETCHED DAD..ARABIC MATHEMATICAL STRETCHED DOTLESS BEH -1EE7E;N # Lo ARABIC MATHEMATICAL STRETCHED DOTLESS FEH -1EE80..1EE89;N # Lo [10] ARABIC MATHEMATICAL LOOPED ALEF..ARABIC MATHEMATICAL LOOPED YEH -1EE8B..1EE9B;N # Lo [17] ARABIC MATHEMATICAL LOOPED LAM..ARABIC MATHEMATICAL LOOPED GHAIN -1EEA1..1EEA3;N # Lo [3] ARABIC MATHEMATICAL DOUBLE-STRUCK BEH..ARABIC MATHEMATICAL DOUBLE-STRUCK DAL -1EEA5..1EEA9;N # Lo [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH -1EEAB..1EEBB;N # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN -1EEF0..1EEF1;N # Sm [2] ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL -1F000..1F003;N # So [4] MAHJONG TILE EAST WIND..MAHJONG TILE NORTH WIND -1F004;W # So MAHJONG TILE RED DRAGON -1F005..1F02B;N # So [39] MAHJONG TILE GREEN DRAGON..MAHJONG TILE BACK -1F030..1F093;N # So [100] DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06 -1F0A0..1F0AE;N # So [15] PLAYING CARD BACK..PLAYING CARD KING OF SPADES -1F0B1..1F0BF;N # So [15] PLAYING CARD ACE OF HEARTS..PLAYING CARD RED JOKER -1F0C1..1F0CE;N # So [14] PLAYING CARD ACE OF DIAMONDS..PLAYING CARD KING OF DIAMONDS -1F0CF;W # So PLAYING CARD BLACK JOKER -1F0D1..1F0F5;N # So [37] PLAYING CARD ACE OF CLUBS..PLAYING CARD TRUMP-21 -1F100..1F10A;A # No [11] DIGIT ZERO FULL STOP..DIGIT NINE COMMA -1F10B..1F10C;N # No [2] DINGBAT CIRCLED SANS-SERIF DIGIT ZERO..DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ZERO -1F10D..1F10F;N # So [3] CIRCLED ZERO WITH SLASH..CIRCLED DOLLAR SIGN WITH OVERLAID BACKSLASH -1F110..1F12D;A # So [30] PARENTHESIZED LATIN CAPITAL LETTER A..CIRCLED CD -1F12E..1F12F;N # So [2] CIRCLED WZ..COPYLEFT SYMBOL -1F130..1F169;A # So [58] SQUARED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z -1F16A..1F16F;N # So [6] RAISED MC SIGN..CIRCLED HUMAN FIGURE -1F170..1F18D;A # So [30] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED SA -1F18E;W # So NEGATIVE SQUARED AB -1F18F..1F190;A # So [2] NEGATIVE SQUARED WC..SQUARE DJ -1F191..1F19A;W # So [10] SQUARED CL..SQUARED VS -1F19B..1F1AC;A # So [18] SQUARED THREE D..SQUARED VOD -1F1AD;N # So MASK WORK SYMBOL -1F1E6..1F1FF;N # So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z -1F200..1F202;W # So [3] SQUARE HIRAGANA HOKA..SQUARED KATAKANA SA -1F210..1F23B;W # So [44] SQUARED CJK UNIFIED IDEOGRAPH-624B..SQUARED CJK UNIFIED IDEOGRAPH-914D -1F240..1F248;W # So [9] TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C..TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557 -1F250..1F251;W # So [2] CIRCLED IDEOGRAPH ADVANTAGE..CIRCLED IDEOGRAPH ACCEPT -1F260..1F265;W # So [6] ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI -1F300..1F320;W # So [33] CYCLONE..SHOOTING STAR -1F321..1F32C;N # So [12] THERMOMETER..WIND BLOWING FACE -1F32D..1F335;W # So [9] HOT DOG..CACTUS -1F336;N # So HOT PEPPER -1F337..1F37C;W # So [70] TULIP..BABY BOTTLE -1F37D;N # So FORK AND KNIFE WITH PLATE -1F37E..1F393;W # So [22] BOTTLE WITH POPPING CORK..GRADUATION CAP -1F394..1F39F;N # So [12] HEART WITH TIP ON THE LEFT..ADMISSION TICKETS -1F3A0..1F3CA;W # So [43] CAROUSEL HORSE..SWIMMER -1F3CB..1F3CE;N # So [4] WEIGHT LIFTER..RACING CAR -1F3CF..1F3D3;W # So [5] CRICKET BAT AND BALL..TABLE TENNIS PADDLE AND BALL -1F3D4..1F3DF;N # So [12] SNOW CAPPED MOUNTAIN..STADIUM -1F3E0..1F3F0;W # So [17] HOUSE BUILDING..EUROPEAN CASTLE -1F3F1..1F3F3;N # So [3] WHITE PENNANT..WAVING WHITE FLAG -1F3F4;W # So WAVING BLACK FLAG -1F3F5..1F3F7;N # So [3] ROSETTE..LABEL -1F3F8..1F3FA;W # So [3] BADMINTON RACQUET AND SHUTTLECOCK..AMPHORA -1F3FB..1F3FF;W # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 -1F400..1F43E;W # So [63] RAT..PAW PRINTS -1F43F;N # So CHIPMUNK -1F440;W # So EYES -1F441;N # So EYE -1F442..1F4FC;W # So [187] EAR..VIDEOCASSETTE -1F4FD..1F4FE;N # So [2] FILM PROJECTOR..PORTABLE STEREO -1F4FF..1F53D;W # So [63] PRAYER BEADS..DOWN-POINTING SMALL RED TRIANGLE -1F53E..1F54A;N # So [13] LOWER RIGHT SHADOWED WHITE CIRCLE..DOVE OF PEACE -1F54B..1F54E;W # So [4] KAABA..MENORAH WITH NINE BRANCHES -1F54F;N # So BOWL OF HYGIEIA -1F550..1F567;W # So [24] CLOCK FACE ONE OCLOCK..CLOCK FACE TWELVE-THIRTY -1F568..1F579;N # So [18] RIGHT SPEAKER..JOYSTICK -1F57A;W # So MAN DANCING -1F57B..1F594;N # So [26] LEFT HAND TELEPHONE RECEIVER..REVERSED VICTORY HAND -1F595..1F596;W # So [2] REVERSED HAND WITH MIDDLE FINGER EXTENDED..RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS -1F597..1F5A3;N # So [13] WHITE DOWN POINTING LEFT HAND INDEX..BLACK DOWN POINTING BACKHAND INDEX -1F5A4;W # So BLACK HEART -1F5A5..1F5FA;N # So [86] DESKTOP COMPUTER..WORLD MAP -1F5FB..1F5FF;W # So [5] MOUNT FUJI..MOYAI -1F600..1F64F;W # So [80] GRINNING FACE..PERSON WITH FOLDED HANDS -1F650..1F67F;N # So [48] NORTH WEST POINTING LEAF..REVERSE CHECKER BOARD -1F680..1F6C5;W # So [70] ROCKET..LEFT LUGGAGE -1F6C6..1F6CB;N # So [6] TRIANGLE WITH ROUNDED CORNERS..COUCH AND LAMP -1F6CC;W # So SLEEPING ACCOMMODATION -1F6CD..1F6CF;N # So [3] SHOPPING BAGS..BED -1F6D0..1F6D2;W # So [3] PLACE OF WORSHIP..SHOPPING TROLLEY -1F6D3..1F6D4;N # So [2] STUPA..PAGODA -1F6D5..1F6D7;W # So [3] HINDU TEMPLE..ELEVATOR -1F6DC..1F6DF;W # So [4] WIRELESS..RING BUOY -1F6E0..1F6EA;N # So [11] HAMMER AND WRENCH..NORTHEAST-POINTING AIRPLANE -1F6EB..1F6EC;W # So [2] AIRPLANE DEPARTURE..AIRPLANE ARRIVING -1F6F0..1F6F3;N # So [4] SATELLITE..PASSENGER SHIP -1F6F4..1F6FC;W # So [9] SCOOTER..ROLLER SKATE -1F700..1F776;N # So [119] ALCHEMICAL SYMBOL FOR QUINTESSENCE..LUNAR ECLIPSE -1F77B..1F77F;N # So [5] HAUMEA..ORCUS -1F780..1F7D9;N # So [90] BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE..NINE POINTED WHITE STAR -1F7E0..1F7EB;W # So [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE -1F7F0;W # So HEAVY EQUALS SIGN -1F800..1F80B;N # So [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD -1F810..1F847;N # So [56] LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW -1F850..1F859;N # So [10] LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW -1F860..1F887;N # So [40] WIDE-HEADED LEFTWARDS LIGHT BARB ARROW..WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW -1F890..1F8AD;N # So [30] LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS -1F8B0..1F8B1;N # So [2] ARROW POINTING UPWARDS THEN NORTH WEST..ARROW POINTING RIGHTWARDS THEN CURVING SOUTH WEST -1F900..1F90B;N # So [12] CIRCLED CROSS FORMEE WITH FOUR DOTS..DOWNWARD FACING NOTCHED HOOK WITH DOT -1F90C..1F93A;W # So [47] PINCHED FINGERS..FENCER -1F93B;N # So MODERN PENTATHLON -1F93C..1F945;W # So [10] WRESTLERS..GOAL NET -1F946;N # So RIFLE -1F947..1F9FF;W # So [185] FIRST PLACE MEDAL..NAZAR AMULET -1FA00..1FA53;N # So [84] NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP -1FA60..1FA6D;N # So [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER -1FA70..1FA7C;W # So [13] BALLET SHOES..CRUTCH -1FA80..1FA88;W # So [9] YO-YO..FLUTE -1FA90..1FABD;W # So [46] RINGED PLANET..WING -1FABF..1FAC5;W # So [7] GOOSE..PERSON WITH CROWN -1FACE..1FADB;W # So [14] MOOSE..PEA POD -1FAE0..1FAE8;W # So [9] MELTING FACE..SHAKING FACE -1FAF0..1FAF8;W # So [9] HAND WITH INDEX FINGER AND THUMB CROSSED..RIGHTWARDS PUSHING HAND -1FB00..1FB92;N # So [147] BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK -1FB94..1FBCA;N # So [55] LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..WHITE UP-POINTING CHEVRON -1FBF0..1FBF9;N # Nd [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE -20000..2A6DF;W # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF -2A6E0..2A6FF;W # Cn [32] <reserved-2A6E0>..<reserved-2A6FF> -2A700..2B739;W # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739 -2B73A..2B73F;W # Cn [6] <reserved-2B73A>..<reserved-2B73F> -2B740..2B81D;W # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D -2B81E..2B81F;W # Cn [2] <reserved-2B81E>..<reserved-2B81F> -2B820..2CEA1;W # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 -2CEA2..2CEAF;W # Cn [14] <reserved-2CEA2>..<reserved-2CEAF> -2CEB0..2EBE0;W # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0 -2EBE1..2F7FF;W # Cn [3103] <reserved-2EBE1>..<reserved-2F7FF> -2F800..2FA1D;W # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D -2FA1E..2FA1F;W # Cn [2] <reserved-2FA1E>..<reserved-2FA1F> -2FA20..2FFFD;W # Cn [1502] <reserved-2FA20>..<reserved-2FFFD> -30000..3134A;W # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A -3134B..3134F;W # Cn [5] <reserved-3134B>..<reserved-3134F> -31350..323AF;W # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF -323B0..3FFFD;W # Cn [56398] <reserved-323B0>..<reserved-3FFFD> -E0001;N # Cf LANGUAGE TAG -E0020..E007F;N # Cf [96] TAG SPACE..CANCEL TAG -E0100..E01EF;A # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 -F0000..FFFFD;A # Co [65534] <private-use-F0000>..<private-use-FFFFD> -100000..10FFFD;A # Co [65534] <private-use-100000>..<private-use-10FFFD> +0000..001F ; N # Cc [32] <control-0000>..<control-001F> +0020 ; Na # Zs SPACE +0021..0023 ; Na # Po [3] EXCLAMATION MARK..NUMBER SIGN +0024 ; Na # Sc DOLLAR SIGN +0025..0027 ; Na # Po [3] PERCENT SIGN..APOSTROPHE +0028 ; Na # Ps LEFT PARENTHESIS +0029 ; Na # Pe RIGHT PARENTHESIS +002A ; Na # Po ASTERISK +002B ; Na # Sm PLUS SIGN +002C ; Na # Po COMMA +002D ; Na # Pd HYPHEN-MINUS +002E..002F ; Na # Po [2] FULL STOP..SOLIDUS +0030..0039 ; Na # Nd [10] DIGIT ZERO..DIGIT NINE +003A..003B ; Na # Po [2] COLON..SEMICOLON +003C..003E ; Na # Sm [3] LESS-THAN SIGN..GREATER-THAN SIGN +003F..0040 ; Na # Po [2] QUESTION MARK..COMMERCIAL AT +0041..005A ; Na # Lu [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z +005B ; Na # Ps LEFT SQUARE BRACKET +005C ; Na # Po REVERSE SOLIDUS +005D ; Na # Pe RIGHT SQUARE BRACKET +005E ; Na # Sk CIRCUMFLEX ACCENT +005F ; Na # Pc LOW LINE +0060 ; Na # Sk GRAVE ACCENT +0061..007A ; Na # Ll [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z +007B ; Na # Ps LEFT CURLY BRACKET +007C ; Na # Sm VERTICAL LINE +007D ; Na # Pe RIGHT CURLY BRACKET +007E ; Na # Sm TILDE +007F ; N # Cc <control-007F> +0080..009F ; N # Cc [32] <control-0080>..<control-009F> +00A0 ; N # Zs NO-BREAK SPACE +00A1 ; A # Po INVERTED EXCLAMATION MARK +00A2..00A3 ; Na # Sc [2] CENT SIGN..POUND SIGN +00A4 ; A # Sc CURRENCY SIGN +00A5 ; Na # Sc YEN SIGN +00A6 ; Na # So BROKEN BAR +00A7 ; A # Po SECTION SIGN +00A8 ; A # Sk DIAERESIS +00A9 ; N # So COPYRIGHT SIGN +00AA ; A # Lo FEMININE ORDINAL INDICATOR +00AB ; N # Pi LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +00AC ; Na # Sm NOT SIGN +00AD ; A # Cf SOFT HYPHEN +00AE ; A # So REGISTERED SIGN +00AF ; Na # Sk MACRON +00B0 ; A # So DEGREE SIGN +00B1 ; A # Sm PLUS-MINUS SIGN +00B2..00B3 ; A # No [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE +00B4 ; A # Sk ACUTE ACCENT +00B5 ; N # Ll MICRO SIGN +00B6..00B7 ; A # Po [2] PILCROW SIGN..MIDDLE DOT +00B8 ; A # Sk CEDILLA +00B9 ; A # No SUPERSCRIPT ONE +00BA ; A # Lo MASCULINE ORDINAL INDICATOR +00BB ; N # Pf RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +00BC..00BE ; A # No [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS +00BF ; A # Po INVERTED QUESTION MARK +00C0..00C5 ; N # Lu [6] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER A WITH RING ABOVE +00C6 ; A # Lu LATIN CAPITAL LETTER AE +00C7..00CF ; N # Lu [9] LATIN CAPITAL LETTER C WITH CEDILLA..LATIN CAPITAL LETTER I WITH DIAERESIS +00D0 ; A # Lu LATIN CAPITAL LETTER ETH +00D1..00D6 ; N # Lu [6] LATIN CAPITAL LETTER N WITH TILDE..LATIN CAPITAL LETTER O WITH DIAERESIS +00D7 ; A # Sm MULTIPLICATION SIGN +00D8 ; A # Lu LATIN CAPITAL LETTER O WITH STROKE +00D9..00DD ; N # Lu [5] LATIN CAPITAL LETTER U WITH GRAVE..LATIN CAPITAL LETTER Y WITH ACUTE +00DE..00E1 ; A # L& [4] LATIN CAPITAL LETTER THORN..LATIN SMALL LETTER A WITH ACUTE +00E2..00E5 ; N # Ll [4] LATIN SMALL LETTER A WITH CIRCUMFLEX..LATIN SMALL LETTER A WITH RING ABOVE +00E6 ; A # Ll LATIN SMALL LETTER AE +00E7 ; N # Ll LATIN SMALL LETTER C WITH CEDILLA +00E8..00EA ; A # Ll [3] LATIN SMALL LETTER E WITH GRAVE..LATIN SMALL LETTER E WITH CIRCUMFLEX +00EB ; N # Ll LATIN SMALL LETTER E WITH DIAERESIS +00EC..00ED ; A # Ll [2] LATIN SMALL LETTER I WITH GRAVE..LATIN SMALL LETTER I WITH ACUTE +00EE..00EF ; N # Ll [2] LATIN SMALL LETTER I WITH CIRCUMFLEX..LATIN SMALL LETTER I WITH DIAERESIS +00F0 ; A # Ll LATIN SMALL LETTER ETH +00F1 ; N # Ll LATIN SMALL LETTER N WITH TILDE +00F2..00F3 ; A # Ll [2] LATIN SMALL LETTER O WITH GRAVE..LATIN SMALL LETTER O WITH ACUTE +00F4..00F6 ; N # Ll [3] LATIN SMALL LETTER O WITH CIRCUMFLEX..LATIN SMALL LETTER O WITH DIAERESIS +00F7 ; A # Sm DIVISION SIGN +00F8..00FA ; A # Ll [3] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER U WITH ACUTE +00FB ; N # Ll LATIN SMALL LETTER U WITH CIRCUMFLEX +00FC ; A # Ll LATIN SMALL LETTER U WITH DIAERESIS +00FD ; N # Ll LATIN SMALL LETTER Y WITH ACUTE +00FE ; A # Ll LATIN SMALL LETTER THORN +00FF ; N # Ll LATIN SMALL LETTER Y WITH DIAERESIS +0100 ; N # Lu LATIN CAPITAL LETTER A WITH MACRON +0101 ; A # Ll LATIN SMALL LETTER A WITH MACRON +0102..0110 ; N # L& [15] LATIN CAPITAL LETTER A WITH BREVE..LATIN CAPITAL LETTER D WITH STROKE +0111 ; A # Ll LATIN SMALL LETTER D WITH STROKE +0112 ; N # Lu LATIN CAPITAL LETTER E WITH MACRON +0113 ; A # Ll LATIN SMALL LETTER E WITH MACRON +0114..011A ; N # L& [7] LATIN CAPITAL LETTER E WITH BREVE..LATIN CAPITAL LETTER E WITH CARON +011B ; A # Ll LATIN SMALL LETTER E WITH CARON +011C..0125 ; N # L& [10] LATIN CAPITAL LETTER G WITH CIRCUMFLEX..LATIN SMALL LETTER H WITH CIRCUMFLEX +0126..0127 ; A # L& [2] LATIN CAPITAL LETTER H WITH STROKE..LATIN SMALL LETTER H WITH STROKE +0128..012A ; N # L& [3] LATIN CAPITAL LETTER I WITH TILDE..LATIN CAPITAL LETTER I WITH MACRON +012B ; A # Ll LATIN SMALL LETTER I WITH MACRON +012C..0130 ; N # L& [5] LATIN CAPITAL LETTER I WITH BREVE..LATIN CAPITAL LETTER I WITH DOT ABOVE +0131..0133 ; A # L& [3] LATIN SMALL LETTER DOTLESS I..LATIN SMALL LIGATURE IJ +0134..0137 ; N # L& [4] LATIN CAPITAL LETTER J WITH CIRCUMFLEX..LATIN SMALL LETTER K WITH CEDILLA +0138 ; A # Ll LATIN SMALL LETTER KRA +0139..013E ; N # L& [6] LATIN CAPITAL LETTER L WITH ACUTE..LATIN SMALL LETTER L WITH CARON +013F..0142 ; A # L& [4] LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATIN SMALL LETTER L WITH STROKE +0143 ; N # Lu LATIN CAPITAL LETTER N WITH ACUTE +0144 ; A # Ll LATIN SMALL LETTER N WITH ACUTE +0145..0147 ; N # L& [3] LATIN CAPITAL LETTER N WITH CEDILLA..LATIN CAPITAL LETTER N WITH CARON +0148..014B ; A # L& [4] LATIN SMALL LETTER N WITH CARON..LATIN SMALL LETTER ENG +014C ; N # Lu LATIN CAPITAL LETTER O WITH MACRON +014D ; A # Ll LATIN SMALL LETTER O WITH MACRON +014E..0151 ; N # L& [4] LATIN CAPITAL LETTER O WITH BREVE..LATIN SMALL LETTER O WITH DOUBLE ACUTE +0152..0153 ; A # L& [2] LATIN CAPITAL LIGATURE OE..LATIN SMALL LIGATURE OE +0154..0165 ; N # L& [18] LATIN CAPITAL LETTER R WITH ACUTE..LATIN SMALL LETTER T WITH CARON +0166..0167 ; A # L& [2] LATIN CAPITAL LETTER T WITH STROKE..LATIN SMALL LETTER T WITH STROKE +0168..016A ; N # L& [3] LATIN CAPITAL LETTER U WITH TILDE..LATIN CAPITAL LETTER U WITH MACRON +016B ; A # Ll LATIN SMALL LETTER U WITH MACRON +016C..017F ; N # L& [20] LATIN CAPITAL LETTER U WITH BREVE..LATIN SMALL LETTER LONG S +0180..01BA ; N # L& [59] LATIN SMALL LETTER B WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL +01BB ; N # Lo LATIN LETTER TWO WITH STROKE +01BC..01BF ; N # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN +01C0..01C3 ; N # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK +01C4..01CD ; N # L& [10] LATIN CAPITAL LETTER DZ WITH CARON..LATIN CAPITAL LETTER A WITH CARON +01CE ; A # Ll LATIN SMALL LETTER A WITH CARON +01CF ; N # Lu LATIN CAPITAL LETTER I WITH CARON +01D0 ; A # Ll LATIN SMALL LETTER I WITH CARON +01D1 ; N # Lu LATIN CAPITAL LETTER O WITH CARON +01D2 ; A # Ll LATIN SMALL LETTER O WITH CARON +01D3 ; N # Lu LATIN CAPITAL LETTER U WITH CARON +01D4 ; A # Ll LATIN SMALL LETTER U WITH CARON +01D5 ; N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON +01D6 ; A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND MACRON +01D7 ; N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE +01D8 ; A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE +01D9 ; N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON +01DA ; A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND CARON +01DB ; N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE +01DC ; A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE +01DD..024F ; N # L& [115] LATIN SMALL LETTER TURNED E..LATIN SMALL LETTER Y WITH STROKE +0250 ; N # Ll LATIN SMALL LETTER TURNED A +0251 ; A # Ll LATIN SMALL LETTER ALPHA +0252..0260 ; N # Ll [15] LATIN SMALL LETTER TURNED ALPHA..LATIN SMALL LETTER G WITH HOOK +0261 ; A # Ll LATIN SMALL LETTER SCRIPT G +0262..0293 ; N # Ll [50] LATIN LETTER SMALL CAPITAL G..LATIN SMALL LETTER EZH WITH CURL +0294 ; N # Lo LATIN LETTER GLOTTAL STOP +0295..02AF ; N # Ll [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +02B0..02C1 ; N # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP +02C2..02C3 ; N # Sk [2] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER RIGHT ARROWHEAD +02C4 ; A # Sk MODIFIER LETTER UP ARROWHEAD +02C5 ; N # Sk MODIFIER LETTER DOWN ARROWHEAD +02C6 ; N # Lm MODIFIER LETTER CIRCUMFLEX ACCENT +02C7 ; A # Lm CARON +02C8 ; N # Lm MODIFIER LETTER VERTICAL LINE +02C9..02CB ; A # Lm [3] MODIFIER LETTER MACRON..MODIFIER LETTER GRAVE ACCENT +02CC ; N # Lm MODIFIER LETTER LOW VERTICAL LINE +02CD ; A # Lm MODIFIER LETTER LOW MACRON +02CE..02CF ; N # Lm [2] MODIFIER LETTER LOW GRAVE ACCENT..MODIFIER LETTER LOW ACUTE ACCENT +02D0 ; A # Lm MODIFIER LETTER TRIANGULAR COLON +02D1 ; N # Lm MODIFIER LETTER HALF TRIANGULAR COLON +02D2..02D7 ; N # Sk [6] MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER MINUS SIGN +02D8..02DB ; A # Sk [4] BREVE..OGONEK +02DC ; N # Sk SMALL TILDE +02DD ; A # Sk DOUBLE ACUTE ACCENT +02DE ; N # Sk MODIFIER LETTER RHOTIC HOOK +02DF ; A # Sk MODIFIER LETTER CROSS ACCENT +02E0..02E4 ; N # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP +02E5..02EB ; N # Sk [7] MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER YANG DEPARTING TONE MARK +02EC ; N # Lm MODIFIER LETTER VOICING +02ED ; N # Sk MODIFIER LETTER UNASPIRATED +02EE ; N # Lm MODIFIER LETTER DOUBLE APOSTROPHE +02EF..02FF ; N # Sk [17] MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW +0300..036F ; A # Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X +0370..0373 ; N # L& [4] GREEK CAPITAL LETTER HETA..GREEK SMALL LETTER ARCHAIC SAMPI +0374 ; N # Lm GREEK NUMERAL SIGN +0375 ; N # Sk GREEK LOWER NUMERAL SIGN +0376..0377 ; N # L& [2] GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA..GREEK SMALL LETTER PAMPHYLIAN DIGAMMA +037A ; N # Lm GREEK YPOGEGRAMMENI +037B..037D ; N # Ll [3] GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL +037E ; N # Po GREEK QUESTION MARK +037F ; N # Lu GREEK CAPITAL LETTER YOT +0384..0385 ; N # Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS +0386 ; N # Lu GREEK CAPITAL LETTER ALPHA WITH TONOS +0387 ; N # Po GREEK ANO TELEIA +0388..038A ; N # Lu [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS +038C ; N # Lu GREEK CAPITAL LETTER OMICRON WITH TONOS +038E..0390 ; N # L& [3] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS +0391..03A1 ; A # Lu [17] GREEK CAPITAL LETTER ALPHA..GREEK CAPITAL LETTER RHO +03A3..03A9 ; A # Lu [7] GREEK CAPITAL LETTER SIGMA..GREEK CAPITAL LETTER OMEGA +03AA..03B0 ; N # L& [7] GREEK CAPITAL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS +03B1..03C1 ; A # Ll [17] GREEK SMALL LETTER ALPHA..GREEK SMALL LETTER RHO +03C2 ; N # Ll GREEK SMALL LETTER FINAL SIGMA +03C3..03C9 ; A # Ll [7] GREEK SMALL LETTER SIGMA..GREEK SMALL LETTER OMEGA +03CA..03F5 ; N # L& [44] GREEK SMALL LETTER IOTA WITH DIALYTIKA..GREEK LUNATE EPSILON SYMBOL +03F6 ; N # Sm GREEK REVERSED LUNATE EPSILON SYMBOL +03F7..03FF ; N # L& [9] GREEK CAPITAL LETTER SHO..GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL +0400 ; N # Lu CYRILLIC CAPITAL LETTER IE WITH GRAVE +0401 ; A # Lu CYRILLIC CAPITAL LETTER IO +0402..040F ; N # Lu [14] CYRILLIC CAPITAL LETTER DJE..CYRILLIC CAPITAL LETTER DZHE +0410..044F ; A # L& [64] CYRILLIC CAPITAL LETTER A..CYRILLIC SMALL LETTER YA +0450 ; N # Ll CYRILLIC SMALL LETTER IE WITH GRAVE +0451 ; A # Ll CYRILLIC SMALL LETTER IO +0452..0481 ; N # L& [48] CYRILLIC SMALL LETTER DJE..CYRILLIC SMALL LETTER KOPPA +0482 ; N # So CYRILLIC THOUSANDS SIGN +0483..0487 ; N # Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE +0488..0489 ; N # Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN +048A..04FF ; N # L& [118] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER HA WITH STROKE +0500..052F ; N # L& [48] CYRILLIC CAPITAL LETTER KOMI DE..CYRILLIC SMALL LETTER EL WITH DESCENDER +0531..0556 ; N # Lu [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH +0559 ; N # Lm ARMENIAN MODIFIER LETTER LEFT HALF RING +055A..055F ; N # Po [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK +0560..0588 ; N # Ll [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE +0589 ; N # Po ARMENIAN FULL STOP +058A ; N # Pd ARMENIAN HYPHEN +058D..058E ; N # So [2] RIGHT-FACING ARMENIAN ETERNITY SIGN..LEFT-FACING ARMENIAN ETERNITY SIGN +058F ; N # Sc ARMENIAN DRAM SIGN +0591..05BD ; N # Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG +05BE ; N # Pd HEBREW PUNCTUATION MAQAF +05BF ; N # Mn HEBREW POINT RAFE +05C0 ; N # Po HEBREW PUNCTUATION PASEQ +05C1..05C2 ; N # Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT +05C3 ; N # Po HEBREW PUNCTUATION SOF PASUQ +05C4..05C5 ; N # Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT +05C6 ; N # Po HEBREW PUNCTUATION NUN HAFUKHA +05C7 ; N # Mn HEBREW POINT QAMATS QATAN +05D0..05EA ; N # Lo [27] HEBREW LETTER ALEF..HEBREW LETTER TAV +05EF..05F2 ; N # Lo [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD +05F3..05F4 ; N # Po [2] HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATION GERSHAYIM +0600..0605 ; N # Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE +0606..0608 ; N # Sm [3] ARABIC-INDIC CUBE ROOT..ARABIC RAY +0609..060A ; N # Po [2] ARABIC-INDIC PER MILLE SIGN..ARABIC-INDIC PER TEN THOUSAND SIGN +060B ; N # Sc AFGHANI SIGN +060C..060D ; N # Po [2] ARABIC COMMA..ARABIC DATE SEPARATOR +060E..060F ; N # So [2] ARABIC POETIC VERSE SIGN..ARABIC SIGN MISRA +0610..061A ; N # Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA +061B ; N # Po ARABIC SEMICOLON +061C ; N # Cf ARABIC LETTER MARK +061D..061F ; N # Po [3] ARABIC END OF TEXT MARK..ARABIC QUESTION MARK +0620..063F ; N # Lo [32] ARABIC LETTER KASHMIRI YEH..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE +0640 ; N # Lm ARABIC TATWEEL +0641..064A ; N # Lo [10] ARABIC LETTER FEH..ARABIC LETTER YEH +064B..065F ; N # Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW +0660..0669 ; N # Nd [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE +066A..066D ; N # Po [4] ARABIC PERCENT SIGN..ARABIC FIVE POINTED STAR +066E..066F ; N # Lo [2] ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF +0670 ; N # Mn ARABIC LETTER SUPERSCRIPT ALEF +0671..06D3 ; N # Lo [99] ARABIC LETTER ALEF WASLA..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE +06D4 ; N # Po ARABIC FULL STOP +06D5 ; N # Lo ARABIC LETTER AE +06D6..06DC ; N # Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN +06DD ; N # Cf ARABIC END OF AYAH +06DE ; N # So ARABIC START OF RUB EL HIZB +06DF..06E4 ; N # Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA +06E5..06E6 ; N # Lm [2] ARABIC SMALL WAW..ARABIC SMALL YEH +06E7..06E8 ; N # Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON +06E9 ; N # So ARABIC PLACE OF SAJDAH +06EA..06ED ; N # Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM +06EE..06EF ; N # Lo [2] ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V +06F0..06F9 ; N # Nd [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE +06FA..06FC ; N # Lo [3] ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC LETTER GHAIN WITH DOT BELOW +06FD..06FE ; N # So [2] ARABIC SIGN SINDHI AMPERSAND..ARABIC SIGN SINDHI POSTPOSITION MEN +06FF ; N # Lo ARABIC LETTER HEH WITH INVERTED V +0700..070D ; N # Po [14] SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN ASTERISCUS +070F ; N # Cf SYRIAC ABBREVIATION MARK +0710 ; N # Lo SYRIAC LETTER ALAPH +0711 ; N # Mn SYRIAC LETTER SUPERSCRIPT ALAPH +0712..072F ; N # Lo [30] SYRIAC LETTER BETH..SYRIAC LETTER PERSIAN DHALATH +0730..074A ; N # Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH +074D..074F ; N # Lo [3] SYRIAC LETTER SOGDIAN ZHAIN..SYRIAC LETTER SOGDIAN FE +0750..077F ; N # Lo [48] ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS ABOVE +0780..07A5 ; N # Lo [38] THAANA LETTER HAA..THAANA LETTER WAAVU +07A6..07B0 ; N # Mn [11] THAANA ABAFILI..THAANA SUKUN +07B1 ; N # Lo THAANA LETTER NAA +07C0..07C9 ; N # Nd [10] NKO DIGIT ZERO..NKO DIGIT NINE +07CA..07EA ; N # Lo [33] NKO LETTER A..NKO LETTER JONA RA +07EB..07F3 ; N # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE +07F4..07F5 ; N # Lm [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE +07F6 ; N # So NKO SYMBOL OO DENNEN +07F7..07F9 ; N # Po [3] NKO SYMBOL GBAKURUNEN..NKO EXCLAMATION MARK +07FA ; N # Lm NKO LAJANYALAN +07FD ; N # Mn NKO DANTAYALAN +07FE..07FF ; N # Sc [2] NKO DOROME SIGN..NKO TAMAN SIGN +0800..0815 ; N # Lo [22] SAMARITAN LETTER ALAF..SAMARITAN LETTER TAAF +0816..0819 ; N # Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH +081A ; N # Lm SAMARITAN MODIFIER LETTER EPENTHETIC YUT +081B..0823 ; N # Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A +0824 ; N # Lm SAMARITAN MODIFIER LETTER SHORT A +0825..0827 ; N # Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U +0828 ; N # Lm SAMARITAN MODIFIER LETTER I +0829..082D ; N # Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA +0830..083E ; N # Po [15] SAMARITAN PUNCTUATION NEQUDAA..SAMARITAN PUNCTUATION ANNAAU +0840..0858 ; N # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN +0859..085B ; N # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK +085E ; N # Po MANDAIC PUNCTUATION +0860..086A ; N # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA +0870..0887 ; N # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT +0888 ; N # Sk ARABIC RAISED ROUND DOT +0889..088E ; N # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL +0890..0891 ; N # Cf [2] ARABIC POUND MARK ABOVE..ARABIC PIASTRE MARK ABOVE +0898..089F ; N # Mn [8] ARABIC SMALL HIGH WORD AL-JUZ..ARABIC HALF MADDA OVER MADDA +08A0..08C8 ; N # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF +08C9 ; N # Lm ARABIC SMALL FARSI YEH +08CA..08E1 ; N # Mn [24] ARABIC SMALL HIGH FARSI YEH..ARABIC SMALL HIGH SIGN SAFHA +08E2 ; N # Cf ARABIC DISPUTED END OF AYAH +08E3..08FF ; N # Mn [29] ARABIC TURNED DAMMA BELOW..ARABIC MARK SIDEWAYS NOON GHUNNA +0900..0902 ; N # Mn [3] DEVANAGARI SIGN INVERTED CANDRABINDU..DEVANAGARI SIGN ANUSVARA +0903 ; N # Mc DEVANAGARI SIGN VISARGA +0904..0939 ; N # Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA +093A ; N # Mn DEVANAGARI VOWEL SIGN OE +093B ; N # Mc DEVANAGARI VOWEL SIGN OOE +093C ; N # Mn DEVANAGARI SIGN NUKTA +093D ; N # Lo DEVANAGARI SIGN AVAGRAHA +093E..0940 ; N # Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II +0941..0948 ; N # Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI +0949..094C ; N # Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU +094D ; N # Mn DEVANAGARI SIGN VIRAMA +094E..094F ; N # Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW +0950 ; N # Lo DEVANAGARI OM +0951..0957 ; N # Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE +0958..0961 ; N # Lo [10] DEVANAGARI LETTER QA..DEVANAGARI LETTER VOCALIC LL +0962..0963 ; N # Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL +0964..0965 ; N # Po [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA +0966..096F ; N # Nd [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE +0970 ; N # Po DEVANAGARI ABBREVIATION SIGN +0971 ; N # Lm DEVANAGARI SIGN HIGH SPACING DOT +0972..097F ; N # Lo [14] DEVANAGARI LETTER CANDRA A..DEVANAGARI LETTER BBA +0980 ; N # Lo BENGALI ANJI +0981 ; N # Mn BENGALI SIGN CANDRABINDU +0982..0983 ; N # Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA +0985..098C ; N # Lo [8] BENGALI LETTER A..BENGALI LETTER VOCALIC L +098F..0990 ; N # Lo [2] BENGALI LETTER E..BENGALI LETTER AI +0993..09A8 ; N # Lo [22] BENGALI LETTER O..BENGALI LETTER NA +09AA..09B0 ; N # Lo [7] BENGALI LETTER PA..BENGALI LETTER RA +09B2 ; N # Lo BENGALI LETTER LA +09B6..09B9 ; N # Lo [4] BENGALI LETTER SHA..BENGALI LETTER HA +09BC ; N # Mn BENGALI SIGN NUKTA +09BD ; N # Lo BENGALI SIGN AVAGRAHA +09BE..09C0 ; N # Mc [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II +09C1..09C4 ; N # Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR +09C7..09C8 ; N # Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI +09CB..09CC ; N # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU +09CD ; N # Mn BENGALI SIGN VIRAMA +09CE ; N # Lo BENGALI LETTER KHANDA TA +09D7 ; N # Mc BENGALI AU LENGTH MARK +09DC..09DD ; N # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA +09DF..09E1 ; N # Lo [3] BENGALI LETTER YYA..BENGALI LETTER VOCALIC LL +09E2..09E3 ; N # Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL +09E6..09EF ; N # Nd [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE +09F0..09F1 ; N # Lo [2] BENGALI LETTER RA WITH MIDDLE DIAGONAL..BENGALI LETTER RA WITH LOWER DIAGONAL +09F2..09F3 ; N # Sc [2] BENGALI RUPEE MARK..BENGALI RUPEE SIGN +09F4..09F9 ; N # No [6] BENGALI CURRENCY NUMERATOR ONE..BENGALI CURRENCY DENOMINATOR SIXTEEN +09FA ; N # So BENGALI ISSHAR +09FB ; N # Sc BENGALI GANDA MARK +09FC ; N # Lo BENGALI LETTER VEDIC ANUSVARA +09FD ; N # Po BENGALI ABBREVIATION SIGN +09FE ; N # Mn BENGALI SANDHI MARK +0A01..0A02 ; N # Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI +0A03 ; N # Mc GURMUKHI SIGN VISARGA +0A05..0A0A ; N # Lo [6] GURMUKHI LETTER A..GURMUKHI LETTER UU +0A0F..0A10 ; N # Lo [2] GURMUKHI LETTER EE..GURMUKHI LETTER AI +0A13..0A28 ; N # Lo [22] GURMUKHI LETTER OO..GURMUKHI LETTER NA +0A2A..0A30 ; N # Lo [7] GURMUKHI LETTER PA..GURMUKHI LETTER RA +0A32..0A33 ; N # Lo [2] GURMUKHI LETTER LA..GURMUKHI LETTER LLA +0A35..0A36 ; N # Lo [2] GURMUKHI LETTER VA..GURMUKHI LETTER SHA +0A38..0A39 ; N # Lo [2] GURMUKHI LETTER SA..GURMUKHI LETTER HA +0A3C ; N # Mn GURMUKHI SIGN NUKTA +0A3E..0A40 ; N # Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II +0A41..0A42 ; N # Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU +0A47..0A48 ; N # Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI +0A4B..0A4D ; N # Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA +0A51 ; N # Mn GURMUKHI SIGN UDAAT +0A59..0A5C ; N # Lo [4] GURMUKHI LETTER KHHA..GURMUKHI LETTER RRA +0A5E ; N # Lo GURMUKHI LETTER FA +0A66..0A6F ; N # Nd [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE +0A70..0A71 ; N # Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK +0A72..0A74 ; N # Lo [3] GURMUKHI IRI..GURMUKHI EK ONKAR +0A75 ; N # Mn GURMUKHI SIGN YAKASH +0A76 ; N # Po GURMUKHI ABBREVIATION SIGN +0A81..0A82 ; N # Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA +0A83 ; N # Mc GUJARATI SIGN VISARGA +0A85..0A8D ; N # Lo [9] GUJARATI LETTER A..GUJARATI VOWEL CANDRA E +0A8F..0A91 ; N # Lo [3] GUJARATI LETTER E..GUJARATI VOWEL CANDRA O +0A93..0AA8 ; N # Lo [22] GUJARATI LETTER O..GUJARATI LETTER NA +0AAA..0AB0 ; N # Lo [7] GUJARATI LETTER PA..GUJARATI LETTER RA +0AB2..0AB3 ; N # Lo [2] GUJARATI LETTER LA..GUJARATI LETTER LLA +0AB5..0AB9 ; N # Lo [5] GUJARATI LETTER VA..GUJARATI LETTER HA +0ABC ; N # Mn GUJARATI SIGN NUKTA +0ABD ; N # Lo GUJARATI SIGN AVAGRAHA +0ABE..0AC0 ; N # Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II +0AC1..0AC5 ; N # Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E +0AC7..0AC8 ; N # Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI +0AC9 ; N # Mc GUJARATI VOWEL SIGN CANDRA O +0ACB..0ACC ; N # Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU +0ACD ; N # Mn GUJARATI SIGN VIRAMA +0AD0 ; N # Lo GUJARATI OM +0AE0..0AE1 ; N # Lo [2] GUJARATI LETTER VOCALIC RR..GUJARATI LETTER VOCALIC LL +0AE2..0AE3 ; N # Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL +0AE6..0AEF ; N # Nd [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE +0AF0 ; N # Po GUJARATI ABBREVIATION SIGN +0AF1 ; N # Sc GUJARATI RUPEE SIGN +0AF9 ; N # Lo GUJARATI LETTER ZHA +0AFA..0AFF ; N # Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE +0B01 ; N # Mn ORIYA SIGN CANDRABINDU +0B02..0B03 ; N # Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA +0B05..0B0C ; N # Lo [8] ORIYA LETTER A..ORIYA LETTER VOCALIC L +0B0F..0B10 ; N # Lo [2] ORIYA LETTER E..ORIYA LETTER AI +0B13..0B28 ; N # Lo [22] ORIYA LETTER O..ORIYA LETTER NA +0B2A..0B30 ; N # Lo [7] ORIYA LETTER PA..ORIYA LETTER RA +0B32..0B33 ; N # Lo [2] ORIYA LETTER LA..ORIYA LETTER LLA +0B35..0B39 ; N # Lo [5] ORIYA LETTER VA..ORIYA LETTER HA +0B3C ; N # Mn ORIYA SIGN NUKTA +0B3D ; N # Lo ORIYA SIGN AVAGRAHA +0B3E ; N # Mc ORIYA VOWEL SIGN AA +0B3F ; N # Mn ORIYA VOWEL SIGN I +0B40 ; N # Mc ORIYA VOWEL SIGN II +0B41..0B44 ; N # Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR +0B47..0B48 ; N # Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI +0B4B..0B4C ; N # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU +0B4D ; N # Mn ORIYA SIGN VIRAMA +0B55..0B56 ; N # Mn [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK +0B57 ; N # Mc ORIYA AU LENGTH MARK +0B5C..0B5D ; N # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA +0B5F..0B61 ; N # Lo [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL +0B62..0B63 ; N # Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL +0B66..0B6F ; N # Nd [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE +0B70 ; N # So ORIYA ISSHAR +0B71 ; N # Lo ORIYA LETTER WA +0B72..0B77 ; N # No [6] ORIYA FRACTION ONE QUARTER..ORIYA FRACTION THREE SIXTEENTHS +0B82 ; N # Mn TAMIL SIGN ANUSVARA +0B83 ; N # Lo TAMIL SIGN VISARGA +0B85..0B8A ; N # Lo [6] TAMIL LETTER A..TAMIL LETTER UU +0B8E..0B90 ; N # Lo [3] TAMIL LETTER E..TAMIL LETTER AI +0B92..0B95 ; N # Lo [4] TAMIL LETTER O..TAMIL LETTER KA +0B99..0B9A ; N # Lo [2] TAMIL LETTER NGA..TAMIL LETTER CA +0B9C ; N # Lo TAMIL LETTER JA +0B9E..0B9F ; N # Lo [2] TAMIL LETTER NYA..TAMIL LETTER TTA +0BA3..0BA4 ; N # Lo [2] TAMIL LETTER NNA..TAMIL LETTER TA +0BA8..0BAA ; N # Lo [3] TAMIL LETTER NA..TAMIL LETTER PA +0BAE..0BB9 ; N # Lo [12] TAMIL LETTER MA..TAMIL LETTER HA +0BBE..0BBF ; N # Mc [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I +0BC0 ; N # Mn TAMIL VOWEL SIGN II +0BC1..0BC2 ; N # Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU +0BC6..0BC8 ; N # Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI +0BCA..0BCC ; N # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU +0BCD ; N # Mn TAMIL SIGN VIRAMA +0BD0 ; N # Lo TAMIL OM +0BD7 ; N # Mc TAMIL AU LENGTH MARK +0BE6..0BEF ; N # Nd [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE +0BF0..0BF2 ; N # No [3] TAMIL NUMBER TEN..TAMIL NUMBER ONE THOUSAND +0BF3..0BF8 ; N # So [6] TAMIL DAY SIGN..TAMIL AS ABOVE SIGN +0BF9 ; N # Sc TAMIL RUPEE SIGN +0BFA ; N # So TAMIL NUMBER SIGN +0C00 ; N # Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE +0C01..0C03 ; N # Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA +0C04 ; N # Mn TELUGU SIGN COMBINING ANUSVARA ABOVE +0C05..0C0C ; N # Lo [8] TELUGU LETTER A..TELUGU LETTER VOCALIC L +0C0E..0C10 ; N # Lo [3] TELUGU LETTER E..TELUGU LETTER AI +0C12..0C28 ; N # Lo [23] TELUGU LETTER O..TELUGU LETTER NA +0C2A..0C39 ; N # Lo [16] TELUGU LETTER PA..TELUGU LETTER HA +0C3C ; N # Mn TELUGU SIGN NUKTA +0C3D ; N # Lo TELUGU SIGN AVAGRAHA +0C3E..0C40 ; N # Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II +0C41..0C44 ; N # Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR +0C46..0C48 ; N # Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI +0C4A..0C4D ; N # Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA +0C55..0C56 ; N # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK +0C58..0C5A ; N # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA +0C5D ; N # Lo TELUGU LETTER NAKAARA POLLU +0C60..0C61 ; N # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL +0C62..0C63 ; N # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL +0C66..0C6F ; N # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE +0C77 ; N # Po TELUGU SIGN SIDDHAM +0C78..0C7E ; N # No [7] TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR +0C7F ; N # So TELUGU SIGN TUUMU +0C80 ; N # Lo KANNADA SIGN SPACING CANDRABINDU +0C81 ; N # Mn KANNADA SIGN CANDRABINDU +0C82..0C83 ; N # Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA +0C84 ; N # Po KANNADA SIGN SIDDHAM +0C85..0C8C ; N # Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L +0C8E..0C90 ; N # Lo [3] KANNADA LETTER E..KANNADA LETTER AI +0C92..0CA8 ; N # Lo [23] KANNADA LETTER O..KANNADA LETTER NA +0CAA..0CB3 ; N # Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA +0CB5..0CB9 ; N # Lo [5] KANNADA LETTER VA..KANNADA LETTER HA +0CBC ; N # Mn KANNADA SIGN NUKTA +0CBD ; N # Lo KANNADA SIGN AVAGRAHA +0CBE ; N # Mc KANNADA VOWEL SIGN AA +0CBF ; N # Mn KANNADA VOWEL SIGN I +0CC0..0CC4 ; N # Mc [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR +0CC6 ; N # Mn KANNADA VOWEL SIGN E +0CC7..0CC8 ; N # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI +0CCA..0CCB ; N # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO +0CCC..0CCD ; N # Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA +0CD5..0CD6 ; N # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK +0CDD..0CDE ; N # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA +0CE0..0CE1 ; N # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL +0CE2..0CE3 ; N # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL +0CE6..0CEF ; N # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE +0CF1..0CF2 ; N # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA +0CF3 ; N # Mc KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT +0D00..0D01 ; N # Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU +0D02..0D03 ; N # Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA +0D04..0D0C ; N # Lo [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L +0D0E..0D10 ; N # Lo [3] MALAYALAM LETTER E..MALAYALAM LETTER AI +0D12..0D3A ; N # Lo [41] MALAYALAM LETTER O..MALAYALAM LETTER TTTA +0D3B..0D3C ; N # Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA +0D3D ; N # Lo MALAYALAM SIGN AVAGRAHA +0D3E..0D40 ; N # Mc [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II +0D41..0D44 ; N # Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR +0D46..0D48 ; N # Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI +0D4A..0D4C ; N # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU +0D4D ; N # Mn MALAYALAM SIGN VIRAMA +0D4E ; N # Lo MALAYALAM LETTER DOT REPH +0D4F ; N # So MALAYALAM SIGN PARA +0D54..0D56 ; N # Lo [3] MALAYALAM LETTER CHILLU M..MALAYALAM LETTER CHILLU LLL +0D57 ; N # Mc MALAYALAM AU LENGTH MARK +0D58..0D5E ; N # No [7] MALAYALAM FRACTION ONE ONE-HUNDRED-AND-SIXTIETH..MALAYALAM FRACTION ONE FIFTH +0D5F..0D61 ; N # Lo [3] MALAYALAM LETTER ARCHAIC II..MALAYALAM LETTER VOCALIC LL +0D62..0D63 ; N # Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL +0D66..0D6F ; N # Nd [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE +0D70..0D78 ; N # No [9] MALAYALAM NUMBER TEN..MALAYALAM FRACTION THREE SIXTEENTHS +0D79 ; N # So MALAYALAM DATE MARK +0D7A..0D7F ; N # Lo [6] MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER CHILLU K +0D81 ; N # Mn SINHALA SIGN CANDRABINDU +0D82..0D83 ; N # Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA +0D85..0D96 ; N # Lo [18] SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA +0D9A..0DB1 ; N # Lo [24] SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA +0DB3..0DBB ; N # Lo [9] SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA +0DBD ; N # Lo SINHALA LETTER DANTAJA LAYANNA +0DC0..0DC6 ; N # Lo [7] SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA +0DCA ; N # Mn SINHALA SIGN AL-LAKUNA +0DCF..0DD1 ; N # Mc [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA +0DD2..0DD4 ; N # Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA +0DD6 ; N # Mn SINHALA VOWEL SIGN DIGA PAA-PILLA +0DD8..0DDF ; N # Mc [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA +0DE6..0DEF ; N # Nd [10] SINHALA LITH DIGIT ZERO..SINHALA LITH DIGIT NINE +0DF2..0DF3 ; N # Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA +0DF4 ; N # Po SINHALA PUNCTUATION KUNDDALIYA +0E01..0E30 ; N # Lo [48] THAI CHARACTER KO KAI..THAI CHARACTER SARA A +0E31 ; N # Mn THAI CHARACTER MAI HAN-AKAT +0E32..0E33 ; N # Lo [2] THAI CHARACTER SARA AA..THAI CHARACTER SARA AM +0E34..0E3A ; N # Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU +0E3F ; N # Sc THAI CURRENCY SYMBOL BAHT +0E40..0E45 ; N # Lo [6] THAI CHARACTER SARA E..THAI CHARACTER LAKKHANGYAO +0E46 ; N # Lm THAI CHARACTER MAIYAMOK +0E47..0E4E ; N # Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN +0E4F ; N # Po THAI CHARACTER FONGMAN +0E50..0E59 ; N # Nd [10] THAI DIGIT ZERO..THAI DIGIT NINE +0E5A..0E5B ; N # Po [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT +0E81..0E82 ; N # Lo [2] LAO LETTER KO..LAO LETTER KHO SUNG +0E84 ; N # Lo LAO LETTER KHO TAM +0E86..0E8A ; N # Lo [5] LAO LETTER PALI GHA..LAO LETTER SO TAM +0E8C..0EA3 ; N # Lo [24] LAO LETTER PALI JHA..LAO LETTER LO LING +0EA5 ; N # Lo LAO LETTER LO LOOT +0EA7..0EB0 ; N # Lo [10] LAO LETTER WO..LAO VOWEL SIGN A +0EB1 ; N # Mn LAO VOWEL SIGN MAI KAN +0EB2..0EB3 ; N # Lo [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM +0EB4..0EBC ; N # Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO +0EBD ; N # Lo LAO SEMIVOWEL SIGN NYO +0EC0..0EC4 ; N # Lo [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI +0EC6 ; N # Lm LAO KO LA +0EC8..0ECE ; N # Mn [7] LAO TONE MAI EK..LAO YAMAKKAN +0ED0..0ED9 ; N # Nd [10] LAO DIGIT ZERO..LAO DIGIT NINE +0EDC..0EDF ; N # Lo [4] LAO HO NO..LAO LETTER KHMU NYO +0F00 ; N # Lo TIBETAN SYLLABLE OM +0F01..0F03 ; N # So [3] TIBETAN MARK GTER YIG MGO TRUNCATED A..TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA +0F04..0F12 ; N # Po [15] TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK RGYA GRAM SHAD +0F13 ; N # So TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN +0F14 ; N # Po TIBETAN MARK GTER TSHEG +0F15..0F17 ; N # So [3] TIBETAN LOGOTYPE SIGN CHAD RTAGS..TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS +0F18..0F19 ; N # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS +0F1A..0F1F ; N # So [6] TIBETAN SIGN RDEL DKAR GCIG..TIBETAN SIGN RDEL DKAR RDEL NAG +0F20..0F29 ; N # Nd [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE +0F2A..0F33 ; N # No [10] TIBETAN DIGIT HALF ONE..TIBETAN DIGIT HALF ZERO +0F34 ; N # So TIBETAN MARK BSDUS RTAGS +0F35 ; N # Mn TIBETAN MARK NGAS BZUNG NYI ZLA +0F36 ; N # So TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN +0F37 ; N # Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS +0F38 ; N # So TIBETAN MARK CHE MGO +0F39 ; N # Mn TIBETAN MARK TSA -PHRU +0F3A ; N # Ps TIBETAN MARK GUG RTAGS GYON +0F3B ; N # Pe TIBETAN MARK GUG RTAGS GYAS +0F3C ; N # Ps TIBETAN MARK ANG KHANG GYON +0F3D ; N # Pe TIBETAN MARK ANG KHANG GYAS +0F3E..0F3F ; N # Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES +0F40..0F47 ; N # Lo [8] TIBETAN LETTER KA..TIBETAN LETTER JA +0F49..0F6C ; N # Lo [36] TIBETAN LETTER NYA..TIBETAN LETTER RRA +0F71..0F7E ; N # Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO +0F7F ; N # Mc TIBETAN SIGN RNAM BCAD +0F80..0F84 ; N # Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA +0F85 ; N # Po TIBETAN MARK PALUTA +0F86..0F87 ; N # Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS +0F88..0F8C ; N # Lo [5] TIBETAN SIGN LCE TSA CAN..TIBETAN SIGN INVERTED MCHU CAN +0F8D..0F97 ; N # Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA +0F99..0FBC ; N # Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA +0FBE..0FC5 ; N # So [8] TIBETAN KU RU KHA..TIBETAN SYMBOL RDO RJE +0FC6 ; N # Mn TIBETAN SYMBOL PADMA GDAN +0FC7..0FCC ; N # So [6] TIBETAN SYMBOL RDO RJE RGYA GRAM..TIBETAN SYMBOL NOR BU BZHI -KHYIL +0FCE..0FCF ; N # So [2] TIBETAN SIGN RDEL NAG RDEL DKAR..TIBETAN SIGN RDEL NAG GSUM +0FD0..0FD4 ; N # Po [5] TIBETAN MARK BSKA- SHOG GI MGO RGYAN..TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA +0FD5..0FD8 ; N # So [4] RIGHT-FACING SVASTI SIGN..LEFT-FACING SVASTI SIGN WITH DOTS +0FD9..0FDA ; N # Po [2] TIBETAN MARK LEADING MCHAN RTAGS..TIBETAN MARK TRAILING MCHAN RTAGS +1000..102A ; N # Lo [43] MYANMAR LETTER KA..MYANMAR LETTER AU +102B..102C ; N # Mc [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA +102D..1030 ; N # Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU +1031 ; N # Mc MYANMAR VOWEL SIGN E +1032..1037 ; N # Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW +1038 ; N # Mc MYANMAR SIGN VISARGA +1039..103A ; N # Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT +103B..103C ; N # Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA +103D..103E ; N # Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA +103F ; N # Lo MYANMAR LETTER GREAT SA +1040..1049 ; N # Nd [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE +104A..104F ; N # Po [6] MYANMAR SIGN LITTLE SECTION..MYANMAR SYMBOL GENITIVE +1050..1055 ; N # Lo [6] MYANMAR LETTER SHA..MYANMAR LETTER VOCALIC LL +1056..1057 ; N # Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR +1058..1059 ; N # Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL +105A..105D ; N # Lo [4] MYANMAR LETTER MON NGA..MYANMAR LETTER MON BBE +105E..1060 ; N # Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA +1061 ; N # Lo MYANMAR LETTER SGAW KAREN SHA +1062..1064 ; N # Mc [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO +1065..1066 ; N # Lo [2] MYANMAR LETTER WESTERN PWO KAREN THA..MYANMAR LETTER WESTERN PWO KAREN PWA +1067..106D ; N # Mc [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5 +106E..1070 ; N # Lo [3] MYANMAR LETTER EASTERN PWO KAREN NNA..MYANMAR LETTER EASTERN PWO KAREN GHWA +1071..1074 ; N # Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE +1075..1081 ; N # Lo [13] MYANMAR LETTER SHAN KA..MYANMAR LETTER SHAN HA +1082 ; N # Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA +1083..1084 ; N # Mc [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E +1085..1086 ; N # Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y +1087..108C ; N # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3 +108D ; N # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE +108E ; N # Lo MYANMAR LETTER RUMAI PALAUNG FA +108F ; N # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5 +1090..1099 ; N # Nd [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE +109A..109C ; N # Mc [3] MYANMAR SIGN KHAMTI TONE-1..MYANMAR VOWEL SIGN AITON A +109D ; N # Mn MYANMAR VOWEL SIGN AITON AI +109E..109F ; N # So [2] MYANMAR SYMBOL SHAN ONE..MYANMAR SYMBOL SHAN EXCLAMATION +10A0..10C5 ; N # Lu [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE +10C7 ; N # Lu GEORGIAN CAPITAL LETTER YN +10CD ; N # Lu GEORGIAN CAPITAL LETTER AEN +10D0..10FA ; N # Ll [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN +10FB ; N # Po GEORGIAN PARAGRAPH SEPARATOR +10FC ; N # Lm MODIFIER LETTER GEORGIAN NAR +10FD..10FF ; N # Ll [3] GEORGIAN LETTER AEN..GEORGIAN LETTER LABIAL SIGN +1100..115F ; W # Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER +1160..11FF ; N # Lo [160] HANGUL JUNGSEONG FILLER..HANGUL JONGSEONG SSANGNIEUN +1200..1248 ; N # Lo [73] ETHIOPIC SYLLABLE HA..ETHIOPIC SYLLABLE QWA +124A..124D ; N # Lo [4] ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE +1250..1256 ; N # Lo [7] ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO +1258 ; N # Lo ETHIOPIC SYLLABLE QHWA +125A..125D ; N # Lo [4] ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE +1260..1288 ; N # Lo [41] ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XWA +128A..128D ; N # Lo [4] ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE +1290..12B0 ; N # Lo [33] ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KWA +12B2..12B5 ; N # Lo [4] ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE +12B8..12BE ; N # Lo [7] ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO +12C0 ; N # Lo ETHIOPIC SYLLABLE KXWA +12C2..12C5 ; N # Lo [4] ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE +12C8..12D6 ; N # Lo [15] ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE PHARYNGEAL O +12D8..1310 ; N # Lo [57] ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE GWA +1312..1315 ; N # Lo [4] ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE +1318..135A ; N # Lo [67] ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE FYA +135D..135F ; N # Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK +1360..1368 ; N # Po [9] ETHIOPIC SECTION MARK..ETHIOPIC PARAGRAPH SEPARATOR +1369..137C ; N # No [20] ETHIOPIC DIGIT ONE..ETHIOPIC NUMBER TEN THOUSAND +1380..138F ; N # Lo [16] ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE +1390..1399 ; N # So [10] ETHIOPIC TONAL MARK YIZET..ETHIOPIC TONAL MARK KURT +13A0..13F5 ; N # Lu [86] CHEROKEE LETTER A..CHEROKEE LETTER MV +13F8..13FD ; N # Ll [6] CHEROKEE SMALL LETTER YE..CHEROKEE SMALL LETTER MV +1400 ; N # Pd CANADIAN SYLLABICS HYPHEN +1401..166C ; N # Lo [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA +166D ; N # So CANADIAN SYLLABICS CHI SIGN +166E ; N # Po CANADIAN SYLLABICS FULL STOP +166F..167F ; N # Lo [17] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS BLACKFOOT W +1680 ; N # Zs OGHAM SPACE MARK +1681..169A ; N # Lo [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH +169B ; N # Ps OGHAM FEATHER MARK +169C ; N # Pe OGHAM REVERSED FEATHER MARK +16A0..16EA ; N # Lo [75] RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X +16EB..16ED ; N # Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION +16EE..16F0 ; N # Nl [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL +16F1..16F8 ; N # Lo [8] RUNIC LETTER K..RUNIC LETTER FRANKS CASKET AESC +1700..1711 ; N # Lo [18] TAGALOG LETTER A..TAGALOG LETTER HA +1712..1714 ; N # Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA +1715 ; N # Mc TAGALOG SIGN PAMUDPOD +171F ; N # Lo TAGALOG LETTER ARCHAIC RA +1720..1731 ; N # Lo [18] HANUNOO LETTER A..HANUNOO LETTER HA +1732..1733 ; N # Mn [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U +1734 ; N # Mc HANUNOO SIGN PAMUDPOD +1735..1736 ; N # Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION +1740..1751 ; N # Lo [18] BUHID LETTER A..BUHID LETTER HA +1752..1753 ; N # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U +1760..176C ; N # Lo [13] TAGBANWA LETTER A..TAGBANWA LETTER YA +176E..1770 ; N # Lo [3] TAGBANWA LETTER LA..TAGBANWA LETTER SA +1772..1773 ; N # Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U +1780..17B3 ; N # Lo [52] KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU +17B4..17B5 ; N # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA +17B6 ; N # Mc KHMER VOWEL SIGN AA +17B7..17BD ; N # Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA +17BE..17C5 ; N # Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU +17C6 ; N # Mn KHMER SIGN NIKAHIT +17C7..17C8 ; N # Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU +17C9..17D3 ; N # Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT +17D4..17D6 ; N # Po [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH +17D7 ; N # Lm KHMER SIGN LEK TOO +17D8..17DA ; N # Po [3] KHMER SIGN BEYYAL..KHMER SIGN KOOMUUT +17DB ; N # Sc KHMER CURRENCY SYMBOL RIEL +17DC ; N # Lo KHMER SIGN AVAKRAHASANYA +17DD ; N # Mn KHMER SIGN ATTHACAN +17E0..17E9 ; N # Nd [10] KHMER DIGIT ZERO..KHMER DIGIT NINE +17F0..17F9 ; N # No [10] KHMER SYMBOL LEK ATTAK SON..KHMER SYMBOL LEK ATTAK PRAM-BUON +1800..1805 ; N # Po [6] MONGOLIAN BIRGA..MONGOLIAN FOUR DOTS +1806 ; N # Pd MONGOLIAN TODO SOFT HYPHEN +1807..180A ; N # Po [4] MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU +180B..180D ; N # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE +180E ; N # Cf MONGOLIAN VOWEL SEPARATOR +180F ; N # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR +1810..1819 ; N # Nd [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE +1820..1842 ; N # Lo [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI +1843 ; N # Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN +1844..1878 ; N # Lo [53] MONGOLIAN LETTER TODO E..MONGOLIAN LETTER CHA WITH TWO DOTS +1880..1884 ; N # Lo [5] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER ALI GALI INVERTED UBADAMA +1885..1886 ; N # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA +1887..18A8 ; N # Lo [34] MONGOLIAN LETTER ALI GALI A..MONGOLIAN LETTER MANCHU ALI GALI BHA +18A9 ; N # Mn MONGOLIAN LETTER ALI GALI DAGALGA +18AA ; N # Lo MONGOLIAN LETTER MANCHU ALI GALI LHA +18B0..18F5 ; N # Lo [70] CANADIAN SYLLABICS OY..CANADIAN SYLLABICS CARRIER DENTAL S +1900..191E ; N # Lo [31] LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER TRA +1920..1922 ; N # Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U +1923..1926 ; N # Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU +1927..1928 ; N # Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O +1929..192B ; N # Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA +1930..1931 ; N # Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA +1932 ; N # Mn LIMBU SMALL LETTER ANUSVARA +1933..1938 ; N # Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA +1939..193B ; N # Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I +1940 ; N # So LIMBU SIGN LOO +1944..1945 ; N # Po [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK +1946..194F ; N # Nd [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE +1950..196D ; N # Lo [30] TAI LE LETTER KA..TAI LE LETTER AI +1970..1974 ; N # Lo [5] TAI LE LETTER TONE-2..TAI LE LETTER TONE-6 +1980..19AB ; N # Lo [44] NEW TAI LUE LETTER HIGH QA..NEW TAI LUE LETTER LOW SUA +19B0..19C9 ; N # Lo [26] NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE TONE MARK-2 +19D0..19D9 ; N # Nd [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE +19DA ; N # No NEW TAI LUE THAM DIGIT ONE +19DE..19DF ; N # So [2] NEW TAI LUE SIGN LAE..NEW TAI LUE SIGN LAEV +19E0..19FF ; N # So [32] KHMER SYMBOL PATHAMASAT..KHMER SYMBOL DAP-PRAM ROC +1A00..1A16 ; N # Lo [23] BUGINESE LETTER KA..BUGINESE LETTER HA +1A17..1A18 ; N # Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U +1A19..1A1A ; N # Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O +1A1B ; N # Mn BUGINESE VOWEL SIGN AE +1A1E..1A1F ; N # Po [2] BUGINESE PALLAWA..BUGINESE END OF SECTION +1A20..1A54 ; N # Lo [53] TAI THAM LETTER HIGH KA..TAI THAM LETTER GREAT SA +1A55 ; N # Mc TAI THAM CONSONANT SIGN MEDIAL RA +1A56 ; N # Mn TAI THAM CONSONANT SIGN MEDIAL LA +1A57 ; N # Mc TAI THAM CONSONANT SIGN LA TANG LAI +1A58..1A5E ; N # Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA +1A60 ; N # Mn TAI THAM SIGN SAKOT +1A61 ; N # Mc TAI THAM VOWEL SIGN A +1A62 ; N # Mn TAI THAM VOWEL SIGN MAI SAT +1A63..1A64 ; N # Mc [2] TAI THAM VOWEL SIGN AA..TAI THAM VOWEL SIGN TALL AA +1A65..1A6C ; N # Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW +1A6D..1A72 ; N # Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI +1A73..1A7C ; N # Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN +1A7F ; N # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT +1A80..1A89 ; N # Nd [10] TAI THAM HORA DIGIT ZERO..TAI THAM HORA DIGIT NINE +1A90..1A99 ; N # Nd [10] TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE +1AA0..1AA6 ; N # Po [7] TAI THAM SIGN WIANG..TAI THAM SIGN REVERSED ROTATED RANA +1AA7 ; N # Lm TAI THAM SIGN MAI YAMOK +1AA8..1AAD ; N # Po [6] TAI THAM SIGN KAAN..TAI THAM SIGN CAANG +1AB0..1ABD ; N # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW +1ABE ; N # Me COMBINING PARENTHESES OVERLAY +1ABF..1ACE ; N # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T +1B00..1B03 ; N # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG +1B04 ; N # Mc BALINESE SIGN BISAH +1B05..1B33 ; N # Lo [47] BALINESE LETTER AKARA..BALINESE LETTER HA +1B34 ; N # Mn BALINESE SIGN REREKAN +1B35 ; N # Mc BALINESE VOWEL SIGN TEDUNG +1B36..1B3A ; N # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA +1B3B ; N # Mc BALINESE VOWEL SIGN RA REPA TEDUNG +1B3C ; N # Mn BALINESE VOWEL SIGN LA LENGA +1B3D..1B41 ; N # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG +1B42 ; N # Mn BALINESE VOWEL SIGN PEPET +1B43..1B44 ; N # Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG +1B45..1B4C ; N # Lo [8] BALINESE LETTER KAF SASAK..BALINESE LETTER ARCHAIC JNYA +1B50..1B59 ; N # Nd [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE +1B5A..1B60 ; N # Po [7] BALINESE PANTI..BALINESE PAMENENG +1B61..1B6A ; N # So [10] BALINESE MUSICAL SYMBOL DONG..BALINESE MUSICAL SYMBOL DANG GEDE +1B6B..1B73 ; N # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG +1B74..1B7C ; N # So [9] BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING +1B7D..1B7E ; N # Po [2] BALINESE PANTI LANTANG..BALINESE PAMADA LANTANG +1B80..1B81 ; N # Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR +1B82 ; N # Mc SUNDANESE SIGN PANGWISAD +1B83..1BA0 ; N # Lo [30] SUNDANESE LETTER A..SUNDANESE LETTER HA +1BA1 ; N # Mc SUNDANESE CONSONANT SIGN PAMINGKAL +1BA2..1BA5 ; N # Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU +1BA6..1BA7 ; N # Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG +1BA8..1BA9 ; N # Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG +1BAA ; N # Mc SUNDANESE SIGN PAMAAEH +1BAB..1BAD ; N # Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA +1BAE..1BAF ; N # Lo [2] SUNDANESE LETTER KHA..SUNDANESE LETTER SYA +1BB0..1BB9 ; N # Nd [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE +1BBA..1BBF ; N # Lo [6] SUNDANESE AVAGRAHA..SUNDANESE LETTER FINAL M +1BC0..1BE5 ; N # Lo [38] BATAK LETTER A..BATAK LETTER U +1BE6 ; N # Mn BATAK SIGN TOMPI +1BE7 ; N # Mc BATAK VOWEL SIGN E +1BE8..1BE9 ; N # Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE +1BEA..1BEC ; N # Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O +1BED ; N # Mn BATAK VOWEL SIGN KARO O +1BEE ; N # Mc BATAK VOWEL SIGN U +1BEF..1BF1 ; N # Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H +1BF2..1BF3 ; N # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN +1BFC..1BFF ; N # Po [4] BATAK SYMBOL BINDU NA METEK..BATAK SYMBOL BINDU PANGOLAT +1C00..1C23 ; N # Lo [36] LEPCHA LETTER KA..LEPCHA LETTER A +1C24..1C2B ; N # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU +1C2C..1C33 ; N # Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T +1C34..1C35 ; N # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG +1C36..1C37 ; N # Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA +1C3B..1C3F ; N # Po [5] LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION TSHOOK +1C40..1C49 ; N # Nd [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE +1C4D..1C4F ; N # Lo [3] LEPCHA LETTER TTA..LEPCHA LETTER DDA +1C50..1C59 ; N # Nd [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE +1C5A..1C77 ; N # Lo [30] OL CHIKI LETTER LA..OL CHIKI LETTER OH +1C78..1C7D ; N # Lm [6] OL CHIKI MU TTUDDAG..OL CHIKI AHAD +1C7E..1C7F ; N # Po [2] OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD +1C80..1C88 ; N # Ll [9] CYRILLIC SMALL LETTER ROUNDED VE..CYRILLIC SMALL LETTER UNBLENDED UK +1C90..1CBA ; N # Lu [43] GEORGIAN MTAVRULI CAPITAL LETTER AN..GEORGIAN MTAVRULI CAPITAL LETTER AIN +1CBD..1CBF ; N # Lu [3] GEORGIAN MTAVRULI CAPITAL LETTER AEN..GEORGIAN MTAVRULI CAPITAL LETTER LABIAL SIGN +1CC0..1CC7 ; N # Po [8] SUNDANESE PUNCTUATION BINDU SURYA..SUNDANESE PUNCTUATION BINDU BA SATANGA +1CD0..1CD2 ; N # Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA +1CD3 ; N # Po VEDIC SIGN NIHSHVASA +1CD4..1CE0 ; N # Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA +1CE1 ; N # Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA +1CE2..1CE8 ; N # Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL +1CE9..1CEC ; N # Lo [4] VEDIC SIGN ANUSVARA ANTARGOMUKHA..VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TAIL +1CED ; N # Mn VEDIC SIGN TIRYAK +1CEE..1CF3 ; N # Lo [6] VEDIC SIGN HEXIFORM LONG ANUSVARA..VEDIC SIGN ROTATED ARDHAVISARGA +1CF4 ; N # Mn VEDIC TONE CANDRA ABOVE +1CF5..1CF6 ; N # Lo [2] VEDIC SIGN JIHVAMULIYA..VEDIC SIGN UPADHMANIYA +1CF7 ; N # Mc VEDIC SIGN ATIKRAMA +1CF8..1CF9 ; N # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE +1CFA ; N # Lo VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA +1D00..1D2B ; N # Ll [44] LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL +1D2C..1D6A ; N # Lm [63] MODIFIER LETTER CAPITAL A..GREEK SUBSCRIPT SMALL LETTER CHI +1D6B..1D77 ; N # Ll [13] LATIN SMALL LETTER UE..LATIN SMALL LETTER TURNED G +1D78 ; N # Lm MODIFIER LETTER CYRILLIC EN +1D79..1D7F ; N # Ll [7] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER UPSILON WITH STROKE +1D80..1D9A ; N # Ll [27] LATIN SMALL LETTER B WITH PALATAL HOOK..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK +1D9B..1DBF ; N # Lm [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA +1DC0..1DFF ; N # Mn [64] COMBINING DOTTED GRAVE ACCENT..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW +1E00..1EFF ; N # L& [256] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER Y WITH LOOP +1F00..1F15 ; N # L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA +1F18..1F1D ; N # Lu [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA +1F20..1F45 ; N # L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA +1F48..1F4D ; N # Lu [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA +1F50..1F57 ; N # Ll [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI +1F59 ; N # Lu GREEK CAPITAL LETTER UPSILON WITH DASIA +1F5B ; N # Lu GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA +1F5D ; N # Lu GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA +1F5F..1F7D ; N # L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA +1F80..1FB4 ; N # L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI +1FB6..1FBC ; N # L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI +1FBD ; N # Sk GREEK KORONIS +1FBE ; N # Ll GREEK PROSGEGRAMMENI +1FBF..1FC1 ; N # Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI +1FC2..1FC4 ; N # Ll [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI +1FC6..1FCC ; N # L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI +1FCD..1FCF ; N # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI +1FD0..1FD3 ; N # Ll [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA +1FD6..1FDB ; N # L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA +1FDD..1FDF ; N # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI +1FE0..1FEC ; N # L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA +1FED..1FEF ; N # Sk [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA +1FF2..1FF4 ; N # Ll [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI +1FF6..1FFC ; N # L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI +1FFD..1FFE ; N # Sk [2] GREEK OXIA..GREEK DASIA +2000..200A ; N # Zs [11] EN QUAD..HAIR SPACE +200B..200F ; N # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK +2010 ; A # Pd HYPHEN +2011..2012 ; N # Pd [2] NON-BREAKING HYPHEN..FIGURE DASH +2013..2015 ; A # Pd [3] EN DASH..HORIZONTAL BAR +2016 ; A # Po DOUBLE VERTICAL LINE +2017 ; N # Po DOUBLE LOW LINE +2018 ; A # Pi LEFT SINGLE QUOTATION MARK +2019 ; A # Pf RIGHT SINGLE QUOTATION MARK +201A ; N # Ps SINGLE LOW-9 QUOTATION MARK +201B ; N # Pi SINGLE HIGH-REVERSED-9 QUOTATION MARK +201C ; A # Pi LEFT DOUBLE QUOTATION MARK +201D ; A # Pf RIGHT DOUBLE QUOTATION MARK +201E ; N # Ps DOUBLE LOW-9 QUOTATION MARK +201F ; N # Pi DOUBLE HIGH-REVERSED-9 QUOTATION MARK +2020..2022 ; A # Po [3] DAGGER..BULLET +2023 ; N # Po TRIANGULAR BULLET +2024..2027 ; A # Po [4] ONE DOT LEADER..HYPHENATION POINT +2028 ; N # Zl LINE SEPARATOR +2029 ; N # Zp PARAGRAPH SEPARATOR +202A..202E ; N # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE +202F ; N # Zs NARROW NO-BREAK SPACE +2030 ; A # Po PER MILLE SIGN +2031 ; N # Po PER TEN THOUSAND SIGN +2032..2033 ; A # Po [2] PRIME..DOUBLE PRIME +2034 ; N # Po TRIPLE PRIME +2035 ; A # Po REVERSED PRIME +2036..2038 ; N # Po [3] REVERSED DOUBLE PRIME..CARET +2039 ; N # Pi SINGLE LEFT-POINTING ANGLE QUOTATION MARK +203A ; N # Pf SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +203B ; A # Po REFERENCE MARK +203C..203D ; N # Po [2] DOUBLE EXCLAMATION MARK..INTERROBANG +203E ; A # Po OVERLINE +203F..2040 ; N # Pc [2] UNDERTIE..CHARACTER TIE +2041..2043 ; N # Po [3] CARET INSERTION POINT..HYPHEN BULLET +2044 ; N # Sm FRACTION SLASH +2045 ; N # Ps LEFT SQUARE BRACKET WITH QUILL +2046 ; N # Pe RIGHT SQUARE BRACKET WITH QUILL +2047..2051 ; N # Po [11] DOUBLE QUESTION MARK..TWO ASTERISKS ALIGNED VERTICALLY +2052 ; N # Sm COMMERCIAL MINUS SIGN +2053 ; N # Po SWUNG DASH +2054 ; N # Pc INVERTED UNDERTIE +2055..205E ; N # Po [10] FLOWER PUNCTUATION MARK..VERTICAL FOUR DOTS +205F ; N # Zs MEDIUM MATHEMATICAL SPACE +2060..2064 ; N # Cf [5] WORD JOINER..INVISIBLE PLUS +2066..206F ; N # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES +2070 ; N # No SUPERSCRIPT ZERO +2071 ; N # Lm SUPERSCRIPT LATIN SMALL LETTER I +2074 ; A # No SUPERSCRIPT FOUR +2075..2079 ; N # No [5] SUPERSCRIPT FIVE..SUPERSCRIPT NINE +207A..207C ; N # Sm [3] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT EQUALS SIGN +207D ; N # Ps SUPERSCRIPT LEFT PARENTHESIS +207E ; N # Pe SUPERSCRIPT RIGHT PARENTHESIS +207F ; A # Lm SUPERSCRIPT LATIN SMALL LETTER N +2080 ; N # No SUBSCRIPT ZERO +2081..2084 ; A # No [4] SUBSCRIPT ONE..SUBSCRIPT FOUR +2085..2089 ; N # No [5] SUBSCRIPT FIVE..SUBSCRIPT NINE +208A..208C ; N # Sm [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN +208D ; N # Ps SUBSCRIPT LEFT PARENTHESIS +208E ; N # Pe SUBSCRIPT RIGHT PARENTHESIS +2090..209C ; N # Lm [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T +20A0..20A8 ; N # Sc [9] EURO-CURRENCY SIGN..RUPEE SIGN +20A9 ; H # Sc WON SIGN +20AA..20AB ; N # Sc [2] NEW SHEQEL SIGN..DONG SIGN +20AC ; A # Sc EURO SIGN +20AD..20C0 ; N # Sc [20] KIP SIGN..SOM SIGN +20D0..20DC ; N # Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE +20DD..20E0 ; N # Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH +20E1 ; N # Mn COMBINING LEFT RIGHT ARROW ABOVE +20E2..20E4 ; N # Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE +20E5..20F0 ; N # Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE +2100..2101 ; N # So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT +2102 ; N # Lu DOUBLE-STRUCK CAPITAL C +2103 ; A # So DEGREE CELSIUS +2104 ; N # So CENTRE LINE SYMBOL +2105 ; A # So CARE OF +2106 ; N # So CADA UNA +2107 ; N # Lu EULER CONSTANT +2108 ; N # So SCRUPLE +2109 ; A # So DEGREE FAHRENHEIT +210A..2112 ; N # L& [9] SCRIPT SMALL G..SCRIPT CAPITAL L +2113 ; A # Ll SCRIPT SMALL L +2114 ; N # So L B BAR SYMBOL +2115 ; N # Lu DOUBLE-STRUCK CAPITAL N +2116 ; A # So NUMERO SIGN +2117 ; N # So SOUND RECORDING COPYRIGHT +2118 ; N # Sm SCRIPT CAPITAL P +2119..211D ; N # Lu [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R +211E..2120 ; N # So [3] PRESCRIPTION TAKE..SERVICE MARK +2121..2122 ; A # So [2] TELEPHONE SIGN..TRADE MARK SIGN +2123 ; N # So VERSICLE +2124 ; N # Lu DOUBLE-STRUCK CAPITAL Z +2125 ; N # So OUNCE SIGN +2126 ; A # Lu OHM SIGN +2127 ; N # So INVERTED OHM SIGN +2128 ; N # Lu BLACK-LETTER CAPITAL Z +2129 ; N # So TURNED GREEK SMALL LETTER IOTA +212A ; N # Lu KELVIN SIGN +212B ; A # Lu ANGSTROM SIGN +212C..212D ; N # Lu [2] SCRIPT CAPITAL B..BLACK-LETTER CAPITAL C +212E ; N # So ESTIMATED SYMBOL +212F..2134 ; N # L& [6] SCRIPT SMALL E..SCRIPT SMALL O +2135..2138 ; N # Lo [4] ALEF SYMBOL..DALET SYMBOL +2139 ; N # Ll INFORMATION SOURCE +213A..213B ; N # So [2] ROTATED CAPITAL Q..FACSIMILE SIGN +213C..213F ; N # L& [4] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK CAPITAL PI +2140..2144 ; N # Sm [5] DOUBLE-STRUCK N-ARY SUMMATION..TURNED SANS-SERIF CAPITAL Y +2145..2149 ; N # L& [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J +214A ; N # So PROPERTY LINE +214B ; N # Sm TURNED AMPERSAND +214C..214D ; N # So [2] PER SIGN..AKTIESELSKAB +214E ; N # Ll TURNED SMALL F +214F ; N # So SYMBOL FOR SAMARITAN SOURCE +2150..2152 ; N # No [3] VULGAR FRACTION ONE SEVENTH..VULGAR FRACTION ONE TENTH +2153..2154 ; A # No [2] VULGAR FRACTION ONE THIRD..VULGAR FRACTION TWO THIRDS +2155..215A ; N # No [6] VULGAR FRACTION ONE FIFTH..VULGAR FRACTION FIVE SIXTHS +215B..215E ; A # No [4] VULGAR FRACTION ONE EIGHTH..VULGAR FRACTION SEVEN EIGHTHS +215F ; N # No FRACTION NUMERATOR ONE +2160..216B ; A # Nl [12] ROMAN NUMERAL ONE..ROMAN NUMERAL TWELVE +216C..216F ; N # Nl [4] ROMAN NUMERAL FIFTY..ROMAN NUMERAL ONE THOUSAND +2170..2179 ; A # Nl [10] SMALL ROMAN NUMERAL ONE..SMALL ROMAN NUMERAL TEN +217A..2182 ; N # Nl [9] SMALL ROMAN NUMERAL ELEVEN..ROMAN NUMERAL TEN THOUSAND +2183..2184 ; N # L& [2] ROMAN NUMERAL REVERSED ONE HUNDRED..LATIN SMALL LETTER REVERSED C +2185..2188 ; N # Nl [4] ROMAN NUMERAL SIX LATE FORM..ROMAN NUMERAL ONE HUNDRED THOUSAND +2189 ; A # No VULGAR FRACTION ZERO THIRDS +218A..218B ; N # So [2] TURNED DIGIT TWO..TURNED DIGIT THREE +2190..2194 ; A # Sm [5] LEFTWARDS ARROW..LEFT RIGHT ARROW +2195..2199 ; A # So [5] UP DOWN ARROW..SOUTH WEST ARROW +219A..219B ; N # Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE +219C..219F ; N # So [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW +21A0 ; N # Sm RIGHTWARDS TWO HEADED ARROW +21A1..21A2 ; N # So [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL +21A3 ; N # Sm RIGHTWARDS ARROW WITH TAIL +21A4..21A5 ; N # So [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR +21A6 ; N # Sm RIGHTWARDS ARROW FROM BAR +21A7..21AD ; N # So [7] DOWNWARDS ARROW FROM BAR..LEFT RIGHT WAVE ARROW +21AE ; N # Sm LEFT RIGHT ARROW WITH STROKE +21AF..21B7 ; N # So [9] DOWNWARDS ZIGZAG ARROW..CLOCKWISE TOP SEMICIRCLE ARROW +21B8..21B9 ; A # So [2] NORTH WEST ARROW TO LONG BAR..LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR +21BA..21CD ; N # So [20] ANTICLOCKWISE OPEN CIRCLE ARROW..LEFTWARDS DOUBLE ARROW WITH STROKE +21CE..21CF ; N # Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE +21D0..21D1 ; N # So [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW +21D2 ; A # Sm RIGHTWARDS DOUBLE ARROW +21D3 ; N # So DOWNWARDS DOUBLE ARROW +21D4 ; A # Sm LEFT RIGHT DOUBLE ARROW +21D5..21E6 ; N # So [18] UP DOWN DOUBLE ARROW..LEFTWARDS WHITE ARROW +21E7 ; A # So UPWARDS WHITE ARROW +21E8..21F3 ; N # So [12] RIGHTWARDS WHITE ARROW..UP DOWN WHITE ARROW +21F4..21FF ; N # Sm [12] RIGHT ARROW WITH SMALL CIRCLE..LEFT RIGHT OPEN-HEADED ARROW +2200 ; A # Sm FOR ALL +2201 ; N # Sm COMPLEMENT +2202..2203 ; A # Sm [2] PARTIAL DIFFERENTIAL..THERE EXISTS +2204..2206 ; N # Sm [3] THERE DOES NOT EXIST..INCREMENT +2207..2208 ; A # Sm [2] NABLA..ELEMENT OF +2209..220A ; N # Sm [2] NOT AN ELEMENT OF..SMALL ELEMENT OF +220B ; A # Sm CONTAINS AS MEMBER +220C..220E ; N # Sm [3] DOES NOT CONTAIN AS MEMBER..END OF PROOF +220F ; A # Sm N-ARY PRODUCT +2210 ; N # Sm N-ARY COPRODUCT +2211 ; A # Sm N-ARY SUMMATION +2212..2214 ; N # Sm [3] MINUS SIGN..DOT PLUS +2215 ; A # Sm DIVISION SLASH +2216..2219 ; N # Sm [4] SET MINUS..BULLET OPERATOR +221A ; A # Sm SQUARE ROOT +221B..221C ; N # Sm [2] CUBE ROOT..FOURTH ROOT +221D..2220 ; A # Sm [4] PROPORTIONAL TO..ANGLE +2221..2222 ; N # Sm [2] MEASURED ANGLE..SPHERICAL ANGLE +2223 ; A # Sm DIVIDES +2224 ; N # Sm DOES NOT DIVIDE +2225 ; A # Sm PARALLEL TO +2226 ; N # Sm NOT PARALLEL TO +2227..222C ; A # Sm [6] LOGICAL AND..DOUBLE INTEGRAL +222D ; N # Sm TRIPLE INTEGRAL +222E ; A # Sm CONTOUR INTEGRAL +222F..2233 ; N # Sm [5] SURFACE INTEGRAL..ANTICLOCKWISE CONTOUR INTEGRAL +2234..2237 ; A # Sm [4] THEREFORE..PROPORTION +2238..223B ; N # Sm [4] DOT MINUS..HOMOTHETIC +223C..223D ; A # Sm [2] TILDE OPERATOR..REVERSED TILDE +223E..2247 ; N # Sm [10] INVERTED LAZY S..NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO +2248 ; A # Sm ALMOST EQUAL TO +2249..224B ; N # Sm [3] NOT ALMOST EQUAL TO..TRIPLE TILDE +224C ; A # Sm ALL EQUAL TO +224D..2251 ; N # Sm [5] EQUIVALENT TO..GEOMETRICALLY EQUAL TO +2252 ; A # Sm APPROXIMATELY EQUAL TO OR THE IMAGE OF +2253..225F ; N # Sm [13] IMAGE OF OR APPROXIMATELY EQUAL TO..QUESTIONED EQUAL TO +2260..2261 ; A # Sm [2] NOT EQUAL TO..IDENTICAL TO +2262..2263 ; N # Sm [2] NOT IDENTICAL TO..STRICTLY EQUIVALENT TO +2264..2267 ; A # Sm [4] LESS-THAN OR EQUAL TO..GREATER-THAN OVER EQUAL TO +2268..2269 ; N # Sm [2] LESS-THAN BUT NOT EQUAL TO..GREATER-THAN BUT NOT EQUAL TO +226A..226B ; A # Sm [2] MUCH LESS-THAN..MUCH GREATER-THAN +226C..226D ; N # Sm [2] BETWEEN..NOT EQUIVALENT TO +226E..226F ; A # Sm [2] NOT LESS-THAN..NOT GREATER-THAN +2270..2281 ; N # Sm [18] NEITHER LESS-THAN NOR EQUAL TO..DOES NOT SUCCEED +2282..2283 ; A # Sm [2] SUBSET OF..SUPERSET OF +2284..2285 ; N # Sm [2] NOT A SUBSET OF..NOT A SUPERSET OF +2286..2287 ; A # Sm [2] SUBSET OF OR EQUAL TO..SUPERSET OF OR EQUAL TO +2288..2294 ; N # Sm [13] NEITHER A SUBSET OF NOR EQUAL TO..SQUARE CUP +2295 ; A # Sm CIRCLED PLUS +2296..2298 ; N # Sm [3] CIRCLED MINUS..CIRCLED DIVISION SLASH +2299 ; A # Sm CIRCLED DOT OPERATOR +229A..22A4 ; N # Sm [11] CIRCLED RING OPERATOR..DOWN TACK +22A5 ; A # Sm UP TACK +22A6..22BE ; N # Sm [25] ASSERTION..RIGHT ANGLE WITH ARC +22BF ; A # Sm RIGHT TRIANGLE +22C0..22FF ; N # Sm [64] N-ARY LOGICAL AND..Z NOTATION BAG MEMBERSHIP +2300..2307 ; N # So [8] DIAMETER SIGN..WAVY LINE +2308 ; N # Ps LEFT CEILING +2309 ; N # Pe RIGHT CEILING +230A ; N # Ps LEFT FLOOR +230B ; N # Pe RIGHT FLOOR +230C..2311 ; N # So [6] BOTTOM RIGHT CROP..SQUARE LOZENGE +2312 ; A # So ARC +2313..2319 ; N # So [7] SEGMENT..TURNED NOT SIGN +231A..231B ; W # So [2] WATCH..HOURGLASS +231C..231F ; N # So [4] TOP LEFT CORNER..BOTTOM RIGHT CORNER +2320..2321 ; N # Sm [2] TOP HALF INTEGRAL..BOTTOM HALF INTEGRAL +2322..2328 ; N # So [7] FROWN..KEYBOARD +2329 ; W # Ps LEFT-POINTING ANGLE BRACKET +232A ; W # Pe RIGHT-POINTING ANGLE BRACKET +232B..237B ; N # So [81] ERASE TO THE LEFT..NOT CHECK MARK +237C ; N # Sm RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW +237D..239A ; N # So [30] SHOULDERED OPEN BOX..CLEAR SCREEN SYMBOL +239B..23B3 ; N # Sm [25] LEFT PARENTHESIS UPPER HOOK..SUMMATION BOTTOM +23B4..23DB ; N # So [40] TOP SQUARE BRACKET..FUSE +23DC..23E1 ; N # Sm [6] TOP PARENTHESIS..BOTTOM TORTOISE SHELL BRACKET +23E2..23E8 ; N # So [7] WHITE TRAPEZIUM..DECIMAL EXPONENT SYMBOL +23E9..23EC ; W # So [4] BLACK RIGHT-POINTING DOUBLE TRIANGLE..BLACK DOWN-POINTING DOUBLE TRIANGLE +23ED..23EF ; N # So [3] BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR..BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR +23F0 ; W # So ALARM CLOCK +23F1..23F2 ; N # So [2] STOPWATCH..TIMER CLOCK +23F3 ; W # So HOURGLASS WITH FLOWING SAND +23F4..23FF ; N # So [12] BLACK MEDIUM LEFT-POINTING TRIANGLE..OBSERVER EYE SYMBOL +2400..2426 ; N # So [39] SYMBOL FOR NULL..SYMBOL FOR SUBSTITUTE FORM TWO +2440..244A ; N # So [11] OCR HOOK..OCR DOUBLE BACKSLASH +2460..249B ; A # No [60] CIRCLED DIGIT ONE..NUMBER TWENTY FULL STOP +249C..24E9 ; A # So [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z +24EA ; N # No CIRCLED DIGIT ZERO +24EB..24FF ; A # No [21] NEGATIVE CIRCLED NUMBER ELEVEN..NEGATIVE CIRCLED DIGIT ZERO +2500..254B ; A # So [76] BOX DRAWINGS LIGHT HORIZONTAL..BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL +254C..254F ; N # So [4] BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL..BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL +2550..2573 ; A # So [36] BOX DRAWINGS DOUBLE HORIZONTAL..BOX DRAWINGS LIGHT DIAGONAL CROSS +2574..257F ; N # So [12] BOX DRAWINGS LIGHT LEFT..BOX DRAWINGS HEAVY UP AND LIGHT DOWN +2580..258F ; A # So [16] UPPER HALF BLOCK..LEFT ONE EIGHTH BLOCK +2590..2591 ; N # So [2] RIGHT HALF BLOCK..LIGHT SHADE +2592..2595 ; A # So [4] MEDIUM SHADE..RIGHT ONE EIGHTH BLOCK +2596..259F ; N # So [10] QUADRANT LOWER LEFT..QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT +25A0..25A1 ; A # So [2] BLACK SQUARE..WHITE SQUARE +25A2 ; N # So WHITE SQUARE WITH ROUNDED CORNERS +25A3..25A9 ; A # So [7] WHITE SQUARE CONTAINING BLACK SMALL SQUARE..SQUARE WITH DIAGONAL CROSSHATCH FILL +25AA..25B1 ; N # So [8] BLACK SMALL SQUARE..WHITE PARALLELOGRAM +25B2..25B3 ; A # So [2] BLACK UP-POINTING TRIANGLE..WHITE UP-POINTING TRIANGLE +25B4..25B5 ; N # So [2] BLACK UP-POINTING SMALL TRIANGLE..WHITE UP-POINTING SMALL TRIANGLE +25B6 ; A # So BLACK RIGHT-POINTING TRIANGLE +25B7 ; A # Sm WHITE RIGHT-POINTING TRIANGLE +25B8..25BB ; N # So [4] BLACK RIGHT-POINTING SMALL TRIANGLE..WHITE RIGHT-POINTING POINTER +25BC..25BD ; A # So [2] BLACK DOWN-POINTING TRIANGLE..WHITE DOWN-POINTING TRIANGLE +25BE..25BF ; N # So [2] BLACK DOWN-POINTING SMALL TRIANGLE..WHITE DOWN-POINTING SMALL TRIANGLE +25C0 ; A # So BLACK LEFT-POINTING TRIANGLE +25C1 ; A # Sm WHITE LEFT-POINTING TRIANGLE +25C2..25C5 ; N # So [4] BLACK LEFT-POINTING SMALL TRIANGLE..WHITE LEFT-POINTING POINTER +25C6..25C8 ; A # So [3] BLACK DIAMOND..WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND +25C9..25CA ; N # So [2] FISHEYE..LOZENGE +25CB ; A # So WHITE CIRCLE +25CC..25CD ; N # So [2] DOTTED CIRCLE..CIRCLE WITH VERTICAL FILL +25CE..25D1 ; A # So [4] BULLSEYE..CIRCLE WITH RIGHT HALF BLACK +25D2..25E1 ; N # So [16] CIRCLE WITH LOWER HALF BLACK..LOWER HALF CIRCLE +25E2..25E5 ; A # So [4] BLACK LOWER RIGHT TRIANGLE..BLACK UPPER RIGHT TRIANGLE +25E6..25EE ; N # So [9] WHITE BULLET..UP-POINTING TRIANGLE WITH RIGHT HALF BLACK +25EF ; A # So LARGE CIRCLE +25F0..25F7 ; N # So [8] WHITE SQUARE WITH UPPER LEFT QUADRANT..WHITE CIRCLE WITH UPPER RIGHT QUADRANT +25F8..25FC ; N # Sm [5] UPPER LEFT TRIANGLE..BLACK MEDIUM SQUARE +25FD..25FE ; W # Sm [2] WHITE MEDIUM SMALL SQUARE..BLACK MEDIUM SMALL SQUARE +25FF ; N # Sm LOWER RIGHT TRIANGLE +2600..2604 ; N # So [5] BLACK SUN WITH RAYS..COMET +2605..2606 ; A # So [2] BLACK STAR..WHITE STAR +2607..2608 ; N # So [2] LIGHTNING..THUNDERSTORM +2609 ; A # So SUN +260A..260D ; N # So [4] ASCENDING NODE..OPPOSITION +260E..260F ; A # So [2] BLACK TELEPHONE..WHITE TELEPHONE +2610..2613 ; N # So [4] BALLOT BOX..SALTIRE +2614..2615 ; W # So [2] UMBRELLA WITH RAIN DROPS..HOT BEVERAGE +2616..261B ; N # So [6] WHITE SHOGI PIECE..BLACK RIGHT POINTING INDEX +261C ; A # So WHITE LEFT POINTING INDEX +261D ; N # So WHITE UP POINTING INDEX +261E ; A # So WHITE RIGHT POINTING INDEX +261F..263F ; N # So [33] WHITE DOWN POINTING INDEX..MERCURY +2640 ; A # So FEMALE SIGN +2641 ; N # So EARTH +2642 ; A # So MALE SIGN +2643..2647 ; N # So [5] JUPITER..PLUTO +2648..2653 ; W # So [12] ARIES..PISCES +2654..265F ; N # So [12] WHITE CHESS KING..BLACK CHESS PAWN +2660..2661 ; A # So [2] BLACK SPADE SUIT..WHITE HEART SUIT +2662 ; N # So WHITE DIAMOND SUIT +2663..2665 ; A # So [3] BLACK CLUB SUIT..BLACK HEART SUIT +2666 ; N # So BLACK DIAMOND SUIT +2667..266A ; A # So [4] WHITE CLUB SUIT..EIGHTH NOTE +266B ; N # So BEAMED EIGHTH NOTES +266C..266D ; A # So [2] BEAMED SIXTEENTH NOTES..MUSIC FLAT SIGN +266E ; N # So MUSIC NATURAL SIGN +266F ; A # Sm MUSIC SHARP SIGN +2670..267E ; N # So [15] WEST SYRIAC CROSS..PERMANENT PAPER SIGN +267F ; W # So WHEELCHAIR SYMBOL +2680..2692 ; N # So [19] DIE FACE-1..HAMMER AND PICK +2693 ; W # So ANCHOR +2694..269D ; N # So [10] CROSSED SWORDS..OUTLINED WHITE STAR +269E..269F ; A # So [2] THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT +26A0 ; N # So WARNING SIGN +26A1 ; W # So HIGH VOLTAGE SIGN +26A2..26A9 ; N # So [8] DOUBLED FEMALE SIGN..HORIZONTAL MALE WITH STROKE SIGN +26AA..26AB ; W # So [2] MEDIUM WHITE CIRCLE..MEDIUM BLACK CIRCLE +26AC..26BC ; N # So [17] MEDIUM SMALL WHITE CIRCLE..SESQUIQUADRATE +26BD..26BE ; W # So [2] SOCCER BALL..BASEBALL +26BF ; A # So SQUARED KEY +26C0..26C3 ; N # So [4] WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING +26C4..26C5 ; W # So [2] SNOWMAN WITHOUT SNOW..SUN BEHIND CLOUD +26C6..26CD ; A # So [8] RAIN..DISABLED CAR +26CE ; W # So OPHIUCHUS +26CF..26D3 ; A # So [5] PICK..CHAINS +26D4 ; W # So NO ENTRY +26D5..26E1 ; A # So [13] ALTERNATE ONE-WAY LEFT WAY TRAFFIC..RESTRICTED LEFT ENTRY-2 +26E2 ; N # So ASTRONOMICAL SYMBOL FOR URANUS +26E3 ; A # So HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE +26E4..26E7 ; N # So [4] PENTAGRAM..INVERTED PENTAGRAM +26E8..26E9 ; A # So [2] BLACK CROSS ON SHIELD..SHINTO SHRINE +26EA ; W # So CHURCH +26EB..26F1 ; A # So [7] CASTLE..UMBRELLA ON GROUND +26F2..26F3 ; W # So [2] FOUNTAIN..FLAG IN HOLE +26F4 ; A # So FERRY +26F5 ; W # So SAILBOAT +26F6..26F9 ; A # So [4] SQUARE FOUR CORNERS..PERSON WITH BALL +26FA ; W # So TENT +26FB..26FC ; A # So [2] JAPANESE BANK SYMBOL..HEADSTONE GRAVEYARD SYMBOL +26FD ; W # So FUEL PUMP +26FE..26FF ; A # So [2] CUP ON BLACK SQUARE..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE +2700..2704 ; N # So [5] BLACK SAFETY SCISSORS..WHITE SCISSORS +2705 ; W # So WHITE HEAVY CHECK MARK +2706..2709 ; N # So [4] TELEPHONE LOCATION SIGN..ENVELOPE +270A..270B ; W # So [2] RAISED FIST..RAISED HAND +270C..2727 ; N # So [28] VICTORY HAND..WHITE FOUR POINTED STAR +2728 ; W # So SPARKLES +2729..273C ; N # So [20] STRESS OUTLINED WHITE STAR..OPEN CENTRE TEARDROP-SPOKED ASTERISK +273D ; A # So HEAVY TEARDROP-SPOKED ASTERISK +273E..274B ; N # So [14] SIX PETALLED BLACK AND WHITE FLORETTE..HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK +274C ; W # So CROSS MARK +274D ; N # So SHADOWED WHITE CIRCLE +274E ; W # So NEGATIVE SQUARED CROSS MARK +274F..2752 ; N # So [4] LOWER RIGHT DROP-SHADOWED WHITE SQUARE..UPPER RIGHT SHADOWED WHITE SQUARE +2753..2755 ; W # So [3] BLACK QUESTION MARK ORNAMENT..WHITE EXCLAMATION MARK ORNAMENT +2756 ; N # So BLACK DIAMOND MINUS WHITE X +2757 ; W # So HEAVY EXCLAMATION MARK SYMBOL +2758..2767 ; N # So [16] LIGHT VERTICAL BAR..ROTATED FLORAL HEART BULLET +2768 ; N # Ps MEDIUM LEFT PARENTHESIS ORNAMENT +2769 ; N # Pe MEDIUM RIGHT PARENTHESIS ORNAMENT +276A ; N # Ps MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT +276B ; N # Pe MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT +276C ; N # Ps MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT +276D ; N # Pe MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT +276E ; N # Ps HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT +276F ; N # Pe HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT +2770 ; N # Ps HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT +2771 ; N # Pe HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT +2772 ; N # Ps LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT +2773 ; N # Pe LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT +2774 ; N # Ps MEDIUM LEFT CURLY BRACKET ORNAMENT +2775 ; N # Pe MEDIUM RIGHT CURLY BRACKET ORNAMENT +2776..277F ; A # No [10] DINGBAT NEGATIVE CIRCLED DIGIT ONE..DINGBAT NEGATIVE CIRCLED NUMBER TEN +2780..2793 ; N # No [20] DINGBAT CIRCLED SANS-SERIF DIGIT ONE..DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN +2794 ; N # So HEAVY WIDE-HEADED RIGHTWARDS ARROW +2795..2797 ; W # So [3] HEAVY PLUS SIGN..HEAVY DIVISION SIGN +2798..27AF ; N # So [24] HEAVY SOUTH EAST ARROW..NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW +27B0 ; W # So CURLY LOOP +27B1..27BE ; N # So [14] NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW..OPEN-OUTLINED RIGHTWARDS ARROW +27BF ; W # So DOUBLE CURLY LOOP +27C0..27C4 ; N # Sm [5] THREE DIMENSIONAL ANGLE..OPEN SUPERSET +27C5 ; N # Ps LEFT S-SHAPED BAG DELIMITER +27C6 ; N # Pe RIGHT S-SHAPED BAG DELIMITER +27C7..27E5 ; N # Sm [31] OR WITH DOT INSIDE..WHITE SQUARE WITH RIGHTWARDS TICK +27E6 ; Na # Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET +27E7 ; Na # Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET +27E8 ; Na # Ps MATHEMATICAL LEFT ANGLE BRACKET +27E9 ; Na # Pe MATHEMATICAL RIGHT ANGLE BRACKET +27EA ; Na # Ps MATHEMATICAL LEFT DOUBLE ANGLE BRACKET +27EB ; Na # Pe MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET +27EC ; Na # Ps MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET +27ED ; Na # Pe MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET +27EE ; N # Ps MATHEMATICAL LEFT FLATTENED PARENTHESIS +27EF ; N # Pe MATHEMATICAL RIGHT FLATTENED PARENTHESIS +27F0..27FF ; N # Sm [16] UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW +2800..28FF ; N # So [256] BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678 +2900..297F ; N # Sm [128] RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..DOWN FISH TAIL +2980..2982 ; N # Sm [3] TRIPLE VERTICAL BAR DELIMITER..Z NOTATION TYPE COLON +2983 ; N # Ps LEFT WHITE CURLY BRACKET +2984 ; N # Pe RIGHT WHITE CURLY BRACKET +2985 ; Na # Ps LEFT WHITE PARENTHESIS +2986 ; Na # Pe RIGHT WHITE PARENTHESIS +2987 ; N # Ps Z NOTATION LEFT IMAGE BRACKET +2988 ; N # Pe Z NOTATION RIGHT IMAGE BRACKET +2989 ; N # Ps Z NOTATION LEFT BINDING BRACKET +298A ; N # Pe Z NOTATION RIGHT BINDING BRACKET +298B ; N # Ps LEFT SQUARE BRACKET WITH UNDERBAR +298C ; N # Pe RIGHT SQUARE BRACKET WITH UNDERBAR +298D ; N # Ps LEFT SQUARE BRACKET WITH TICK IN TOP CORNER +298E ; N # Pe RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER +298F ; N # Ps LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER +2990 ; N # Pe RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER +2991 ; N # Ps LEFT ANGLE BRACKET WITH DOT +2992 ; N # Pe RIGHT ANGLE BRACKET WITH DOT +2993 ; N # Ps LEFT ARC LESS-THAN BRACKET +2994 ; N # Pe RIGHT ARC GREATER-THAN BRACKET +2995 ; N # Ps DOUBLE LEFT ARC GREATER-THAN BRACKET +2996 ; N # Pe DOUBLE RIGHT ARC LESS-THAN BRACKET +2997 ; N # Ps LEFT BLACK TORTOISE SHELL BRACKET +2998 ; N # Pe RIGHT BLACK TORTOISE SHELL BRACKET +2999..29D7 ; N # Sm [63] DOTTED FENCE..BLACK HOURGLASS +29D8 ; N # Ps LEFT WIGGLY FENCE +29D9 ; N # Pe RIGHT WIGGLY FENCE +29DA ; N # Ps LEFT DOUBLE WIGGLY FENCE +29DB ; N # Pe RIGHT DOUBLE WIGGLY FENCE +29DC..29FB ; N # Sm [32] INCOMPLETE INFINITY..TRIPLE PLUS +29FC ; N # Ps LEFT-POINTING CURVED ANGLE BRACKET +29FD ; N # Pe RIGHT-POINTING CURVED ANGLE BRACKET +29FE..29FF ; N # Sm [2] TINY..MINY +2A00..2AFF ; N # Sm [256] N-ARY CIRCLED DOT OPERATOR..N-ARY WHITE VERTICAL BAR +2B00..2B1A ; N # So [27] NORTH EAST WHITE ARROW..DOTTED SQUARE +2B1B..2B1C ; W # So [2] BLACK LARGE SQUARE..WHITE LARGE SQUARE +2B1D..2B2F ; N # So [19] BLACK VERY SMALL SQUARE..WHITE VERTICAL ELLIPSE +2B30..2B44 ; N # Sm [21] LEFT ARROW WITH SMALL CIRCLE..RIGHTWARDS ARROW THROUGH SUPERSET +2B45..2B46 ; N # So [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW +2B47..2B4C ; N # Sm [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR +2B4D..2B4F ; N # So [3] DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW..SHORT BACKSLANTED SOUTH ARROW +2B50 ; W # So WHITE MEDIUM STAR +2B51..2B54 ; N # So [4] BLACK SMALL STAR..WHITE RIGHT-POINTING PENTAGON +2B55 ; W # So HEAVY LARGE CIRCLE +2B56..2B59 ; A # So [4] HEAVY OVAL WITH OVAL INSIDE..HEAVY CIRCLED SALTIRE +2B5A..2B73 ; N # So [26] SLANTED NORTH ARROW WITH HOOKED HEAD..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR +2B76..2B95 ; N # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW +2B97..2BFF ; N # So [105] SYMBOL FOR TYPE A ELECTRONICS..HELLSCHREIBER PAUSE SYMBOL +2C00..2C5F ; N # L& [96] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC SMALL LETTER CAUDATE CHRIVI +2C60..2C7B ; N # L& [28] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN LETTER SMALL CAPITAL TURNED E +2C7C..2C7D ; N # Lm [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V +2C7E..2C7F ; N # Lu [2] LATIN CAPITAL LETTER S WITH SWASH TAIL..LATIN CAPITAL LETTER Z WITH SWASH TAIL +2C80..2CE4 ; N # L& [101] COPTIC CAPITAL LETTER ALFA..COPTIC SYMBOL KAI +2CE5..2CEA ; N # So [6] COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA +2CEB..2CEE ; N # L& [4] COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI..COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA +2CEF..2CF1 ; N # Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS +2CF2..2CF3 ; N # L& [2] COPTIC CAPITAL LETTER BOHAIRIC KHEI..COPTIC SMALL LETTER BOHAIRIC KHEI +2CF9..2CFC ; N # Po [4] COPTIC OLD NUBIAN FULL STOP..COPTIC OLD NUBIAN VERSE DIVIDER +2CFD ; N # No COPTIC FRACTION ONE HALF +2CFE..2CFF ; N # Po [2] COPTIC FULL STOP..COPTIC MORPHOLOGICAL DIVIDER +2D00..2D25 ; N # Ll [38] GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE +2D27 ; N # Ll GEORGIAN SMALL LETTER YN +2D2D ; N # Ll GEORGIAN SMALL LETTER AEN +2D30..2D67 ; N # Lo [56] TIFINAGH LETTER YA..TIFINAGH LETTER YO +2D6F ; N # Lm TIFINAGH MODIFIER LETTER LABIALIZATION MARK +2D70 ; N # Po TIFINAGH SEPARATOR MARK +2D7F ; N # Mn TIFINAGH CONSONANT JOINER +2D80..2D96 ; N # Lo [23] ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE +2DA0..2DA6 ; N # Lo [7] ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO +2DA8..2DAE ; N # Lo [7] ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO +2DB0..2DB6 ; N # Lo [7] ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO +2DB8..2DBE ; N # Lo [7] ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO +2DC0..2DC6 ; N # Lo [7] ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO +2DC8..2DCE ; N # Lo [7] ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO +2DD0..2DD6 ; N # Lo [7] ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO +2DD8..2DDE ; N # Lo [7] ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO +2DE0..2DFF ; N # Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS +2E00..2E01 ; N # Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER +2E02 ; N # Pi LEFT SUBSTITUTION BRACKET +2E03 ; N # Pf RIGHT SUBSTITUTION BRACKET +2E04 ; N # Pi LEFT DOTTED SUBSTITUTION BRACKET +2E05 ; N # Pf RIGHT DOTTED SUBSTITUTION BRACKET +2E06..2E08 ; N # Po [3] RAISED INTERPOLATION MARKER..DOTTED TRANSPOSITION MARKER +2E09 ; N # Pi LEFT TRANSPOSITION BRACKET +2E0A ; N # Pf RIGHT TRANSPOSITION BRACKET +2E0B ; N # Po RAISED SQUARE +2E0C ; N # Pi LEFT RAISED OMISSION BRACKET +2E0D ; N # Pf RIGHT RAISED OMISSION BRACKET +2E0E..2E16 ; N # Po [9] EDITORIAL CORONIS..DOTTED RIGHT-POINTING ANGLE +2E17 ; N # Pd DOUBLE OBLIQUE HYPHEN +2E18..2E19 ; N # Po [2] INVERTED INTERROBANG..PALM BRANCH +2E1A ; N # Pd HYPHEN WITH DIAERESIS +2E1B ; N # Po TILDE WITH RING ABOVE +2E1C ; N # Pi LEFT LOW PARAPHRASE BRACKET +2E1D ; N # Pf RIGHT LOW PARAPHRASE BRACKET +2E1E..2E1F ; N # Po [2] TILDE WITH DOT ABOVE..TILDE WITH DOT BELOW +2E20 ; N # Pi LEFT VERTICAL BAR WITH QUILL +2E21 ; N # Pf RIGHT VERTICAL BAR WITH QUILL +2E22 ; N # Ps TOP LEFT HALF BRACKET +2E23 ; N # Pe TOP RIGHT HALF BRACKET +2E24 ; N # Ps BOTTOM LEFT HALF BRACKET +2E25 ; N # Pe BOTTOM RIGHT HALF BRACKET +2E26 ; N # Ps LEFT SIDEWAYS U BRACKET +2E27 ; N # Pe RIGHT SIDEWAYS U BRACKET +2E28 ; N # Ps LEFT DOUBLE PARENTHESIS +2E29 ; N # Pe RIGHT DOUBLE PARENTHESIS +2E2A..2E2E ; N # Po [5] TWO DOTS OVER ONE DOT PUNCTUATION..REVERSED QUESTION MARK +2E2F ; N # Lm VERTICAL TILDE +2E30..2E39 ; N # Po [10] RING POINT..TOP HALF SECTION SIGN +2E3A..2E3B ; N # Pd [2] TWO-EM DASH..THREE-EM DASH +2E3C..2E3F ; N # Po [4] STENOGRAPHIC FULL STOP..CAPITULUM +2E40 ; N # Pd DOUBLE HYPHEN +2E41 ; N # Po REVERSED COMMA +2E42 ; N # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK +2E43..2E4F ; N # Po [13] DASH WITH LEFT UPTURN..CORNISH VERSE DIVIDER +2E50..2E51 ; N # So [2] CROSS PATTY WITH RIGHT CROSSBAR..CROSS PATTY WITH LEFT CROSSBAR +2E52..2E54 ; N # Po [3] TIRONIAN SIGN CAPITAL ET..MEDIEVAL QUESTION MARK +2E55 ; N # Ps LEFT SQUARE BRACKET WITH STROKE +2E56 ; N # Pe RIGHT SQUARE BRACKET WITH STROKE +2E57 ; N # Ps LEFT SQUARE BRACKET WITH DOUBLE STROKE +2E58 ; N # Pe RIGHT SQUARE BRACKET WITH DOUBLE STROKE +2E59 ; N # Ps TOP HALF LEFT PARENTHESIS +2E5A ; N # Pe TOP HALF RIGHT PARENTHESIS +2E5B ; N # Ps BOTTOM HALF LEFT PARENTHESIS +2E5C ; N # Pe BOTTOM HALF RIGHT PARENTHESIS +2E5D ; N # Pd OBLIQUE HYPHEN +2E80..2E99 ; W # So [26] CJK RADICAL REPEAT..CJK RADICAL RAP +2E9B..2EF3 ; W # So [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE +2F00..2FD5 ; W # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE +2FF0..2FFF ; W # So [16] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER ROTATION +3000 ; F # Zs IDEOGRAPHIC SPACE +3001..3003 ; W # Po [3] IDEOGRAPHIC COMMA..DITTO MARK +3004 ; W # So JAPANESE INDUSTRIAL STANDARD SYMBOL +3005 ; W # Lm IDEOGRAPHIC ITERATION MARK +3006 ; W # Lo IDEOGRAPHIC CLOSING MARK +3007 ; W # Nl IDEOGRAPHIC NUMBER ZERO +3008 ; W # Ps LEFT ANGLE BRACKET +3009 ; W # Pe RIGHT ANGLE BRACKET +300A ; W # Ps LEFT DOUBLE ANGLE BRACKET +300B ; W # Pe RIGHT DOUBLE ANGLE BRACKET +300C ; W # Ps LEFT CORNER BRACKET +300D ; W # Pe RIGHT CORNER BRACKET +300E ; W # Ps LEFT WHITE CORNER BRACKET +300F ; W # Pe RIGHT WHITE CORNER BRACKET +3010 ; W # Ps LEFT BLACK LENTICULAR BRACKET +3011 ; W # Pe RIGHT BLACK LENTICULAR BRACKET +3012..3013 ; W # So [2] POSTAL MARK..GETA MARK +3014 ; W # Ps LEFT TORTOISE SHELL BRACKET +3015 ; W # Pe RIGHT TORTOISE SHELL BRACKET +3016 ; W # Ps LEFT WHITE LENTICULAR BRACKET +3017 ; W # Pe RIGHT WHITE LENTICULAR BRACKET +3018 ; W # Ps LEFT WHITE TORTOISE SHELL BRACKET +3019 ; W # Pe RIGHT WHITE TORTOISE SHELL BRACKET +301A ; W # Ps LEFT WHITE SQUARE BRACKET +301B ; W # Pe RIGHT WHITE SQUARE BRACKET +301C ; W # Pd WAVE DASH +301D ; W # Ps REVERSED DOUBLE PRIME QUOTATION MARK +301E..301F ; W # Pe [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK +3020 ; W # So POSTAL MARK FACE +3021..3029 ; W # Nl [9] HANGZHOU NUMERAL ONE..HANGZHOU NUMERAL NINE +302A..302D ; W # Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK +302E..302F ; W # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK +3030 ; W # Pd WAVY DASH +3031..3035 ; W # Lm [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF +3036..3037 ; W # So [2] CIRCLED POSTAL MARK..IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL +3038..303A ; W # Nl [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY +303B ; W # Lm VERTICAL IDEOGRAPHIC ITERATION MARK +303C ; W # Lo MASU MARK +303D ; W # Po PART ALTERNATION MARK +303E ; W # So IDEOGRAPHIC VARIATION INDICATOR +303F ; N # So IDEOGRAPHIC HALF FILL SPACE +3041..3096 ; W # Lo [86] HIRAGANA LETTER SMALL A..HIRAGANA LETTER SMALL KE +3099..309A ; W # Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK +309B..309C ; W # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK +309D..309E ; W # Lm [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK +309F ; W # Lo HIRAGANA DIGRAPH YORI +30A0 ; W # Pd KATAKANA-HIRAGANA DOUBLE HYPHEN +30A1..30FA ; W # Lo [90] KATAKANA LETTER SMALL A..KATAKANA LETTER VO +30FB ; W # Po KATAKANA MIDDLE DOT +30FC..30FE ; W # Lm [3] KATAKANA-HIRAGANA PROLONGED SOUND MARK..KATAKANA VOICED ITERATION MARK +30FF ; W # Lo KATAKANA DIGRAPH KOTO +3105..312F ; W # Lo [43] BOPOMOFO LETTER B..BOPOMOFO LETTER NN +3131..318E ; W # Lo [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE +3190..3191 ; W # So [2] IDEOGRAPHIC ANNOTATION LINKING MARK..IDEOGRAPHIC ANNOTATION REVERSE MARK +3192..3195 ; W # No [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK +3196..319F ; W # So [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK +31A0..31BF ; W # Lo [32] BOPOMOFO LETTER BU..BOPOMOFO LETTER AH +31C0..31E3 ; W # So [36] CJK STROKE T..CJK STROKE Q +31EF ; W # So IDEOGRAPHIC DESCRIPTION CHARACTER SUBTRACTION +31F0..31FF ; W # Lo [16] KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO +3200..321E ; W # So [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU +3220..3229 ; W # No [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN +322A..3247 ; W # So [30] PARENTHESIZED IDEOGRAPH MOON..CIRCLED IDEOGRAPH KOTO +3248..324F ; A # No [8] CIRCLED NUMBER TEN ON BLACK SQUARE..CIRCLED NUMBER EIGHTY ON BLACK SQUARE +3250 ; W # So PARTNERSHIP SIGN +3251..325F ; W # No [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE +3260..327F ; W # So [32] CIRCLED HANGUL KIYEOK..KOREAN STANDARD SYMBOL +3280..3289 ; W # No [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN +328A..32B0 ; W # So [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT +32B1..32BF ; W # No [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY +32C0..32FF ; W # So [64] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..SQUARE ERA NAME REIWA +3300..33FF ; W # So [256] SQUARE APAATO..SQUARE GAL +3400..4DBF ; W # Lo [6592] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DBF +4DC0..4DFF ; N # So [64] HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION +4E00..9FFF ; W # Lo [20992] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FFF +A000..A014 ; W # Lo [21] YI SYLLABLE IT..YI SYLLABLE E +A015 ; W # Lm YI SYLLABLE WU +A016..A48C ; W # Lo [1143] YI SYLLABLE BIT..YI SYLLABLE YYR +A490..A4C6 ; W # So [55] YI RADICAL QOT..YI RADICAL KE +A4D0..A4F7 ; N # Lo [40] LISU LETTER BA..LISU LETTER OE +A4F8..A4FD ; N # Lm [6] LISU LETTER TONE MYA TI..LISU LETTER TONE MYA JEU +A4FE..A4FF ; N # Po [2] LISU PUNCTUATION COMMA..LISU PUNCTUATION FULL STOP +A500..A60B ; N # Lo [268] VAI SYLLABLE EE..VAI SYLLABLE NG +A60C ; N # Lm VAI SYLLABLE LENGTHENER +A60D..A60F ; N # Po [3] VAI COMMA..VAI QUESTION MARK +A610..A61F ; N # Lo [16] VAI SYLLABLE NDOLE FA..VAI SYMBOL JONG +A620..A629 ; N # Nd [10] VAI DIGIT ZERO..VAI DIGIT NINE +A62A..A62B ; N # Lo [2] VAI SYLLABLE NDOLE MA..VAI SYLLABLE NDOLE DO +A640..A66D ; N # L& [46] CYRILLIC CAPITAL LETTER ZEMLYA..CYRILLIC SMALL LETTER DOUBLE MONOCULAR O +A66E ; N # Lo CYRILLIC LETTER MULTIOCULAR O +A66F ; N # Mn COMBINING CYRILLIC VZMET +A670..A672 ; N # Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN +A673 ; N # Po SLAVONIC ASTERISK +A674..A67D ; N # Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK +A67E ; N # Po CYRILLIC KAVYKA +A67F ; N # Lm CYRILLIC PAYEROK +A680..A69B ; N # L& [28] CYRILLIC CAPITAL LETTER DWE..CYRILLIC SMALL LETTER CROSSED O +A69C..A69D ; N # Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN +A69E..A69F ; N # Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E +A6A0..A6E5 ; N # Lo [70] BAMUM LETTER A..BAMUM LETTER KI +A6E6..A6EF ; N # Nl [10] BAMUM LETTER MO..BAMUM LETTER KOGHOM +A6F0..A6F1 ; N # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS +A6F2..A6F7 ; N # Po [6] BAMUM NJAEMLI..BAMUM QUESTION MARK +A700..A716 ; N # Sk [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR +A717..A71F ; N # Lm [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK +A720..A721 ; N # Sk [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE +A722..A76F ; N # L& [78] LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF..LATIN SMALL LETTER CON +A770 ; N # Lm MODIFIER LETTER US +A771..A787 ; N # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T +A788 ; N # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT +A789..A78A ; N # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN +A78B..A78E ; N # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT +A78F ; N # Lo LATIN LETTER SINOLOGICAL DOT +A790..A7CA ; N # L& [59] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY +A7D0..A7D1 ; N # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G +A7D3 ; N # Ll LATIN SMALL LETTER DOUBLE THORN +A7D5..A7D9 ; N # L& [5] LATIN SMALL LETTER DOUBLE WYNN..LATIN SMALL LETTER SIGMOID S +A7F2..A7F4 ; N # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A7F5..A7F6 ; N # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H +A7F7 ; N # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I +A7F8..A7F9 ; N # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE +A7FA ; N # Ll LATIN LETTER SMALL CAPITAL TURNED M +A7FB..A7FF ; N # Lo [5] LATIN EPIGRAPHIC LETTER REVERSED F..LATIN EPIGRAPHIC LETTER ARCHAIC M +A800..A801 ; N # Lo [2] SYLOTI NAGRI LETTER A..SYLOTI NAGRI LETTER I +A802 ; N # Mn SYLOTI NAGRI SIGN DVISVARA +A803..A805 ; N # Lo [3] SYLOTI NAGRI LETTER U..SYLOTI NAGRI LETTER O +A806 ; N # Mn SYLOTI NAGRI SIGN HASANTA +A807..A80A ; N # Lo [4] SYLOTI NAGRI LETTER KO..SYLOTI NAGRI LETTER GHO +A80B ; N # Mn SYLOTI NAGRI SIGN ANUSVARA +A80C..A822 ; N # Lo [23] SYLOTI NAGRI LETTER CO..SYLOTI NAGRI LETTER HO +A823..A824 ; N # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I +A825..A826 ; N # Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E +A827 ; N # Mc SYLOTI NAGRI VOWEL SIGN OO +A828..A82B ; N # So [4] SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-4 +A82C ; N # Mn SYLOTI NAGRI SIGN ALTERNATE HASANTA +A830..A835 ; N # No [6] NORTH INDIC FRACTION ONE QUARTER..NORTH INDIC FRACTION THREE SIXTEENTHS +A836..A837 ; N # So [2] NORTH INDIC QUARTER MARK..NORTH INDIC PLACEHOLDER MARK +A838 ; N # Sc NORTH INDIC RUPEE MARK +A839 ; N # So NORTH INDIC QUANTITY MARK +A840..A873 ; N # Lo [52] PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU +A874..A877 ; N # Po [4] PHAGS-PA SINGLE HEAD MARK..PHAGS-PA MARK DOUBLE SHAD +A880..A881 ; N # Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA +A882..A8B3 ; N # Lo [50] SAURASHTRA LETTER A..SAURASHTRA LETTER LLA +A8B4..A8C3 ; N # Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU +A8C4..A8C5 ; N # Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU +A8CE..A8CF ; N # Po [2] SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA +A8D0..A8D9 ; N # Nd [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE +A8E0..A8F1 ; N # Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA +A8F2..A8F7 ; N # Lo [6] DEVANAGARI SIGN SPACING CANDRABINDU..DEVANAGARI SIGN CANDRABINDU AVAGRAHA +A8F8..A8FA ; N # Po [3] DEVANAGARI SIGN PUSHPIKA..DEVANAGARI CARET +A8FB ; N # Lo DEVANAGARI HEADSTROKE +A8FC ; N # Po DEVANAGARI SIGN SIDDHAM +A8FD..A8FE ; N # Lo [2] DEVANAGARI JAIN OM..DEVANAGARI LETTER AY +A8FF ; N # Mn DEVANAGARI VOWEL SIGN AY +A900..A909 ; N # Nd [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE +A90A..A925 ; N # Lo [28] KAYAH LI LETTER KA..KAYAH LI LETTER OO +A926..A92D ; N # Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU +A92E..A92F ; N # Po [2] KAYAH LI SIGN CWI..KAYAH LI SIGN SHYA +A930..A946 ; N # Lo [23] REJANG LETTER KA..REJANG LETTER A +A947..A951 ; N # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R +A952..A953 ; N # Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA +A95F ; N # Po REJANG SECTION MARK +A960..A97C ; W # Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH +A980..A982 ; N # Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR +A983 ; N # Mc JAVANESE SIGN WIGNYAN +A984..A9B2 ; N # Lo [47] JAVANESE LETTER A..JAVANESE LETTER HA +A9B3 ; N # Mn JAVANESE SIGN CECAK TELU +A9B4..A9B5 ; N # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG +A9B6..A9B9 ; N # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT +A9BA..A9BB ; N # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE +A9BC..A9BD ; N # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET +A9BE..A9C0 ; N # Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON +A9C1..A9CD ; N # Po [13] JAVANESE LEFT RERENGGAN..JAVANESE TURNED PADA PISELEH +A9CF ; N # Lm JAVANESE PANGRANGKEP +A9D0..A9D9 ; N # Nd [10] JAVANESE DIGIT ZERO..JAVANESE DIGIT NINE +A9DE..A9DF ; N # Po [2] JAVANESE PADA TIRTA TUMETES..JAVANESE PADA ISEN-ISEN +A9E0..A9E4 ; N # Lo [5] MYANMAR LETTER SHAN GHA..MYANMAR LETTER SHAN BHA +A9E5 ; N # Mn MYANMAR SIGN SHAN SAW +A9E6 ; N # Lm MYANMAR MODIFIER LETTER SHAN REDUPLICATION +A9E7..A9EF ; N # Lo [9] MYANMAR LETTER TAI LAING NYA..MYANMAR LETTER TAI LAING NNA +A9F0..A9F9 ; N # Nd [10] MYANMAR TAI LAING DIGIT ZERO..MYANMAR TAI LAING DIGIT NINE +A9FA..A9FE ; N # Lo [5] MYANMAR LETTER TAI LAING LLA..MYANMAR LETTER TAI LAING BHA +AA00..AA28 ; N # Lo [41] CHAM LETTER A..CHAM LETTER HA +AA29..AA2E ; N # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE +AA2F..AA30 ; N # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI +AA31..AA32 ; N # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE +AA33..AA34 ; N # Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA +AA35..AA36 ; N # Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA +AA40..AA42 ; N # Lo [3] CHAM LETTER FINAL K..CHAM LETTER FINAL NG +AA43 ; N # Mn CHAM CONSONANT SIGN FINAL NG +AA44..AA4B ; N # Lo [8] CHAM LETTER FINAL CH..CHAM LETTER FINAL SS +AA4C ; N # Mn CHAM CONSONANT SIGN FINAL M +AA4D ; N # Mc CHAM CONSONANT SIGN FINAL H +AA50..AA59 ; N # Nd [10] CHAM DIGIT ZERO..CHAM DIGIT NINE +AA5C..AA5F ; N # Po [4] CHAM PUNCTUATION SPIRAL..CHAM PUNCTUATION TRIPLE DANDA +AA60..AA6F ; N # Lo [16] MYANMAR LETTER KHAMTI GA..MYANMAR LETTER KHAMTI FA +AA70 ; N # Lm MYANMAR MODIFIER LETTER KHAMTI REDUPLICATION +AA71..AA76 ; N # Lo [6] MYANMAR LETTER KHAMTI XA..MYANMAR LOGOGRAM KHAMTI HM +AA77..AA79 ; N # So [3] MYANMAR SYMBOL AITON EXCLAMATION..MYANMAR SYMBOL AITON TWO +AA7A ; N # Lo MYANMAR LETTER AITON RA +AA7B ; N # Mc MYANMAR SIGN PAO KAREN TONE +AA7C ; N # Mn MYANMAR SIGN TAI LAING TONE-2 +AA7D ; N # Mc MYANMAR SIGN TAI LAING TONE-5 +AA7E..AA7F ; N # Lo [2] MYANMAR LETTER SHWE PALAUNG CHA..MYANMAR LETTER SHWE PALAUNG SHA +AA80..AAAF ; N # Lo [48] TAI VIET LETTER LOW KO..TAI VIET LETTER HIGH O +AAB0 ; N # Mn TAI VIET MAI KANG +AAB1 ; N # Lo TAI VIET VOWEL AA +AAB2..AAB4 ; N # Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U +AAB5..AAB6 ; N # Lo [2] TAI VIET VOWEL E..TAI VIET VOWEL O +AAB7..AAB8 ; N # Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA +AAB9..AABD ; N # Lo [5] TAI VIET VOWEL UEA..TAI VIET VOWEL AN +AABE..AABF ; N # Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK +AAC0 ; N # Lo TAI VIET TONE MAI NUENG +AAC1 ; N # Mn TAI VIET TONE MAI THO +AAC2 ; N # Lo TAI VIET TONE MAI SONG +AADB..AADC ; N # Lo [2] TAI VIET SYMBOL KON..TAI VIET SYMBOL NUENG +AADD ; N # Lm TAI VIET SYMBOL SAM +AADE..AADF ; N # Po [2] TAI VIET SYMBOL HO HOI..TAI VIET SYMBOL KOI KOI +AAE0..AAEA ; N # Lo [11] MEETEI MAYEK LETTER E..MEETEI MAYEK LETTER SSA +AAEB ; N # Mc MEETEI MAYEK VOWEL SIGN II +AAEC..AAED ; N # Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI +AAEE..AAEF ; N # Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU +AAF0..AAF1 ; N # Po [2] MEETEI MAYEK CHEIKHAN..MEETEI MAYEK AHANG KHUDAM +AAF2 ; N # Lo MEETEI MAYEK ANJI +AAF3..AAF4 ; N # Lm [2] MEETEI MAYEK SYLLABLE REPETITION MARK..MEETEI MAYEK WORD REPETITION MARK +AAF5 ; N # Mc MEETEI MAYEK VOWEL SIGN VISARGA +AAF6 ; N # Mn MEETEI MAYEK VIRAMA +AB01..AB06 ; N # Lo [6] ETHIOPIC SYLLABLE TTHU..ETHIOPIC SYLLABLE TTHO +AB09..AB0E ; N # Lo [6] ETHIOPIC SYLLABLE DDHU..ETHIOPIC SYLLABLE DDHO +AB11..AB16 ; N # Lo [6] ETHIOPIC SYLLABLE DZU..ETHIOPIC SYLLABLE DZO +AB20..AB26 ; N # Lo [7] ETHIOPIC SYLLABLE CCHHA..ETHIOPIC SYLLABLE CCHHO +AB28..AB2E ; N # Lo [7] ETHIOPIC SYLLABLE BBA..ETHIOPIC SYLLABLE BBO +AB30..AB5A ; N # Ll [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMALL LETTER Y WITH SHORT RIGHT LEG +AB5B ; N # Sk MODIFIER BREVE WITH INVERTED BREVE +AB5C..AB5F ; N # Lm [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK +AB60..AB68 ; N # Ll [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE +AB69 ; N # Lm MODIFIER LETTER SMALL TURNED W +AB6A..AB6B ; N # Sk [2] MODIFIER LETTER LEFT TACK..MODIFIER LETTER RIGHT TACK +AB70..ABBF ; N # Ll [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA +ABC0..ABE2 ; N # Lo [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM +ABE3..ABE4 ; N # Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP +ABE5 ; N # Mn MEETEI MAYEK VOWEL SIGN ANAP +ABE6..ABE7 ; N # Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP +ABE8 ; N # Mn MEETEI MAYEK VOWEL SIGN UNAP +ABE9..ABEA ; N # Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG +ABEB ; N # Po MEETEI MAYEK CHEIKHEI +ABEC ; N # Mc MEETEI MAYEK LUM IYEK +ABED ; N # Mn MEETEI MAYEK APUN IYEK +ABF0..ABF9 ; N # Nd [10] MEETEI MAYEK DIGIT ZERO..MEETEI MAYEK DIGIT NINE +AC00..D7A3 ; W # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH +D7B0..D7C6 ; N # Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E +D7CB..D7FB ; N # Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH +D800..DB7F ; N # Cs [896] <surrogate-D800>..<surrogate-DB7F> +DB80..DBFF ; N # Cs [128] <surrogate-DB80>..<surrogate-DBFF> +DC00..DFFF ; N # Cs [1024] <surrogate-DC00>..<surrogate-DFFF> +E000..F8FF ; A # Co [6400] <private-use-E000>..<private-use-F8FF> +F900..FA6D ; W # Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA6D +FA6E..FA6F ; W # Cn [2] <reserved-FA6E>..<reserved-FA6F> +FA70..FAD9 ; W # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9 +FADA..FAFF ; W # Cn [38] <reserved-FADA>..<reserved-FAFF> +FB00..FB06 ; N # Ll [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST +FB13..FB17 ; N # Ll [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH +FB1D ; N # Lo HEBREW LETTER YOD WITH HIRIQ +FB1E ; N # Mn HEBREW POINT JUDEO-SPANISH VARIKA +FB1F..FB28 ; N # Lo [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV +FB29 ; N # Sm HEBREW LETTER ALTERNATIVE PLUS SIGN +FB2A..FB36 ; N # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH +FB38..FB3C ; N # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH +FB3E ; N # Lo HEBREW LETTER MEM WITH DAGESH +FB40..FB41 ; N # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH +FB43..FB44 ; N # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH +FB46..FB4F ; N # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED +FB50..FBB1 ; N # Lo [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM +FBB2..FBC2 ; N # Sk [17] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL WASLA ABOVE +FBD3..FD3D ; N # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM +FD3E ; N # Pe ORNATE LEFT PARENTHESIS +FD3F ; N # Ps ORNATE RIGHT PARENTHESIS +FD40..FD4F ; N # So [16] ARABIC LIGATURE RAHIMAHU ALLAAH..ARABIC LIGATURE RAHIMAHUM ALLAAH +FD50..FD8F ; N # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM +FD92..FDC7 ; N # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM +FDCF ; N # So ARABIC LIGATURE SALAAMUHU ALAYNAA +FDF0..FDFB ; N # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU +FDFC ; N # Sc RIAL SIGN +FDFD..FDFF ; N # So [3] ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM..ARABIC LIGATURE AZZA WA JALL +FE00..FE0F ; A # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 +FE10..FE16 ; W # Po [7] PRESENTATION FORM FOR VERTICAL COMMA..PRESENTATION FORM FOR VERTICAL QUESTION MARK +FE17 ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET +FE18 ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET +FE19 ; W # Po PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS +FE20..FE2F ; N # Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF +FE30 ; W # Po PRESENTATION FORM FOR VERTICAL TWO DOT LEADER +FE31..FE32 ; W # Pd [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH +FE33..FE34 ; W # Pc [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE +FE35 ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS +FE36 ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS +FE37 ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET +FE38 ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET +FE39 ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET +FE3A ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET +FE3B ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET +FE3C ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET +FE3D ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET +FE3E ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET +FE3F ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET +FE40 ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET +FE41 ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET +FE42 ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET +FE43 ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET +FE44 ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET +FE45..FE46 ; W # Po [2] SESAME DOT..WHITE SESAME DOT +FE47 ; W # Ps PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET +FE48 ; W # Pe PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET +FE49..FE4C ; W # Po [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE +FE4D..FE4F ; W # Pc [3] DASHED LOW LINE..WAVY LOW LINE +FE50..FE52 ; W # Po [3] SMALL COMMA..SMALL FULL STOP +FE54..FE57 ; W # Po [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK +FE58 ; W # Pd SMALL EM DASH +FE59 ; W # Ps SMALL LEFT PARENTHESIS +FE5A ; W # Pe SMALL RIGHT PARENTHESIS +FE5B ; W # Ps SMALL LEFT CURLY BRACKET +FE5C ; W # Pe SMALL RIGHT CURLY BRACKET +FE5D ; W # Ps SMALL LEFT TORTOISE SHELL BRACKET +FE5E ; W # Pe SMALL RIGHT TORTOISE SHELL BRACKET +FE5F..FE61 ; W # Po [3] SMALL NUMBER SIGN..SMALL ASTERISK +FE62 ; W # Sm SMALL PLUS SIGN +FE63 ; W # Pd SMALL HYPHEN-MINUS +FE64..FE66 ; W # Sm [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN +FE68 ; W # Po SMALL REVERSE SOLIDUS +FE69 ; W # Sc SMALL DOLLAR SIGN +FE6A..FE6B ; W # Po [2] SMALL PERCENT SIGN..SMALL COMMERCIAL AT +FE70..FE74 ; N # Lo [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN ISOLATED FORM +FE76..FEFC ; N # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM +FEFF ; N # Cf ZERO WIDTH NO-BREAK SPACE +FF01..FF03 ; F # Po [3] FULLWIDTH EXCLAMATION MARK..FULLWIDTH NUMBER SIGN +FF04 ; F # Sc FULLWIDTH DOLLAR SIGN +FF05..FF07 ; F # Po [3] FULLWIDTH PERCENT SIGN..FULLWIDTH APOSTROPHE +FF08 ; F # Ps FULLWIDTH LEFT PARENTHESIS +FF09 ; F # Pe FULLWIDTH RIGHT PARENTHESIS +FF0A ; F # Po FULLWIDTH ASTERISK +FF0B ; F # Sm FULLWIDTH PLUS SIGN +FF0C ; F # Po FULLWIDTH COMMA +FF0D ; F # Pd FULLWIDTH HYPHEN-MINUS +FF0E..FF0F ; F # Po [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS +FF10..FF19 ; F # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE +FF1A..FF1B ; F # Po [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON +FF1C..FF1E ; F # Sm [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN +FF1F..FF20 ; F # Po [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT +FF21..FF3A ; F # Lu [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z +FF3B ; F # Ps FULLWIDTH LEFT SQUARE BRACKET +FF3C ; F # Po FULLWIDTH REVERSE SOLIDUS +FF3D ; F # Pe FULLWIDTH RIGHT SQUARE BRACKET +FF3E ; F # Sk FULLWIDTH CIRCUMFLEX ACCENT +FF3F ; F # Pc FULLWIDTH LOW LINE +FF40 ; F # Sk FULLWIDTH GRAVE ACCENT +FF41..FF5A ; F # Ll [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z +FF5B ; F # Ps FULLWIDTH LEFT CURLY BRACKET +FF5C ; F # Sm FULLWIDTH VERTICAL LINE +FF5D ; F # Pe FULLWIDTH RIGHT CURLY BRACKET +FF5E ; F # Sm FULLWIDTH TILDE +FF5F ; F # Ps FULLWIDTH LEFT WHITE PARENTHESIS +FF60 ; F # Pe FULLWIDTH RIGHT WHITE PARENTHESIS +FF61 ; H # Po HALFWIDTH IDEOGRAPHIC FULL STOP +FF62 ; H # Ps HALFWIDTH LEFT CORNER BRACKET +FF63 ; H # Pe HALFWIDTH RIGHT CORNER BRACKET +FF64..FF65 ; H # Po [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDLE DOT +FF66..FF6F ; H # Lo [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU +FF70 ; H # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK +FF71..FF9D ; H # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N +FF9E..FF9F ; H # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK +FFA0..FFBE ; H # Lo [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH +FFC2..FFC7 ; H # Lo [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E +FFCA..FFCF ; H # Lo [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE +FFD2..FFD7 ; H # Lo [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU +FFDA..FFDC ; H # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I +FFE0..FFE1 ; F # Sc [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN +FFE2 ; F # Sm FULLWIDTH NOT SIGN +FFE3 ; F # Sk FULLWIDTH MACRON +FFE4 ; F # So FULLWIDTH BROKEN BAR +FFE5..FFE6 ; F # Sc [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN +FFE8 ; H # So HALFWIDTH FORMS LIGHT VERTICAL +FFE9..FFEC ; H # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW +FFED..FFEE ; H # So [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE +FFF9..FFFB ; N # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR +FFFC ; N # So OBJECT REPLACEMENT CHARACTER +FFFD ; A # So REPLACEMENT CHARACTER +10000..1000B ; N # Lo [12] LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE +1000D..10026 ; N # Lo [26] LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO +10028..1003A ; N # Lo [19] LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO +1003C..1003D ; N # Lo [2] LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE +1003F..1004D ; N # Lo [15] LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO +10050..1005D ; N # Lo [14] LINEAR B SYMBOL B018..LINEAR B SYMBOL B089 +10080..100FA ; N # Lo [123] LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305 +10100..10102 ; N # Po [3] AEGEAN WORD SEPARATOR LINE..AEGEAN CHECK MARK +10107..10133 ; N # No [45] AEGEAN NUMBER ONE..AEGEAN NUMBER NINETY THOUSAND +10137..1013F ; N # So [9] AEGEAN WEIGHT BASE UNIT..AEGEAN MEASURE THIRD SUBUNIT +10140..10174 ; N # Nl [53] GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ACROPHONIC STRATIAN FIFTY MNAS +10175..10178 ; N # No [4] GREEK ONE HALF SIGN..GREEK THREE QUARTERS SIGN +10179..10189 ; N # So [17] GREEK YEAR SIGN..GREEK TRYBLION BASE SIGN +1018A..1018B ; N # No [2] GREEK ZERO SIGN..GREEK ONE QUARTER SIGN +1018C..1018E ; N # So [3] GREEK SINUSOID SIGN..NOMISMA SIGN +10190..1019C ; N # So [13] ROMAN SEXTANS SIGN..ASCIA SYMBOL +101A0 ; N # So GREEK SYMBOL TAU RHO +101D0..101FC ; N # So [45] PHAISTOS DISC SIGN PEDESTRIAN..PHAISTOS DISC SIGN WAVY BAND +101FD ; N # Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE +10280..1029C ; N # Lo [29] LYCIAN LETTER A..LYCIAN LETTER X +102A0..102D0 ; N # Lo [49] CARIAN LETTER A..CARIAN LETTER UUU3 +102E0 ; N # Mn COPTIC EPACT THOUSANDS MARK +102E1..102FB ; N # No [27] COPTIC EPACT DIGIT ONE..COPTIC EPACT NUMBER NINE HUNDRED +10300..1031F ; N # Lo [32] OLD ITALIC LETTER A..OLD ITALIC LETTER ESS +10320..10323 ; N # No [4] OLD ITALIC NUMERAL ONE..OLD ITALIC NUMERAL FIFTY +1032D..1032F ; N # Lo [3] OLD ITALIC LETTER YE..OLD ITALIC LETTER SOUTHERN TSE +10330..10340 ; N # Lo [17] GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA +10341 ; N # Nl GOTHIC LETTER NINETY +10342..10349 ; N # Lo [8] GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL +1034A ; N # Nl GOTHIC LETTER NINE HUNDRED +10350..10375 ; N # Lo [38] OLD PERMIC LETTER AN..OLD PERMIC LETTER IA +10376..1037A ; N # Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII +10380..1039D ; N # Lo [30] UGARITIC LETTER ALPA..UGARITIC LETTER SSU +1039F ; N # Po UGARITIC WORD DIVIDER +103A0..103C3 ; N # Lo [36] OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA +103C8..103CF ; N # Lo [8] OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH +103D0 ; N # Po OLD PERSIAN WORD DIVIDER +103D1..103D5 ; N # Nl [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED +10400..1044F ; N # L& [80] DESERET CAPITAL LETTER LONG I..DESERET SMALL LETTER EW +10450..1047F ; N # Lo [48] SHAVIAN LETTER PEEP..SHAVIAN LETTER YEW +10480..1049D ; N # Lo [30] OSMANYA LETTER ALEF..OSMANYA LETTER OO +104A0..104A9 ; N # Nd [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE +104B0..104D3 ; N # Lu [36] OSAGE CAPITAL LETTER A..OSAGE CAPITAL LETTER ZHA +104D8..104FB ; N # Ll [36] OSAGE SMALL LETTER A..OSAGE SMALL LETTER ZHA +10500..10527 ; N # Lo [40] ELBASAN LETTER A..ELBASAN LETTER KHE +10530..10563 ; N # Lo [52] CAUCASIAN ALBANIAN LETTER ALT..CAUCASIAN ALBANIAN LETTER KIW +1056F ; N # Po CAUCASIAN ALBANIAN CITATION MARK +10570..1057A ; N # Lu [11] VITHKUQI CAPITAL LETTER A..VITHKUQI CAPITAL LETTER GA +1057C..1058A ; N # Lu [15] VITHKUQI CAPITAL LETTER HA..VITHKUQI CAPITAL LETTER RE +1058C..10592 ; N # Lu [7] VITHKUQI CAPITAL LETTER SE..VITHKUQI CAPITAL LETTER XE +10594..10595 ; N # Lu [2] VITHKUQI CAPITAL LETTER Y..VITHKUQI CAPITAL LETTER ZE +10597..105A1 ; N # Ll [11] VITHKUQI SMALL LETTER A..VITHKUQI SMALL LETTER GA +105A3..105B1 ; N # Ll [15] VITHKUQI SMALL LETTER HA..VITHKUQI SMALL LETTER RE +105B3..105B9 ; N # Ll [7] VITHKUQI SMALL LETTER SE..VITHKUQI SMALL LETTER XE +105BB..105BC ; N # Ll [2] VITHKUQI SMALL LETTER Y..VITHKUQI SMALL LETTER ZE +10600..10736 ; N # Lo [311] LINEAR A SIGN AB001..LINEAR A SIGN A664 +10740..10755 ; N # Lo [22] LINEAR A SIGN A701 A..LINEAR A SIGN A732 JE +10760..10767 ; N # Lo [8] LINEAR A SIGN A800..LINEAR A SIGN A807 +10780..10785 ; N # Lm [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK +10787..107B0 ; N # Lm [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK +107B2..107BA ; N # Lm [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL +10800..10805 ; N # Lo [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA +10808 ; N # Lo CYPRIOT SYLLABLE JO +1080A..10835 ; N # Lo [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO +10837..10838 ; N # Lo [2] CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE +1083C ; N # Lo CYPRIOT SYLLABLE ZA +1083F ; N # Lo CYPRIOT SYLLABLE ZO +10840..10855 ; N # Lo [22] IMPERIAL ARAMAIC LETTER ALEPH..IMPERIAL ARAMAIC LETTER TAW +10857 ; N # Po IMPERIAL ARAMAIC SECTION SIGN +10858..1085F ; N # No [8] IMPERIAL ARAMAIC NUMBER ONE..IMPERIAL ARAMAIC NUMBER TEN THOUSAND +10860..10876 ; N # Lo [23] PALMYRENE LETTER ALEPH..PALMYRENE LETTER TAW +10877..10878 ; N # So [2] PALMYRENE LEFT-POINTING FLEURON..PALMYRENE RIGHT-POINTING FLEURON +10879..1087F ; N # No [7] PALMYRENE NUMBER ONE..PALMYRENE NUMBER TWENTY +10880..1089E ; N # Lo [31] NABATAEAN LETTER FINAL ALEPH..NABATAEAN LETTER TAW +108A7..108AF ; N # No [9] NABATAEAN NUMBER ONE..NABATAEAN NUMBER ONE HUNDRED +108E0..108F2 ; N # Lo [19] HATRAN LETTER ALEPH..HATRAN LETTER QOPH +108F4..108F5 ; N # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW +108FB..108FF ; N # No [5] HATRAN NUMBER ONE..HATRAN NUMBER ONE HUNDRED +10900..10915 ; N # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU +10916..1091B ; N # No [6] PHOENICIAN NUMBER ONE..PHOENICIAN NUMBER THREE +1091F ; N # Po PHOENICIAN WORD SEPARATOR +10920..10939 ; N # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C +1093F ; N # Po LYDIAN TRIANGULAR MARK +10980..1099F ; N # Lo [32] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2 +109A0..109B7 ; N # Lo [24] MEROITIC CURSIVE LETTER A..MEROITIC CURSIVE LETTER DA +109BC..109BD ; N # No [2] MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS..MEROITIC CURSIVE FRACTION ONE HALF +109BE..109BF ; N # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN +109C0..109CF ; N # No [16] MEROITIC CURSIVE NUMBER ONE..MEROITIC CURSIVE NUMBER SEVENTY +109D2..109FF ; N # No [46] MEROITIC CURSIVE NUMBER ONE HUNDRED..MEROITIC CURSIVE FRACTION TEN TWELFTHS +10A00 ; N # Lo KHAROSHTHI LETTER A +10A01..10A03 ; N # Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R +10A05..10A06 ; N # Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O +10A0C..10A0F ; N # Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA +10A10..10A13 ; N # Lo [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA +10A15..10A17 ; N # Lo [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA +10A19..10A35 ; N # Lo [29] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER VHA +10A38..10A3A ; N # Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW +10A3F ; N # Mn KHAROSHTHI VIRAMA +10A40..10A48 ; N # No [9] KHAROSHTHI DIGIT ONE..KHAROSHTHI FRACTION ONE HALF +10A50..10A58 ; N # Po [9] KHAROSHTHI PUNCTUATION DOT..KHAROSHTHI PUNCTUATION LINES +10A60..10A7C ; N # Lo [29] OLD SOUTH ARABIAN LETTER HE..OLD SOUTH ARABIAN LETTER THETH +10A7D..10A7E ; N # No [2] OLD SOUTH ARABIAN NUMBER ONE..OLD SOUTH ARABIAN NUMBER FIFTY +10A7F ; N # Po OLD SOUTH ARABIAN NUMERIC INDICATOR +10A80..10A9C ; N # Lo [29] OLD NORTH ARABIAN LETTER HEH..OLD NORTH ARABIAN LETTER ZAH +10A9D..10A9F ; N # No [3] OLD NORTH ARABIAN NUMBER ONE..OLD NORTH ARABIAN NUMBER TWENTY +10AC0..10AC7 ; N # Lo [8] MANICHAEAN LETTER ALEPH..MANICHAEAN LETTER WAW +10AC8 ; N # So MANICHAEAN SIGN UD +10AC9..10AE4 ; N # Lo [28] MANICHAEAN LETTER ZAYIN..MANICHAEAN LETTER TAW +10AE5..10AE6 ; N # Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW +10AEB..10AEF ; N # No [5] MANICHAEAN NUMBER ONE..MANICHAEAN NUMBER ONE HUNDRED +10AF0..10AF6 ; N # Po [7] MANICHAEAN PUNCTUATION STAR..MANICHAEAN PUNCTUATION LINE FILLER +10B00..10B35 ; N # Lo [54] AVESTAN LETTER A..AVESTAN LETTER HE +10B39..10B3F ; N # Po [7] AVESTAN ABBREVIATION MARK..LARGE ONE RING OVER TWO RINGS PUNCTUATION +10B40..10B55 ; N # Lo [22] INSCRIPTIONAL PARTHIAN LETTER ALEPH..INSCRIPTIONAL PARTHIAN LETTER TAW +10B58..10B5F ; N # No [8] INSCRIPTIONAL PARTHIAN NUMBER ONE..INSCRIPTIONAL PARTHIAN NUMBER ONE THOUSAND +10B60..10B72 ; N # Lo [19] INSCRIPTIONAL PAHLAVI LETTER ALEPH..INSCRIPTIONAL PAHLAVI LETTER TAW +10B78..10B7F ; N # No [8] INSCRIPTIONAL PAHLAVI NUMBER ONE..INSCRIPTIONAL PAHLAVI NUMBER ONE THOUSAND +10B80..10B91 ; N # Lo [18] PSALTER PAHLAVI LETTER ALEPH..PSALTER PAHLAVI LETTER TAW +10B99..10B9C ; N # Po [4] PSALTER PAHLAVI SECTION MARK..PSALTER PAHLAVI FOUR DOTS WITH DOT +10BA9..10BAF ; N # No [7] PSALTER PAHLAVI NUMBER ONE..PSALTER PAHLAVI NUMBER ONE HUNDRED +10C00..10C48 ; N # Lo [73] OLD TURKIC LETTER ORKHON A..OLD TURKIC LETTER ORKHON BASH +10C80..10CB2 ; N # Lu [51] OLD HUNGARIAN CAPITAL LETTER A..OLD HUNGARIAN CAPITAL LETTER US +10CC0..10CF2 ; N # Ll [51] OLD HUNGARIAN SMALL LETTER A..OLD HUNGARIAN SMALL LETTER US +10CFA..10CFF ; N # No [6] OLD HUNGARIAN NUMBER ONE..OLD HUNGARIAN NUMBER ONE THOUSAND +10D00..10D23 ; N # Lo [36] HANIFI ROHINGYA LETTER A..HANIFI ROHINGYA MARK NA KHONNA +10D24..10D27 ; N # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI +10D30..10D39 ; N # Nd [10] HANIFI ROHINGYA DIGIT ZERO..HANIFI ROHINGYA DIGIT NINE +10E60..10E7E ; N # No [31] RUMI DIGIT ONE..RUMI FRACTION TWO THIRDS +10E80..10EA9 ; N # Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET +10EAB..10EAC ; N # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK +10EAD ; N # Pd YEZIDI HYPHENATION MARK +10EB0..10EB1 ; N # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE +10EFD..10EFF ; N # Mn [3] ARABIC SMALL LOW WORD SAKTA..ARABIC SMALL LOW WORD MADDA +10F00..10F1C ; N # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL +10F1D..10F26 ; N # No [10] OLD SOGDIAN NUMBER ONE..OLD SOGDIAN FRACTION ONE HALF +10F27 ; N # Lo OLD SOGDIAN LIGATURE AYIN-DALETH +10F30..10F45 ; N # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN +10F46..10F50 ; N # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW +10F51..10F54 ; N # No [4] SOGDIAN NUMBER ONE..SOGDIAN NUMBER ONE HUNDRED +10F55..10F59 ; N # Po [5] SOGDIAN PUNCTUATION TWO VERTICAL BARS..SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT +10F70..10F81 ; N # Lo [18] OLD UYGHUR LETTER ALEPH..OLD UYGHUR LETTER LESH +10F82..10F85 ; N # Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW +10F86..10F89 ; N # Po [4] OLD UYGHUR PUNCTUATION BAR..OLD UYGHUR PUNCTUATION FOUR DOTS +10FB0..10FC4 ; N # Lo [21] CHORASMIAN LETTER ALEPH..CHORASMIAN LETTER TAW +10FC5..10FCB ; N # No [7] CHORASMIAN NUMBER ONE..CHORASMIAN NUMBER ONE HUNDRED +10FE0..10FF6 ; N # Lo [23] ELYMAIC LETTER ALEPH..ELYMAIC LIGATURE ZAYIN-YODH +11000 ; N # Mc BRAHMI SIGN CANDRABINDU +11001 ; N # Mn BRAHMI SIGN ANUSVARA +11002 ; N # Mc BRAHMI SIGN VISARGA +11003..11037 ; N # Lo [53] BRAHMI SIGN JIHVAMULIYA..BRAHMI LETTER OLD TAMIL NNNA +11038..11046 ; N # Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA +11047..1104D ; N # Po [7] BRAHMI DANDA..BRAHMI PUNCTUATION LOTUS +11052..11065 ; N # No [20] BRAHMI NUMBER ONE..BRAHMI NUMBER ONE THOUSAND +11066..1106F ; N # Nd [10] BRAHMI DIGIT ZERO..BRAHMI DIGIT NINE +11070 ; N # Mn BRAHMI SIGN OLD TAMIL VIRAMA +11071..11072 ; N # Lo [2] BRAHMI LETTER OLD TAMIL SHORT E..BRAHMI LETTER OLD TAMIL SHORT O +11073..11074 ; N # Mn [2] BRAHMI VOWEL SIGN OLD TAMIL SHORT E..BRAHMI VOWEL SIGN OLD TAMIL SHORT O +11075 ; N # Lo BRAHMI LETTER OLD TAMIL LLA +1107F ; N # Mn BRAHMI NUMBER JOINER +11080..11081 ; N # Mn [2] KAITHI SIGN CANDRABINDU..KAITHI SIGN ANUSVARA +11082 ; N # Mc KAITHI SIGN VISARGA +11083..110AF ; N # Lo [45] KAITHI LETTER A..KAITHI LETTER HA +110B0..110B2 ; N # Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II +110B3..110B6 ; N # Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI +110B7..110B8 ; N # Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU +110B9..110BA ; N # Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA +110BB..110BC ; N # Po [2] KAITHI ABBREVIATION SIGN..KAITHI ENUMERATION SIGN +110BD ; N # Cf KAITHI NUMBER SIGN +110BE..110C1 ; N # Po [4] KAITHI SECTION MARK..KAITHI DOUBLE DANDA +110C2 ; N # Mn KAITHI VOWEL SIGN VOCALIC R +110CD ; N # Cf KAITHI NUMBER SIGN ABOVE +110D0..110E8 ; N # Lo [25] SORA SOMPENG LETTER SAH..SORA SOMPENG LETTER MAE +110F0..110F9 ; N # Nd [10] SORA SOMPENG DIGIT ZERO..SORA SOMPENG DIGIT NINE +11100..11102 ; N # Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA +11103..11126 ; N # Lo [36] CHAKMA LETTER AA..CHAKMA LETTER HAA +11127..1112B ; N # Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU +1112C ; N # Mc CHAKMA VOWEL SIGN E +1112D..11134 ; N # Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA +11136..1113F ; N # Nd [10] CHAKMA DIGIT ZERO..CHAKMA DIGIT NINE +11140..11143 ; N # Po [4] CHAKMA SECTION MARK..CHAKMA QUESTION MARK +11144 ; N # Lo CHAKMA LETTER LHAA +11145..11146 ; N # Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI +11147 ; N # Lo CHAKMA LETTER VAA +11150..11172 ; N # Lo [35] MAHAJANI LETTER A..MAHAJANI LETTER RRA +11173 ; N # Mn MAHAJANI SIGN NUKTA +11174..11175 ; N # Po [2] MAHAJANI ABBREVIATION SIGN..MAHAJANI SECTION MARK +11176 ; N # Lo MAHAJANI LIGATURE SHRI +11180..11181 ; N # Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA +11182 ; N # Mc SHARADA SIGN VISARGA +11183..111B2 ; N # Lo [48] SHARADA LETTER A..SHARADA LETTER HA +111B3..111B5 ; N # Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II +111B6..111BE ; N # Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O +111BF..111C0 ; N # Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA +111C1..111C4 ; N # Lo [4] SHARADA SIGN AVAGRAHA..SHARADA OM +111C5..111C8 ; N # Po [4] SHARADA DANDA..SHARADA SEPARATOR +111C9..111CC ; N # Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK +111CD ; N # Po SHARADA SUTRA MARK +111CE ; N # Mc SHARADA VOWEL SIGN PRISHTHAMATRA E +111CF ; N # Mn SHARADA SIGN INVERTED CANDRABINDU +111D0..111D9 ; N # Nd [10] SHARADA DIGIT ZERO..SHARADA DIGIT NINE +111DA ; N # Lo SHARADA EKAM +111DB ; N # Po SHARADA SIGN SIDDHAM +111DC ; N # Lo SHARADA HEADSTROKE +111DD..111DF ; N # Po [3] SHARADA CONTINUATION SIGN..SHARADA SECTION MARK-2 +111E1..111F4 ; N # No [20] SINHALA ARCHAIC DIGIT ONE..SINHALA ARCHAIC NUMBER ONE THOUSAND +11200..11211 ; N # Lo [18] KHOJKI LETTER A..KHOJKI LETTER JJA +11213..1122B ; N # Lo [25] KHOJKI LETTER NYA..KHOJKI LETTER LLA +1122C..1122E ; N # Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II +1122F..11231 ; N # Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI +11232..11233 ; N # Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU +11234 ; N # Mn KHOJKI SIGN ANUSVARA +11235 ; N # Mc KHOJKI SIGN VIRAMA +11236..11237 ; N # Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA +11238..1123D ; N # Po [6] KHOJKI DANDA..KHOJKI ABBREVIATION SIGN +1123E ; N # Mn KHOJKI SIGN SUKUN +1123F..11240 ; N # Lo [2] KHOJKI LETTER QA..KHOJKI LETTER SHORT I +11241 ; N # Mn KHOJKI VOWEL SIGN VOCALIC R +11280..11286 ; N # Lo [7] MULTANI LETTER A..MULTANI LETTER GA +11288 ; N # Lo MULTANI LETTER GHA +1128A..1128D ; N # Lo [4] MULTANI LETTER CA..MULTANI LETTER JJA +1128F..1129D ; N # Lo [15] MULTANI LETTER NYA..MULTANI LETTER BA +1129F..112A8 ; N # Lo [10] MULTANI LETTER BHA..MULTANI LETTER RHA +112A9 ; N # Po MULTANI SECTION MARK +112B0..112DE ; N # Lo [47] KHUDAWADI LETTER A..KHUDAWADI LETTER HA +112DF ; N # Mn KHUDAWADI SIGN ANUSVARA +112E0..112E2 ; N # Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II +112E3..112EA ; N # Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA +112F0..112F9 ; N # Nd [10] KHUDAWADI DIGIT ZERO..KHUDAWADI DIGIT NINE +11300..11301 ; N # Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU +11302..11303 ; N # Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA +11305..1130C ; N # Lo [8] GRANTHA LETTER A..GRANTHA LETTER VOCALIC L +1130F..11310 ; N # Lo [2] GRANTHA LETTER EE..GRANTHA LETTER AI +11313..11328 ; N # Lo [22] GRANTHA LETTER OO..GRANTHA LETTER NA +1132A..11330 ; N # Lo [7] GRANTHA LETTER PA..GRANTHA LETTER RA +11332..11333 ; N # Lo [2] GRANTHA LETTER LA..GRANTHA LETTER LLA +11335..11339 ; N # Lo [5] GRANTHA LETTER VA..GRANTHA LETTER HA +1133B..1133C ; N # Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA +1133D ; N # Lo GRANTHA SIGN AVAGRAHA +1133E..1133F ; N # Mc [2] GRANTHA VOWEL SIGN AA..GRANTHA VOWEL SIGN I +11340 ; N # Mn GRANTHA VOWEL SIGN II +11341..11344 ; N # Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR +11347..11348 ; N # Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI +1134B..1134D ; N # Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA +11350 ; N # Lo GRANTHA OM +11357 ; N # Mc GRANTHA AU LENGTH MARK +1135D..11361 ; N # Lo [5] GRANTHA SIGN PLUTA..GRANTHA LETTER VOCALIC LL +11362..11363 ; N # Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL +11366..1136C ; N # Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX +11370..11374 ; N # Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA +11400..11434 ; N # Lo [53] NEWA LETTER A..NEWA LETTER HA +11435..11437 ; N # Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II +11438..1143F ; N # Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI +11440..11441 ; N # Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU +11442..11444 ; N # Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA +11445 ; N # Mc NEWA SIGN VISARGA +11446 ; N # Mn NEWA SIGN NUKTA +11447..1144A ; N # Lo [4] NEWA SIGN AVAGRAHA..NEWA SIDDHI +1144B..1144F ; N # Po [5] NEWA DANDA..NEWA ABBREVIATION SIGN +11450..11459 ; N # Nd [10] NEWA DIGIT ZERO..NEWA DIGIT NINE +1145A..1145B ; N # Po [2] NEWA DOUBLE COMMA..NEWA PLACEHOLDER MARK +1145D ; N # Po NEWA INSERTION SIGN +1145E ; N # Mn NEWA SANDHI MARK +1145F..11461 ; N # Lo [3] NEWA LETTER VEDIC ANUSVARA..NEWA SIGN UPADHMANIYA +11480..114AF ; N # Lo [48] TIRHUTA ANJI..TIRHUTA LETTER HA +114B0..114B2 ; N # Mc [3] TIRHUTA VOWEL SIGN AA..TIRHUTA VOWEL SIGN II +114B3..114B8 ; N # Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL +114B9 ; N # Mc TIRHUTA VOWEL SIGN E +114BA ; N # Mn TIRHUTA VOWEL SIGN SHORT E +114BB..114BE ; N # Mc [4] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN AU +114BF..114C0 ; N # Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA +114C1 ; N # Mc TIRHUTA SIGN VISARGA +114C2..114C3 ; N # Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA +114C4..114C5 ; N # Lo [2] TIRHUTA SIGN AVAGRAHA..TIRHUTA GVANG +114C6 ; N # Po TIRHUTA ABBREVIATION SIGN +114C7 ; N # Lo TIRHUTA OM +114D0..114D9 ; N # Nd [10] TIRHUTA DIGIT ZERO..TIRHUTA DIGIT NINE +11580..115AE ; N # Lo [47] SIDDHAM LETTER A..SIDDHAM LETTER HA +115AF..115B1 ; N # Mc [3] SIDDHAM VOWEL SIGN AA..SIDDHAM VOWEL SIGN II +115B2..115B5 ; N # Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR +115B8..115BB ; N # Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU +115BC..115BD ; N # Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA +115BE ; N # Mc SIDDHAM SIGN VISARGA +115BF..115C0 ; N # Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA +115C1..115D7 ; N # Po [23] SIDDHAM SIGN SIDDHAM..SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES +115D8..115DB ; N # Lo [4] SIDDHAM LETTER THREE-CIRCLE ALTERNATE I..SIDDHAM LETTER ALTERNATE U +115DC..115DD ; N # Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU +11600..1162F ; N # Lo [48] MODI LETTER A..MODI LETTER LLA +11630..11632 ; N # Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II +11633..1163A ; N # Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI +1163B..1163C ; N # Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU +1163D ; N # Mn MODI SIGN ANUSVARA +1163E ; N # Mc MODI SIGN VISARGA +1163F..11640 ; N # Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA +11641..11643 ; N # Po [3] MODI DANDA..MODI ABBREVIATION SIGN +11644 ; N # Lo MODI SIGN HUVA +11650..11659 ; N # Nd [10] MODI DIGIT ZERO..MODI DIGIT NINE +11660..1166C ; N # Po [13] MONGOLIAN BIRGA WITH ORNAMENT..MONGOLIAN TURNED SWIRL BIRGA WITH DOUBLE ORNAMENT +11680..116AA ; N # Lo [43] TAKRI LETTER A..TAKRI LETTER RRA +116AB ; N # Mn TAKRI SIGN ANUSVARA +116AC ; N # Mc TAKRI SIGN VISARGA +116AD ; N # Mn TAKRI VOWEL SIGN AA +116AE..116AF ; N # Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II +116B0..116B5 ; N # Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU +116B6 ; N # Mc TAKRI SIGN VIRAMA +116B7 ; N # Mn TAKRI SIGN NUKTA +116B8 ; N # Lo TAKRI LETTER ARCHAIC KHA +116B9 ; N # Po TAKRI ABBREVIATION SIGN +116C0..116C9 ; N # Nd [10] TAKRI DIGIT ZERO..TAKRI DIGIT NINE +11700..1171A ; N # Lo [27] AHOM LETTER KA..AHOM LETTER ALTERNATE BA +1171D..1171F ; N # Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA +11720..11721 ; N # Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA +11722..11725 ; N # Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU +11726 ; N # Mc AHOM VOWEL SIGN E +11727..1172B ; N # Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER +11730..11739 ; N # Nd [10] AHOM DIGIT ZERO..AHOM DIGIT NINE +1173A..1173B ; N # No [2] AHOM NUMBER TEN..AHOM NUMBER TWENTY +1173C..1173E ; N # Po [3] AHOM SIGN SMALL SECTION..AHOM SIGN RULAI +1173F ; N # So AHOM SYMBOL VI +11740..11746 ; N # Lo [7] AHOM LETTER CA..AHOM LETTER LLA +11800..1182B ; N # Lo [44] DOGRA LETTER A..DOGRA LETTER RRA +1182C..1182E ; N # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II +1182F..11837 ; N # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA +11838 ; N # Mc DOGRA SIGN VISARGA +11839..1183A ; N # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA +1183B ; N # Po DOGRA ABBREVIATION SIGN +118A0..118DF ; N # L& [64] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI SMALL LETTER VIYO +118E0..118E9 ; N # Nd [10] WARANG CITI DIGIT ZERO..WARANG CITI DIGIT NINE +118EA..118F2 ; N # No [9] WARANG CITI NUMBER TEN..WARANG CITI NUMBER NINETY +118FF ; N # Lo WARANG CITI OM +11900..11906 ; N # Lo [7] DIVES AKURU LETTER A..DIVES AKURU LETTER E +11909 ; N # Lo DIVES AKURU LETTER O +1190C..11913 ; N # Lo [8] DIVES AKURU LETTER KA..DIVES AKURU LETTER JA +11915..11916 ; N # Lo [2] DIVES AKURU LETTER NYA..DIVES AKURU LETTER TTA +11918..1192F ; N # Lo [24] DIVES AKURU LETTER DDA..DIVES AKURU LETTER ZA +11930..11935 ; N # Mc [6] DIVES AKURU VOWEL SIGN AA..DIVES AKURU VOWEL SIGN E +11937..11938 ; N # Mc [2] DIVES AKURU VOWEL SIGN AI..DIVES AKURU VOWEL SIGN O +1193B..1193C ; N # Mn [2] DIVES AKURU SIGN ANUSVARA..DIVES AKURU SIGN CANDRABINDU +1193D ; N # Mc DIVES AKURU SIGN HALANTA +1193E ; N # Mn DIVES AKURU VIRAMA +1193F ; N # Lo DIVES AKURU PREFIXED NASAL SIGN +11940 ; N # Mc DIVES AKURU MEDIAL YA +11941 ; N # Lo DIVES AKURU INITIAL RA +11942 ; N # Mc DIVES AKURU MEDIAL RA +11943 ; N # Mn DIVES AKURU SIGN NUKTA +11944..11946 ; N # Po [3] DIVES AKURU DOUBLE DANDA..DIVES AKURU END OF TEXT MARK +11950..11959 ; N # Nd [10] DIVES AKURU DIGIT ZERO..DIVES AKURU DIGIT NINE +119A0..119A7 ; N # Lo [8] NANDINAGARI LETTER A..NANDINAGARI LETTER VOCALIC RR +119AA..119D0 ; N # Lo [39] NANDINAGARI LETTER E..NANDINAGARI LETTER RRA +119D1..119D3 ; N # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II +119D4..119D7 ; N # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR +119DA..119DB ; N # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI +119DC..119DF ; N # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA +119E0 ; N # Mn NANDINAGARI SIGN VIRAMA +119E1 ; N # Lo NANDINAGARI SIGN AVAGRAHA +119E2 ; N # Po NANDINAGARI SIGN SIDDHAM +119E3 ; N # Lo NANDINAGARI HEADSTROKE +119E4 ; N # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E +11A00 ; N # Lo ZANABAZAR SQUARE LETTER A +11A01..11A0A ; N # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK +11A0B..11A32 ; N # Lo [40] ZANABAZAR SQUARE LETTER KA..ZANABAZAR SQUARE LETTER KSSA +11A33..11A38 ; N # Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA +11A39 ; N # Mc ZANABAZAR SQUARE SIGN VISARGA +11A3A ; N # Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA +11A3B..11A3E ; N # Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA +11A3F..11A46 ; N # Po [8] ZANABAZAR SQUARE INITIAL HEAD MARK..ZANABAZAR SQUARE CLOSING DOUBLE-LINED HEAD MARK +11A47 ; N # Mn ZANABAZAR SQUARE SUBJOINER +11A50 ; N # Lo SOYOMBO LETTER A +11A51..11A56 ; N # Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE +11A57..11A58 ; N # Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU +11A59..11A5B ; N # Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK +11A5C..11A89 ; N # Lo [46] SOYOMBO LETTER KA..SOYOMBO CLUSTER-INITIAL LETTER SA +11A8A..11A96 ; N # Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA +11A97 ; N # Mc SOYOMBO SIGN VISARGA +11A98..11A99 ; N # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER +11A9A..11A9C ; N # Po [3] SOYOMBO MARK TSHEG..SOYOMBO MARK DOUBLE SHAD +11A9D ; N # Lo SOYOMBO MARK PLUTA +11A9E..11AA2 ; N # Po [5] SOYOMBO HEAD MARK WITH MOON AND SUN AND TRIPLE FLAME..SOYOMBO TERMINAL MARK-2 +11AB0..11ABF ; N # Lo [16] CANADIAN SYLLABICS NATTILIK HI..CANADIAN SYLLABICS SPA +11AC0..11AF8 ; N # Lo [57] PAU CIN HAU LETTER PA..PAU CIN HAU GLOTTAL STOP FINAL +11B00..11B09 ; N # Po [10] DEVANAGARI HEAD MARK..DEVANAGARI SIGN MINDU +11C00..11C08 ; N # Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L +11C0A..11C2E ; N # Lo [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA +11C2F ; N # Mc BHAIKSUKI VOWEL SIGN AA +11C30..11C36 ; N # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L +11C38..11C3D ; N # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA +11C3E ; N # Mc BHAIKSUKI SIGN VISARGA +11C3F ; N # Mn BHAIKSUKI SIGN VIRAMA +11C40 ; N # Lo BHAIKSUKI SIGN AVAGRAHA +11C41..11C45 ; N # Po [5] BHAIKSUKI DANDA..BHAIKSUKI GAP FILLER-2 +11C50..11C59 ; N # Nd [10] BHAIKSUKI DIGIT ZERO..BHAIKSUKI DIGIT NINE +11C5A..11C6C ; N # No [19] BHAIKSUKI NUMBER ONE..BHAIKSUKI HUNDREDS UNIT MARK +11C70..11C71 ; N # Po [2] MARCHEN HEAD MARK..MARCHEN MARK SHAD +11C72..11C8F ; N # Lo [30] MARCHEN LETTER KA..MARCHEN LETTER A +11C92..11CA7 ; N # Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA +11CA9 ; N # Mc MARCHEN SUBJOINED LETTER YA +11CAA..11CB0 ; N # Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA +11CB1 ; N # Mc MARCHEN VOWEL SIGN I +11CB2..11CB3 ; N # Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E +11CB4 ; N # Mc MARCHEN VOWEL SIGN O +11CB5..11CB6 ; N # Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU +11D00..11D06 ; N # Lo [7] MASARAM GONDI LETTER A..MASARAM GONDI LETTER E +11D08..11D09 ; N # Lo [2] MASARAM GONDI LETTER AI..MASARAM GONDI LETTER O +11D0B..11D30 ; N # Lo [38] MASARAM GONDI LETTER AU..MASARAM GONDI LETTER TRA +11D31..11D36 ; N # Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R +11D3A ; N # Mn MASARAM GONDI VOWEL SIGN E +11D3C..11D3D ; N # Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O +11D3F..11D45 ; N # Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA +11D46 ; N # Lo MASARAM GONDI REPHA +11D47 ; N # Mn MASARAM GONDI RA-KARA +11D50..11D59 ; N # Nd [10] MASARAM GONDI DIGIT ZERO..MASARAM GONDI DIGIT NINE +11D60..11D65 ; N # Lo [6] GUNJALA GONDI LETTER A..GUNJALA GONDI LETTER UU +11D67..11D68 ; N # Lo [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI +11D6A..11D89 ; N # Lo [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA +11D8A..11D8E ; N # Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU +11D90..11D91 ; N # Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI +11D93..11D94 ; N # Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU +11D95 ; N # Mn GUNJALA GONDI SIGN ANUSVARA +11D96 ; N # Mc GUNJALA GONDI SIGN VISARGA +11D97 ; N # Mn GUNJALA GONDI VIRAMA +11D98 ; N # Lo GUNJALA GONDI OM +11DA0..11DA9 ; N # Nd [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE +11EE0..11EF2 ; N # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA +11EF3..11EF4 ; N # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U +11EF5..11EF6 ; N # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O +11EF7..11EF8 ; N # Po [2] MAKASAR PASSIMBANG..MAKASAR END OF SECTION +11F00..11F01 ; N # Mn [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA +11F02 ; N # Lo KAWI SIGN REPHA +11F03 ; N # Mc KAWI SIGN VISARGA +11F04..11F10 ; N # Lo [13] KAWI LETTER A..KAWI LETTER O +11F12..11F33 ; N # Lo [34] KAWI LETTER KA..KAWI LETTER JNYA +11F34..11F35 ; N # Mc [2] KAWI VOWEL SIGN AA..KAWI VOWEL SIGN ALTERNATE AA +11F36..11F3A ; N # Mn [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R +11F3E..11F3F ; N # Mc [2] KAWI VOWEL SIGN E..KAWI VOWEL SIGN AI +11F40 ; N # Mn KAWI VOWEL SIGN EU +11F41 ; N # Mc KAWI SIGN KILLER +11F42 ; N # Mn KAWI CONJOINER +11F43..11F4F ; N # Po [13] KAWI DANDA..KAWI PUNCTUATION CLOSING SPIRAL +11F50..11F59 ; N # Nd [10] KAWI DIGIT ZERO..KAWI DIGIT NINE +11FB0 ; N # Lo LISU LETTER YHA +11FC0..11FD4 ; N # No [21] TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH..TAMIL FRACTION DOWNSCALING FACTOR KIIZH +11FD5..11FDC ; N # So [8] TAMIL SIGN NEL..TAMIL SIGN MUKKURUNI +11FDD..11FE0 ; N # Sc [4] TAMIL SIGN KAACU..TAMIL SIGN VARAAKAN +11FE1..11FF1 ; N # So [17] TAMIL SIGN PAARAM..TAMIL SIGN VAKAIYARAA +11FFF ; N # Po TAMIL PUNCTUATION END OF TEXT +12000..12399 ; N # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U +12400..1246E ; N # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM +12470..12474 ; N # Po [5] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON +12480..12543 ; N # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU +12F90..12FF0 ; N # Lo [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114 +12FF1..12FF2 ; N # Po [2] CYPRO-MINOAN SIGN CM301..CYPRO-MINOAN SIGN CM302 +13000..1342F ; N # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D +13430..1343F ; N # Cf [16] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE +13440 ; N # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY +13441..13446 ; N # Lo [6] EGYPTIAN HIEROGLYPH FULL BLANK..EGYPTIAN HIEROGLYPH WIDE LOST SIGN +13447..13455 ; N # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED +14400..14646 ; N # Lo [583] ANATOLIAN HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530 +16800..16A38 ; N # Lo [569] BAMUM LETTER PHASE-A NGKUE MFON..BAMUM LETTER PHASE-F VUEQ +16A40..16A5E ; N # Lo [31] MRO LETTER TA..MRO LETTER TEK +16A60..16A69 ; N # Nd [10] MRO DIGIT ZERO..MRO DIGIT NINE +16A6E..16A6F ; N # Po [2] MRO DANDA..MRO DOUBLE DANDA +16A70..16ABE ; N # Lo [79] TANGSA LETTER OZ..TANGSA LETTER ZA +16AC0..16AC9 ; N # Nd [10] TANGSA DIGIT ZERO..TANGSA DIGIT NINE +16AD0..16AED ; N # Lo [30] BASSA VAH LETTER ENNI..BASSA VAH LETTER I +16AF0..16AF4 ; N # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE +16AF5 ; N # Po BASSA VAH FULL STOP +16B00..16B2F ; N # Lo [48] PAHAWH HMONG VOWEL KEEB..PAHAWH HMONG CONSONANT CAU +16B30..16B36 ; N # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM +16B37..16B3B ; N # Po [5] PAHAWH HMONG SIGN VOS THOM..PAHAWH HMONG SIGN VOS FEEM +16B3C..16B3F ; N # So [4] PAHAWH HMONG SIGN XYEEM NTXIV..PAHAWH HMONG SIGN XYEEM FAIB +16B40..16B43 ; N # Lm [4] PAHAWH HMONG SIGN VOS SEEV..PAHAWH HMONG SIGN IB YAM +16B44 ; N # Po PAHAWH HMONG SIGN XAUS +16B45 ; N # So PAHAWH HMONG SIGN CIM TSOV ROG +16B50..16B59 ; N # Nd [10] PAHAWH HMONG DIGIT ZERO..PAHAWH HMONG DIGIT NINE +16B5B..16B61 ; N # No [7] PAHAWH HMONG NUMBER TENS..PAHAWH HMONG NUMBER TRILLIONS +16B63..16B77 ; N # Lo [21] PAHAWH HMONG SIGN VOS LUB..PAHAWH HMONG SIGN CIM NRES TOS +16B7D..16B8F ; N # Lo [19] PAHAWH HMONG CLAN SIGN TSHEEJ..PAHAWH HMONG CLAN SIGN VWJ +16E40..16E7F ; N # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16E80..16E96 ; N # No [23] MEDEFAIDRIN DIGIT ZERO..MEDEFAIDRIN DIGIT THREE ALTERNATE FORM +16E97..16E9A ; N # Po [4] MEDEFAIDRIN COMMA..MEDEFAIDRIN EXCLAMATION OH +16F00..16F4A ; N # Lo [75] MIAO LETTER PA..MIAO LETTER RTE +16F4F ; N # Mn MIAO SIGN CONSONANT MODIFIER BAR +16F50 ; N # Lo MIAO LETTER NASALIZATION +16F51..16F87 ; N # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI +16F8F..16F92 ; N # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW +16F93..16F9F ; N # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8 +16FE0..16FE1 ; W # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK +16FE2 ; W # Po OLD CHINESE HOOK MARK +16FE3 ; W # Lm OLD CHINESE ITERATION MARK +16FE4 ; W # Mn KHITAN SMALL SCRIPT FILLER +16FF0..16FF1 ; W # Mc [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY +17000..187F7 ; W # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7 +18800..18AFF ; W # Lo [768] TANGUT COMPONENT-001..TANGUT COMPONENT-768 +18B00..18CD5 ; W # Lo [470] KHITAN SMALL SCRIPT CHARACTER-18B00..KHITAN SMALL SCRIPT CHARACTER-18CD5 +18D00..18D08 ; W # Lo [9] TANGUT IDEOGRAPH-18D00..TANGUT IDEOGRAPH-18D08 +1AFF0..1AFF3 ; W # Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5 +1AFF5..1AFFB ; W # Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5 +1AFFD..1AFFE ; W # Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8 +1B000..1B0FF ; W # Lo [256] KATAKANA LETTER ARCHAIC E..HENTAIGANA LETTER RE-2 +1B100..1B122 ; W # Lo [35] HENTAIGANA LETTER RE-3..KATAKANA LETTER ARCHAIC WU +1B132 ; W # Lo HIRAGANA LETTER SMALL KO +1B150..1B152 ; W # Lo [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO +1B155 ; W # Lo KATAKANA LETTER SMALL KO +1B164..1B167 ; W # Lo [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N +1B170..1B2FB ; W # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB +1BC00..1BC6A ; N # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M +1BC70..1BC7C ; N # Lo [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK +1BC80..1BC88 ; N # Lo [9] DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL +1BC90..1BC99 ; N # Lo [10] DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW +1BC9C ; N # So DUPLOYAN SIGN O WITH CROSS +1BC9D..1BC9E ; N # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK +1BC9F ; N # Po DUPLOYAN PUNCTUATION CHINOOK FULL STOP +1BCA0..1BCA3 ; N # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP +1CF00..1CF2D ; N # Mn [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT +1CF30..1CF46 ; N # Mn [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG +1CF50..1CFC3 ; N # So [116] ZNAMENNY NEUME KRYUK..ZNAMENNY NEUME PAUK +1D000..1D0F5 ; N # So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO +1D100..1D126 ; N # So [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2 +1D129..1D164 ; N # So [60] MUSICAL SYMBOL MULTIPLE MEASURE REST..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE +1D165..1D166 ; N # Mc [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM +1D167..1D169 ; N # Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 +1D16A..1D16C ; N # So [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3 +1D16D..1D172 ; N # Mc [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5 +1D173..1D17A ; N # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE +1D17B..1D182 ; N # Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE +1D183..1D184 ; N # So [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN +1D185..1D18B ; N # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE +1D18C..1D1A9 ; N # So [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH +1D1AA..1D1AD ; N # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO +1D1AE..1D1EA ; N # So [61] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL KORON +1D200..1D241 ; N # So [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54 +1D242..1D244 ; N # Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME +1D245 ; N # So GREEK MUSICAL LEIMMA +1D2C0..1D2D3 ; N # No [20] KAKTOVIK NUMERAL ZERO..KAKTOVIK NUMERAL NINETEEN +1D2E0..1D2F3 ; N # No [20] MAYAN NUMERAL ZERO..MAYAN NUMERAL NINETEEN +1D300..1D356 ; N # So [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING +1D360..1D378 ; N # No [25] COUNTING ROD UNIT DIGIT ONE..TALLY MARK FIVE +1D400..1D454 ; N # L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G +1D456..1D49C ; N # L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A +1D49E..1D49F ; N # Lu [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D +1D4A2 ; N # Lu MATHEMATICAL SCRIPT CAPITAL G +1D4A5..1D4A6 ; N # Lu [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K +1D4A9..1D4AC ; N # Lu [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q +1D4AE..1D4B9 ; N # L& [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D +1D4BB ; N # Ll MATHEMATICAL SCRIPT SMALL F +1D4BD..1D4C3 ; N # Ll [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N +1D4C5..1D505 ; N # L& [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B +1D507..1D50A ; N # Lu [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G +1D50D..1D514 ; N # Lu [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q +1D516..1D51C ; N # Lu [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y +1D51E..1D539 ; N # L& [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B +1D53B..1D53E ; N # Lu [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G +1D540..1D544 ; N # Lu [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M +1D546 ; N # Lu MATHEMATICAL DOUBLE-STRUCK CAPITAL O +1D54A..1D550 ; N # Lu [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y +1D552..1D6A5 ; N # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J +1D6A8..1D6C0 ; N # Lu [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA +1D6C1 ; N # Sm MATHEMATICAL BOLD NABLA +1D6C2..1D6DA ; N # Ll [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA +1D6DB ; N # Sm MATHEMATICAL BOLD PARTIAL DIFFERENTIAL +1D6DC..1D6FA ; N # L& [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA +1D6FB ; N # Sm MATHEMATICAL ITALIC NABLA +1D6FC..1D714 ; N # Ll [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA +1D715 ; N # Sm MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL +1D716..1D734 ; N # L& [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA +1D735 ; N # Sm MATHEMATICAL BOLD ITALIC NABLA +1D736..1D74E ; N # Ll [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA +1D74F ; N # Sm MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL +1D750..1D76E ; N # L& [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA +1D76F ; N # Sm MATHEMATICAL SANS-SERIF BOLD NABLA +1D770..1D788 ; N # Ll [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA +1D789 ; N # Sm MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL +1D78A..1D7A8 ; N # L& [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA +1D7A9 ; N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA +1D7AA..1D7C2 ; N # Ll [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA +1D7C3 ; N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL +1D7C4..1D7CB ; N # L& [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA +1D7CE..1D7FF ; N # Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE +1D800..1D9FF ; N # So [512] SIGNWRITING HAND-FIST INDEX..SIGNWRITING HEAD +1DA00..1DA36 ; N # Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN +1DA37..1DA3A ; N # So [4] SIGNWRITING AIR BLOW SMALL ROTATIONS..SIGNWRITING BREATH EXHALE +1DA3B..1DA6C ; N # Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT +1DA6D..1DA74 ; N # So [8] SIGNWRITING SHOULDER HIP SPINE..SIGNWRITING TORSO-FLOORPLANE TWISTING +1DA75 ; N # Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS +1DA76..1DA83 ; N # So [14] SIGNWRITING LIMB COMBINATION..SIGNWRITING LOCATION DEPTH +1DA84 ; N # Mn SIGNWRITING LOCATION HEAD NECK +1DA85..1DA86 ; N # So [2] SIGNWRITING LOCATION TORSO..SIGNWRITING LOCATION LIMBS DIGITS +1DA87..1DA8B ; N # Po [5] SIGNWRITING COMMA..SIGNWRITING PARENTHESIS +1DA9B..1DA9F ; N # Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6 +1DAA1..1DAAF ; N # Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16 +1DF00..1DF09 ; N # Ll [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK +1DF0A ; N # Lo LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK +1DF0B..1DF1E ; N # Ll [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL +1DF25..1DF2A ; N # Ll [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK +1E000..1E006 ; N # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE +1E008..1E018 ; N # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU +1E01B..1E021 ; N # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI +1E023..1E024 ; N # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS +1E026..1E02A ; N # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA +1E030..1E06D ; N # Lm [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE +1E08F ; N # Mn COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I +1E100..1E12C ; N # Lo [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W +1E130..1E136 ; N # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D +1E137..1E13D ; N # Lm [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER +1E140..1E149 ; N # Nd [10] NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE +1E14E ; N # Lo NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ +1E14F ; N # So NYIAKENG PUACHUE HMONG CIRCLED CA +1E290..1E2AD ; N # Lo [30] TOTO LETTER PA..TOTO LETTER A +1E2AE ; N # Mn TOTO SIGN RISING TONE +1E2C0..1E2EB ; N # Lo [44] WANCHO LETTER AA..WANCHO LETTER YIH +1E2EC..1E2EF ; N # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI +1E2F0..1E2F9 ; N # Nd [10] WANCHO DIGIT ZERO..WANCHO DIGIT NINE +1E2FF ; N # Sc WANCHO NGUN SIGN +1E4D0..1E4EA ; N # Lo [27] NAG MUNDARI LETTER O..NAG MUNDARI LETTER ELL +1E4EB ; N # Lm NAG MUNDARI SIGN OJOD +1E4EC..1E4EF ; N # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH +1E4F0..1E4F9 ; N # Nd [10] NAG MUNDARI DIGIT ZERO..NAG MUNDARI DIGIT NINE +1E7E0..1E7E6 ; N # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO +1E7E8..1E7EB ; N # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE +1E7ED..1E7EE ; N # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE +1E7F0..1E7FE ; N # Lo [15] ETHIOPIC SYLLABLE GURAGE QWI..ETHIOPIC SYLLABLE GURAGE PWEE +1E800..1E8C4 ; N # Lo [197] MENDE KIKAKUI SYLLABLE M001 KI..MENDE KIKAKUI SYLLABLE M060 NYON +1E8C7..1E8CF ; N # No [9] MENDE KIKAKUI DIGIT ONE..MENDE KIKAKUI DIGIT NINE +1E8D0..1E8D6 ; N # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS +1E900..1E943 ; N # L& [68] ADLAM CAPITAL LETTER ALIF..ADLAM SMALL LETTER SHA +1E944..1E94A ; N # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA +1E94B ; N # Lm ADLAM NASALIZATION MARK +1E950..1E959 ; N # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE +1E95E..1E95F ; N # Po [2] ADLAM INITIAL EXCLAMATION MARK..ADLAM INITIAL QUESTION MARK +1EC71..1ECAB ; N # No [59] INDIC SIYAQ NUMBER ONE..INDIC SIYAQ NUMBER PREFIXED NINE +1ECAC ; N # So INDIC SIYAQ PLACEHOLDER +1ECAD..1ECAF ; N # No [3] INDIC SIYAQ FRACTION ONE QUARTER..INDIC SIYAQ FRACTION THREE QUARTERS +1ECB0 ; N # Sc INDIC SIYAQ RUPEE MARK +1ECB1..1ECB4 ; N # No [4] INDIC SIYAQ NUMBER ALTERNATE ONE..INDIC SIYAQ ALTERNATE LAKH MARK +1ED01..1ED2D ; N # No [45] OTTOMAN SIYAQ NUMBER ONE..OTTOMAN SIYAQ NUMBER NINETY THOUSAND +1ED2E ; N # So OTTOMAN SIYAQ MARRATAN +1ED2F..1ED3D ; N # No [15] OTTOMAN SIYAQ ALTERNATE NUMBER TWO..OTTOMAN SIYAQ FRACTION ONE SIXTH +1EE00..1EE03 ; N # Lo [4] ARABIC MATHEMATICAL ALEF..ARABIC MATHEMATICAL DAL +1EE05..1EE1F ; N # Lo [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL DOTLESS QAF +1EE21..1EE22 ; N # Lo [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC MATHEMATICAL INITIAL JEEM +1EE24 ; N # Lo ARABIC MATHEMATICAL INITIAL HEH +1EE27 ; N # Lo ARABIC MATHEMATICAL INITIAL HAH +1EE29..1EE32 ; N # Lo [10] ARABIC MATHEMATICAL INITIAL YEH..ARABIC MATHEMATICAL INITIAL QAF +1EE34..1EE37 ; N # Lo [4] ARABIC MATHEMATICAL INITIAL SHEEN..ARABIC MATHEMATICAL INITIAL KHAH +1EE39 ; N # Lo ARABIC MATHEMATICAL INITIAL DAD +1EE3B ; N # Lo ARABIC MATHEMATICAL INITIAL GHAIN +1EE42 ; N # Lo ARABIC MATHEMATICAL TAILED JEEM +1EE47 ; N # Lo ARABIC MATHEMATICAL TAILED HAH +1EE49 ; N # Lo ARABIC MATHEMATICAL TAILED YEH +1EE4B ; N # Lo ARABIC MATHEMATICAL TAILED LAM +1EE4D..1EE4F ; N # Lo [3] ARABIC MATHEMATICAL TAILED NOON..ARABIC MATHEMATICAL TAILED AIN +1EE51..1EE52 ; N # Lo [2] ARABIC MATHEMATICAL TAILED SAD..ARABIC MATHEMATICAL TAILED QAF +1EE54 ; N # Lo ARABIC MATHEMATICAL TAILED SHEEN +1EE57 ; N # Lo ARABIC MATHEMATICAL TAILED KHAH +1EE59 ; N # Lo ARABIC MATHEMATICAL TAILED DAD +1EE5B ; N # Lo ARABIC MATHEMATICAL TAILED GHAIN +1EE5D ; N # Lo ARABIC MATHEMATICAL TAILED DOTLESS NOON +1EE5F ; N # Lo ARABIC MATHEMATICAL TAILED DOTLESS QAF +1EE61..1EE62 ; N # Lo [2] ARABIC MATHEMATICAL STRETCHED BEH..ARABIC MATHEMATICAL STRETCHED JEEM +1EE64 ; N # Lo ARABIC MATHEMATICAL STRETCHED HEH +1EE67..1EE6A ; N # Lo [4] ARABIC MATHEMATICAL STRETCHED HAH..ARABIC MATHEMATICAL STRETCHED KAF +1EE6C..1EE72 ; N # Lo [7] ARABIC MATHEMATICAL STRETCHED MEEM..ARABIC MATHEMATICAL STRETCHED QAF +1EE74..1EE77 ; N # Lo [4] ARABIC MATHEMATICAL STRETCHED SHEEN..ARABIC MATHEMATICAL STRETCHED KHAH +1EE79..1EE7C ; N # Lo [4] ARABIC MATHEMATICAL STRETCHED DAD..ARABIC MATHEMATICAL STRETCHED DOTLESS BEH +1EE7E ; N # Lo ARABIC MATHEMATICAL STRETCHED DOTLESS FEH +1EE80..1EE89 ; N # Lo [10] ARABIC MATHEMATICAL LOOPED ALEF..ARABIC MATHEMATICAL LOOPED YEH +1EE8B..1EE9B ; N # Lo [17] ARABIC MATHEMATICAL LOOPED LAM..ARABIC MATHEMATICAL LOOPED GHAIN +1EEA1..1EEA3 ; N # Lo [3] ARABIC MATHEMATICAL DOUBLE-STRUCK BEH..ARABIC MATHEMATICAL DOUBLE-STRUCK DAL +1EEA5..1EEA9 ; N # Lo [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH +1EEAB..1EEBB ; N # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN +1EEF0..1EEF1 ; N # Sm [2] ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL +1F000..1F003 ; N # So [4] MAHJONG TILE EAST WIND..MAHJONG TILE NORTH WIND +1F004 ; W # So MAHJONG TILE RED DRAGON +1F005..1F02B ; N # So [39] MAHJONG TILE GREEN DRAGON..MAHJONG TILE BACK +1F030..1F093 ; N # So [100] DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06 +1F0A0..1F0AE ; N # So [15] PLAYING CARD BACK..PLAYING CARD KING OF SPADES +1F0B1..1F0BF ; N # So [15] PLAYING CARD ACE OF HEARTS..PLAYING CARD RED JOKER +1F0C1..1F0CE ; N # So [14] PLAYING CARD ACE OF DIAMONDS..PLAYING CARD KING OF DIAMONDS +1F0CF ; W # So PLAYING CARD BLACK JOKER +1F0D1..1F0F5 ; N # So [37] PLAYING CARD ACE OF CLUBS..PLAYING CARD TRUMP-21 +1F100..1F10A ; A # No [11] DIGIT ZERO FULL STOP..DIGIT NINE COMMA +1F10B..1F10C ; N # No [2] DINGBAT CIRCLED SANS-SERIF DIGIT ZERO..DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ZERO +1F10D..1F10F ; N # So [3] CIRCLED ZERO WITH SLASH..CIRCLED DOLLAR SIGN WITH OVERLAID BACKSLASH +1F110..1F12D ; A # So [30] PARENTHESIZED LATIN CAPITAL LETTER A..CIRCLED CD +1F12E..1F12F ; N # So [2] CIRCLED WZ..COPYLEFT SYMBOL +1F130..1F169 ; A # So [58] SQUARED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z +1F16A..1F16F ; N # So [6] RAISED MC SIGN..CIRCLED HUMAN FIGURE +1F170..1F18D ; A # So [30] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED SA +1F18E ; W # So NEGATIVE SQUARED AB +1F18F..1F190 ; A # So [2] NEGATIVE SQUARED WC..SQUARE DJ +1F191..1F19A ; W # So [10] SQUARED CL..SQUARED VS +1F19B..1F1AC ; A # So [18] SQUARED THREE D..SQUARED VOD +1F1AD ; N # So MASK WORK SYMBOL +1F1E6..1F1FF ; N # So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z +1F200..1F202 ; W # So [3] SQUARE HIRAGANA HOKA..SQUARED KATAKANA SA +1F210..1F23B ; W # So [44] SQUARED CJK UNIFIED IDEOGRAPH-624B..SQUARED CJK UNIFIED IDEOGRAPH-914D +1F240..1F248 ; W # So [9] TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C..TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557 +1F250..1F251 ; W # So [2] CIRCLED IDEOGRAPH ADVANTAGE..CIRCLED IDEOGRAPH ACCEPT +1F260..1F265 ; W # So [6] ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI +1F300..1F320 ; W # So [33] CYCLONE..SHOOTING STAR +1F321..1F32C ; N # So [12] THERMOMETER..WIND BLOWING FACE +1F32D..1F335 ; W # So [9] HOT DOG..CACTUS +1F336 ; N # So HOT PEPPER +1F337..1F37C ; W # So [70] TULIP..BABY BOTTLE +1F37D ; N # So FORK AND KNIFE WITH PLATE +1F37E..1F393 ; W # So [22] BOTTLE WITH POPPING CORK..GRADUATION CAP +1F394..1F39F ; N # So [12] HEART WITH TIP ON THE LEFT..ADMISSION TICKETS +1F3A0..1F3CA ; W # So [43] CAROUSEL HORSE..SWIMMER +1F3CB..1F3CE ; N # So [4] WEIGHT LIFTER..RACING CAR +1F3CF..1F3D3 ; W # So [5] CRICKET BAT AND BALL..TABLE TENNIS PADDLE AND BALL +1F3D4..1F3DF ; N # So [12] SNOW CAPPED MOUNTAIN..STADIUM +1F3E0..1F3F0 ; W # So [17] HOUSE BUILDING..EUROPEAN CASTLE +1F3F1..1F3F3 ; N # So [3] WHITE PENNANT..WAVING WHITE FLAG +1F3F4 ; W # So WAVING BLACK FLAG +1F3F5..1F3F7 ; N # So [3] ROSETTE..LABEL +1F3F8..1F3FA ; W # So [3] BADMINTON RACQUET AND SHUTTLECOCK..AMPHORA +1F3FB..1F3FF ; W # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 +1F400..1F43E ; W # So [63] RAT..PAW PRINTS +1F43F ; N # So CHIPMUNK +1F440 ; W # So EYES +1F441 ; N # So EYE +1F442..1F4FC ; W # So [187] EAR..VIDEOCASSETTE +1F4FD..1F4FE ; N # So [2] FILM PROJECTOR..PORTABLE STEREO +1F4FF..1F53D ; W # So [63] PRAYER BEADS..DOWN-POINTING SMALL RED TRIANGLE +1F53E..1F54A ; N # So [13] LOWER RIGHT SHADOWED WHITE CIRCLE..DOVE OF PEACE +1F54B..1F54E ; W # So [4] KAABA..MENORAH WITH NINE BRANCHES +1F54F ; N # So BOWL OF HYGIEIA +1F550..1F567 ; W # So [24] CLOCK FACE ONE OCLOCK..CLOCK FACE TWELVE-THIRTY +1F568..1F579 ; N # So [18] RIGHT SPEAKER..JOYSTICK +1F57A ; W # So MAN DANCING +1F57B..1F594 ; N # So [26] LEFT HAND TELEPHONE RECEIVER..REVERSED VICTORY HAND +1F595..1F596 ; W # So [2] REVERSED HAND WITH MIDDLE FINGER EXTENDED..RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS +1F597..1F5A3 ; N # So [13] WHITE DOWN POINTING LEFT HAND INDEX..BLACK DOWN POINTING BACKHAND INDEX +1F5A4 ; W # So BLACK HEART +1F5A5..1F5FA ; N # So [86] DESKTOP COMPUTER..WORLD MAP +1F5FB..1F5FF ; W # So [5] MOUNT FUJI..MOYAI +1F600..1F64F ; W # So [80] GRINNING FACE..PERSON WITH FOLDED HANDS +1F650..1F67F ; N # So [48] NORTH WEST POINTING LEAF..REVERSE CHECKER BOARD +1F680..1F6C5 ; W # So [70] ROCKET..LEFT LUGGAGE +1F6C6..1F6CB ; N # So [6] TRIANGLE WITH ROUNDED CORNERS..COUCH AND LAMP +1F6CC ; W # So SLEEPING ACCOMMODATION +1F6CD..1F6CF ; N # So [3] SHOPPING BAGS..BED +1F6D0..1F6D2 ; W # So [3] PLACE OF WORSHIP..SHOPPING TROLLEY +1F6D3..1F6D4 ; N # So [2] STUPA..PAGODA +1F6D5..1F6D7 ; W # So [3] HINDU TEMPLE..ELEVATOR +1F6DC..1F6DF ; W # So [4] WIRELESS..RING BUOY +1F6E0..1F6EA ; N # So [11] HAMMER AND WRENCH..NORTHEAST-POINTING AIRPLANE +1F6EB..1F6EC ; W # So [2] AIRPLANE DEPARTURE..AIRPLANE ARRIVING +1F6F0..1F6F3 ; N # So [4] SATELLITE..PASSENGER SHIP +1F6F4..1F6FC ; W # So [9] SCOOTER..ROLLER SKATE +1F700..1F776 ; N # So [119] ALCHEMICAL SYMBOL FOR QUINTESSENCE..LUNAR ECLIPSE +1F77B..1F77F ; N # So [5] HAUMEA..ORCUS +1F780..1F7D9 ; N # So [90] BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE..NINE POINTED WHITE STAR +1F7E0..1F7EB ; W # So [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE +1F7F0 ; W # So HEAVY EQUALS SIGN +1F800..1F80B ; N # So [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD +1F810..1F847 ; N # So [56] LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW +1F850..1F859 ; N # So [10] LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW +1F860..1F887 ; N # So [40] WIDE-HEADED LEFTWARDS LIGHT BARB ARROW..WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW +1F890..1F8AD ; N # So [30] LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS +1F8B0..1F8B1 ; N # So [2] ARROW POINTING UPWARDS THEN NORTH WEST..ARROW POINTING RIGHTWARDS THEN CURVING SOUTH WEST +1F900..1F90B ; N # So [12] CIRCLED CROSS FORMEE WITH FOUR DOTS..DOWNWARD FACING NOTCHED HOOK WITH DOT +1F90C..1F93A ; W # So [47] PINCHED FINGERS..FENCER +1F93B ; N # So MODERN PENTATHLON +1F93C..1F945 ; W # So [10] WRESTLERS..GOAL NET +1F946 ; N # So RIFLE +1F947..1F9FF ; W # So [185] FIRST PLACE MEDAL..NAZAR AMULET +1FA00..1FA53 ; N # So [84] NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP +1FA60..1FA6D ; N # So [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER +1FA70..1FA7C ; W # So [13] BALLET SHOES..CRUTCH +1FA80..1FA88 ; W # So [9] YO-YO..FLUTE +1FA90..1FABD ; W # So [46] RINGED PLANET..WING +1FABF..1FAC5 ; W # So [7] GOOSE..PERSON WITH CROWN +1FACE..1FADB ; W # So [14] MOOSE..PEA POD +1FAE0..1FAE8 ; W # So [9] MELTING FACE..SHAKING FACE +1FAF0..1FAF8 ; W # So [9] HAND WITH INDEX FINGER AND THUMB CROSSED..RIGHTWARDS PUSHING HAND +1FB00..1FB92 ; N # So [147] BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK +1FB94..1FBCA ; N # So [55] LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..WHITE UP-POINTING CHEVRON +1FBF0..1FBF9 ; N # Nd [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE +20000..2A6DF ; W # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF +2A6E0..2A6FF ; W # Cn [32] <reserved-2A6E0>..<reserved-2A6FF> +2A700..2B739 ; W # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739 +2B73A..2B73F ; W # Cn [6] <reserved-2B73A>..<reserved-2B73F> +2B740..2B81D ; W # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D +2B81E..2B81F ; W # Cn [2] <reserved-2B81E>..<reserved-2B81F> +2B820..2CEA1 ; W # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 +2CEA2..2CEAF ; W # Cn [14] <reserved-2CEA2>..<reserved-2CEAF> +2CEB0..2EBE0 ; W # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0 +2EBE1..2EBEF ; W # Cn [15] <reserved-2EBE1>..<reserved-2EBEF> +2EBF0..2EE5D ; W # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D +2EE5E..2F7FF ; W # Cn [2466] <reserved-2EE5E>..<reserved-2F7FF> +2F800..2FA1D ; W # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D +2FA1E..2FA1F ; W # Cn [2] <reserved-2FA1E>..<reserved-2FA1F> +2FA20..2FFFD ; W # Cn [1502] <reserved-2FA20>..<reserved-2FFFD> +30000..3134A ; W # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A +3134B..3134F ; W # Cn [5] <reserved-3134B>..<reserved-3134F> +31350..323AF ; W # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF +323B0..3FFFD ; W # Cn [56398] <reserved-323B0>..<reserved-3FFFD> +E0001 ; N # Cf LANGUAGE TAG +E0020..E007F ; N # Cf [96] TAG SPACE..CANCEL TAG +E0100..E01EF ; A # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 +F0000..FFFFD ; A # Co [65534] <private-use-F0000>..<private-use-FFFFD> +100000..10FFFD ; A # Co [65534] <private-use-100000>..<private-use-10FFFD> # EOF diff --git a/src/unicode/UnicodeData.txt b/src/unicode/UnicodeData.txt index ea963a7162..bdcc41850d 100644 --- a/src/unicode/UnicodeData.txt +++ b/src/unicode/UnicodeData.txt @@ -11231,6 +11231,10 @@ 2FF9;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER RIGHT;So;0;ON;;;;;N;;;;; 2FFA;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER LEFT;So;0;ON;;;;;N;;;;; 2FFB;IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID;So;0;ON;;;;;N;;;;; +2FFC;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM RIGHT;So;0;ON;;;;;N;;;;; +2FFD;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER RIGHT;So;0;ON;;;;;N;;;;; +2FFE;IDEOGRAPHIC DESCRIPTION CHARACTER HORIZONTAL REFLECTION;So;0;ON;;;;;N;;;;; +2FFF;IDEOGRAPHIC DESCRIPTION CHARACTER ROTATION;So;0;ON;;;;;N;;;;; 3000;IDEOGRAPHIC SPACE;Zs;0;WS;<wide> 0020;;;;N;;;;; 3001;IDEOGRAPHIC COMMA;Po;0;ON;;;;;N;;;;; 3002;IDEOGRAPHIC FULL STOP;Po;0;ON;;;;;N;IDEOGRAPHIC PERIOD;;;; @@ -11705,6 +11709,7 @@ 31E1;CJK STROKE HZZZG;So;0;ON;;;;;N;;;;; 31E2;CJK STROKE PG;So;0;ON;;;;;N;;;;; 31E3;CJK STROKE Q;So;0;ON;;;;;N;;;;; +31EF;IDEOGRAPHIC DESCRIPTION CHARACTER SUBTRACTION;So;0;ON;;;;;N;;;;; 31F0;KATAKANA LETTER SMALL KU;Lo;0;L;;;;;N;;;;; 31F1;KATAKANA LETTER SMALL SI;Lo;0;L;;;;;N;;;;; 31F2;KATAKANA LETTER SMALL SU;Lo;0;L;;;;;N;;;;; @@ -34035,6 +34040,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 2CEA1;<CJK Ideograph Extension E, Last>;Lo;0;L;;;;;N;;;;; 2CEB0;<CJK Ideograph Extension F, First>;Lo;0;L;;;;;N;;;;; 2EBE0;<CJK Ideograph Extension F, Last>;Lo;0;L;;;;;N;;;;; +2EBF0;<CJK Ideograph Extension I, First>;Lo;0;L;;;;;N;;;;; +2EE5D;<CJK Ideograph Extension I, Last>;Lo;0;L;;;;;N;;;;; 2F800;CJK COMPATIBILITY IDEOGRAPH-2F800;Lo;0;L;4E3D;;;;N;;;;; 2F801;CJK COMPATIBILITY IDEOGRAPH-2F801;Lo;0;L;4E38;;;;N;;;;; 2F802;CJK COMPATIBILITY IDEOGRAPH-2F802;Lo;0;L;4E41;;;;N;;;;; diff --git a/src/unicode/emoji-data.txt b/src/unicode/emoji-data.txt index 999a436779..0ba10e9ce4 100644 --- a/src/unicode/emoji-data.txt +++ b/src/unicode/emoji-data.txt @@ -1,11 +1,11 @@ # emoji-data.txt -# Date: 2022-08-02, 00:26:10 GMT -# © 2022 Unicode®, Inc. +# Date: 2023-02-01, 02:22:54 GMT +# © 2023 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # For terms of use, see https://www.unicode.org/terms_of_use.html # # Emoji Data for UTS #51 -# Used with Emoji Version 15.0 and subsequent minor revisions (if any) +# Used with Emoji Version 15.1 and subsequent minor revisions (if any) # # For documentation and usage, see https://www.unicode.org/reports/tr51 # |