diff options
-rw-r--r-- | runtime/doc/starting.txt | 3 | ||||
-rw-r--r-- | src/nvim/api/private/dispatch.c | 3 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 47 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 8 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 114 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 21 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 2 | ||||
-rw-r--r-- | src/nvim/getchar.c | 75 | ||||
-rw-r--r-- | src/nvim/main.c | 1 | ||||
-rw-r--r-- | src/nvim/normal.c | 4 | ||||
-rw-r--r-- | src/nvim/testdir/test_help.vim | 36 | ||||
-rw-r--r-- | src/nvim/testdir/test_help_tagjump.vim | 28 | ||||
-rw-r--r-- | src/nvim/testdir/test_mksession.vim | 90 | ||||
-rw-r--r-- | src/nvim/testdir/test_timers.vim | 20 | ||||
-rw-r--r-- | test/functional/api/buffer_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/api/tabpage_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 58 | ||||
-rw-r--r-- | test/functional/api/window_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/eval/timer_spec.lua | 1 |
19 files changed, 366 insertions, 151 deletions
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index c9ce2b9dc1..3440131642 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -76,7 +76,8 @@ The option arguments may be given in any order. Single-letter options can be combined after one dash. There can be no option arguments after the "--" argument. ---help *-h* *--help* +--help *-h* *--help* *-?* +-? -h Give usage (help) message and exit. See |info-message| about capturing the text. diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index dec2b6c185..c08225bbfc 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -40,7 +40,8 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, map_get(String, MsgpackRpcRequestHandler)(methods, m); if (!rv.fn) { - api_set_error(error, kErrorTypeException, "Invalid method: %s", + api_set_error(error, kErrorTypeException, "Invalid method: %.*s", + m.size > 0 ? m.size : sizeof("<empty>"), m.size > 0 ? m.data : "<empty>"); } return rv; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f3e883de02..ecc0ede4a4 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -162,7 +162,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err) dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); if (di == NULL) { - api_set_error(err, kErrorTypeValidation, "Key '%s' not found", key.data); + api_set_error(err, kErrorTypeValidation, "Key not found: %s", key.data); return (Object)OBJECT_INIT; } @@ -191,13 +191,12 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, } if (key.size == 0) { - api_set_error(err, kErrorTypeValidation, - "Empty variable names aren't allowed"); + api_set_error(err, kErrorTypeValidation, "Key name is empty"); return rv; } if (key.size > INT_MAX) { - api_set_error(err, kErrorTypeValidation, "Key length is too high"); + api_set_error(err, kErrorTypeValidation, "Key name is too long"); return rv; } @@ -220,7 +219,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, // Delete the key if (di == NULL) { // Doesn't exist, fail - api_set_error(err, kErrorTypeValidation, "Key does not exist: %s", + api_set_error(err, kErrorTypeValidation, "Key not found: %s", key.data); } else { // Return the old value @@ -284,9 +283,7 @@ Object get_option_from(void *from, int type, String name, Error *err) type, from); if (!flags) { - api_set_error(err, - kErrorTypeValidation, - "Invalid option name \"%s\"", + api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'", name.data); return rv; } @@ -303,15 +300,14 @@ Object get_option_from(void *from, int type, String name, Error *err) rv.data.string.data = stringval; rv.data.string.size = strlen(stringval); } else { - api_set_error(err, - kErrorTypeException, - "Unable to get value for option \"%s\"", + api_set_error(err, kErrorTypeException, + "Failed to get value for option '%s'", name.data); } } else { api_set_error(err, kErrorTypeException, - "Unknown type for option \"%s\"", + "Unknown type for option '%s'", name.data); } @@ -336,24 +332,20 @@ void set_option_to(uint64_t channel_id, void *to, int type, int flags = get_option_value_strict(name.data, NULL, NULL, type, to); if (flags == 0) { - api_set_error(err, - kErrorTypeValidation, - "Invalid option name \"%s\"", + api_set_error(err, kErrorTypeValidation, "Invalid option name '%s'", name.data); return; } if (value.type == kObjectTypeNil) { if (type == SREQ_GLOBAL) { - api_set_error(err, - kErrorTypeException, - "Unable to unset option \"%s\"", + api_set_error(err, kErrorTypeException, "Cannot unset option '%s'", name.data); return; } else if (!(flags & SOPT_GLOBAL)) { api_set_error(err, kErrorTypeException, - "Cannot unset option \"%s\" " + "Cannot unset option '%s' " "because it doesn't have a global value", name.data); return; @@ -370,7 +362,7 @@ void set_option_to(uint64_t channel_id, void *to, int type, if (value.type != kObjectTypeBoolean) { api_set_error(err, kErrorTypeValidation, - "Option \"%s\" requires a boolean value", + "Option '%s' requires a Boolean value", name.data); return; } @@ -378,17 +370,15 @@ void set_option_to(uint64_t channel_id, void *to, int type, numval = value.data.boolean; } else if (flags & SOPT_NUM) { if (value.type != kObjectTypeInteger) { - api_set_error(err, - kErrorTypeValidation, - "Option \"%s\" requires an integer value", + api_set_error(err, kErrorTypeValidation, + "Option '%s' requires an integer value", name.data); return; } if (value.data.integer > INT_MAX || value.data.integer < INT_MIN) { - api_set_error(err, - kErrorTypeValidation, - "Value for option \"%s\" is outside range", + api_set_error(err, kErrorTypeValidation, + "Value for option '%s' is out of range", name.data); return; } @@ -396,9 +386,8 @@ void set_option_to(uint64_t channel_id, void *to, int type, numval = (int)value.data.integer; } else { if (value.type != kObjectTypeString) { - api_set_error(err, - kErrorTypeValidation, - "Option \"%s\" requires a string value", + api_set_error(err, kErrorTypeValidation, + "Option '%s' requires a string value", name.data); return; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index e78b8c776d..fed20a272a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -502,7 +502,7 @@ Integer nvim_strwidth(String text, Error *err) FUNC_API_SINCE(1) { if (text.size > INT_MAX) { - api_set_error(err, kErrorTypeValidation, "String length is too high"); + api_set_error(err, kErrorTypeValidation, "String is too long"); return 0; } @@ -559,7 +559,7 @@ void nvim_set_current_dir(String dir, Error *err) FUNC_API_SINCE(1) { if (dir.size >= MAXPATHL) { - api_set_error(err, kErrorTypeValidation, "Directory string is too long"); + api_set_error(err, kErrorTypeValidation, "Directory name is too long"); return; } @@ -1136,14 +1136,14 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err) if (calls.items[i].type != kObjectTypeArray) { api_set_error(err, kErrorTypeValidation, - "All items in calls array must be arrays"); + "Items in calls array must be arrays"); goto validation_error; } Array call = calls.items[i].data.array; if (call.size != 2) { api_set_error(err, kErrorTypeValidation, - "All items in calls array must be arrays of size 2"); + "Items in calls array must be arrays of size 2"); goto validation_error; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index ca975ee02a..a9e9364dc3 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4675,49 +4675,66 @@ static int help_compare(const void *s1, const void *s2) return strcmp(p1, p2); } -/* - * Find all help tags matching "arg", sort them and return in matches[], with - * the number of matches in num_matches. - * The matches will be sorted with a "best" match algorithm. - * When "keep_lang" is TRUE try keeping the language of the current buffer. - */ -int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_lang) +// Find all help tags matching "arg", sort them and return in matches[], with +// the number of matches in num_matches. +// The matches will be sorted with a "best" match algorithm. +// When "keep_lang" is true try keeping the language of the current buffer. +int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, + bool keep_lang) { - char_u *s, *d; int i; - static char *(mtable[]) = {"*", "g*", "[*", "]*", - "/*", "/\\*", "\"*", "**", - "/\\(\\)", "/\\%(\\)", - "?", ":?", "?<CR>", "g?", "g?g?", "g??", - "/\\?", "/\\z(\\)", "\\=", ":s\\=", - "[count]", "[quotex]", - "[range]", ":[range]", - "[pattern]", "\\|", "\\%$", - "s/\\~", "s/\\U", "s/\\L", - "s/\\1", "s/\\2", "s/\\3", "s/\\9"}; - static char *(rtable[]) = {"star", "gstar", "[star", "]star", - "/star", "/\\\\star", "quotestar", "starstar", - "/\\\\(\\\\)", "/\\\\%(\\\\)", - "?", ":?", "?<CR>", "g?", "g?g?", "g??", - "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", - "\\[count]", "\\[quotex]", - "\\[range]", ":\\[range]", - "\\[pattern]", "\\\\bar", "/\\\\%\\$", - "s/\\\\\\~", "s/\\\\U", "s/\\\\L", - "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"}; - int flags; - - d = IObuff; /* assume IObuff is long enough! */ - - /* - * Recognize a few exceptions to the rule. Some strings that contain '*' - * with "star". Otherwise '*' is recognized as a wildcard. - */ - for (i = (int)ARRAY_SIZE(mtable); --i >= 0; ) - if (STRCMP(arg, mtable[i]) == 0) { - STRCPY(d, rtable[i]); - break; + static const char *(mtable[]) = { + "*", "g*", "[*", "]*", + "/*", "/\\*", "\"*", "**", + "/\\(\\)", "/\\%(\\)", + "?", ":?", "?<CR>", "g?", "g?g?", "g??", + "-?", "q?", "v_g?", + "/\\?", "/\\z(\\)", "\\=", ":s\\=", + "[count]", "[quotex]", + "[range]", ":[range]", + "[pattern]", "\\|", "\\%$", + "s/\\~", "s/\\U", "s/\\L", + "s/\\1", "s/\\2", "s/\\3", "s/\\9" + }; + static const char *(rtable[]) = { + "star", "gstar", "[star", "]star", + "/star", "/\\\\star", "quotestar", "starstar", + "/\\\\(\\\\)", "/\\\\%(\\\\)", + "?", ":?", "?<CR>", "g?", "g?g?", "g??", + "-?", "q?", "v_g?", + "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", + "\\[count]", "\\[quotex]", + "\\[range]", ":\\[range]", + "\\[pattern]", "\\\\bar", "/\\\\%\\$", + "s/\\\\\\~", "s/\\\\U", "s/\\\\L", + "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9" + }; + static const char *(expr_table[]) = { + "!=?", "!~?", "<=?", "<?", "==?", "=~?", + ">=?", ">?", "is?", "isnot?" + }; + char_u *d = IObuff; // assume IObuff is long enough! + + if (STRNICMP(arg, "expr-", 5) == 0) { + // When the string starting with "expr-" and containing '?' and matches + // the table, it is taken literally. Otherwise '?' is recognized as a + // wildcard. + for (i = (int)ARRAY_SIZE(expr_table); --i >= 0; ) { + if (STRCMP(arg + 5, expr_table[i]) == 0) { + STRCPY(d, arg); + break; + } + } + } else { + // Recognize a few exceptions to the rule. Some strings that contain + // '*' with "star". Otherwise '*' is recognized as a wildcard. + for (i = (int)ARRAY_SIZE(mtable); --i >= 0; ) { + if (STRCMP(arg, mtable[i]) == 0) { + STRCPY(d, rtable[i]); + break; + } } + } if (i < 0) { /* no match in table */ /* Replace "\S" with "/\\S", etc. Otherwise every tag is matched. @@ -4749,7 +4766,7 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la if (*arg == '(' && arg[1] == '\'') { arg++; } - for (s = arg; *s; s++) { + for (const char_u *s = arg; *s; s++) { // Replace "|" with "bar" and '"' with "quote" to match the name of // the tags for these commands. // Replace "*" with ".*" and "?" with "." to match command line @@ -4858,9 +4875,10 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la *matches = (char_u **)""; *num_matches = 0; - flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE; - if (keep_lang) + int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE; + if (keep_lang) { flags |= TAG_KEEP_LANG; + } if (find_tags(IObuff, num_matches, matches, flags, (int)MAXCOL, NULL) == OK && *num_matches > 0) { /* Sort the matches found on the heuristic number that is after the @@ -5017,11 +5035,9 @@ void fix_help_buffer(void) const char_u *const f1 = fnames[i1]; const char_u *const f2 = fnames[i2]; const char_u *const t1 = path_tail(f1); - if (fnamencmp(f1, f2, t1 - f1) != 0) { - continue; - } + const char_u *const t2 = path_tail(f2); const char_u *const e1 = STRRCHR(t1, '.'); - const char_u *const e2 = STRRCHR(path_tail(f2), '.'); + const char_u *const e2 = STRRCHR(t2, '.'); if (e1 == NULL || e2 == NULL) { continue; } @@ -5032,8 +5048,10 @@ void fix_help_buffer(void) fnames[i1] = NULL; continue; } - if (fnamencmp(f1, f2, e1 - f1) != 0) + if (e1 - f1 != e2 - f2 + || fnamencmp(f1, f2, e1 - f1) != 0) { continue; + } if (fnamecmp(e1, ".txt") == 0 && fnamecmp(e2, fname + 4) == 0) { /* use .abx instead of .txt */ diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 0337b37cb2..03f1446265 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9259,6 +9259,18 @@ static int ses_do_win(win_T *wp) return true; } +static int put_view_curpos(FILE *fd, const win_T *wp, char *spaces) +{ + int r; + + if (wp->w_curswant == MAXCOL) { + r = fprintf(fd, "%snormal! $", spaces); + } else { + r = fprintf(fd, "%snormal! 0%d|", spaces, wp->w_virtcol + 1); + } + return r < 0 || put_eol(fd) == FAIL ? FAIL : OK; +} + /* * Write commands to "fd" to restore the view of a window. * Caller must make sure 'scrolloff' is zero. @@ -9425,14 +9437,11 @@ put_view( (int64_t)(wp->w_virtcol + 1)) < 0 || put_eol(fd) == FAIL || put_line(fd, "else") == FAIL - || fprintf(fd, " normal! 0%d|", wp->w_virtcol + 1) < 0 - || put_eol(fd) == FAIL + || put_view_curpos(fd, wp, " ") == FAIL || put_line(fd, "endif") == FAIL) return FAIL; - } else { - if (fprintf(fd, "normal! 0%d|", wp->w_virtcol + 1) < 0 - || put_eol(fd) == FAIL) - return FAIL; + } else if (put_view_curpos(fd, wp, "") == FAIL) { + return FAIL; } } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 786e2cd12c..ff625d6dc9 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4677,7 +4677,7 @@ ExpandFromContext ( /* With an empty argument we would get all the help tags, which is * very slow. Get matches for "help" instead. */ if (find_help_tags(*pat == NUL ? (char_u *)"help" : pat, - num_file, file, FALSE) == OK) { + num_file, file, false) == OK) { cleanup_help_tags(*num_file, *file); return OK; } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index df185f1a5b..f13bede076 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -434,9 +434,8 @@ void flush_buffers(int flush_typeahead) * of an escape sequence. * In an xterm we get one char at a time and we have to get them all. */ - while (inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 10L, - typebuf.tb_change_cnt) != 0) - ; + while (inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 10L) != 0) { + } typebuf.tb_off = MAXMAPLEN; typebuf.tb_len = 0; // Reset the flag that text received from a client or from feedkeys() @@ -1697,22 +1696,20 @@ static int vgetorpeek(int advance) os_breakcheck(); /* check for CTRL-C */ keylen = 0; if (got_int) { - /* flush all input */ - c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L, - typebuf.tb_change_cnt); - /* - * If inchar() returns TRUE (script file was active) or we - * are inside a mapping, get out of insert mode. - * Otherwise we behave like having gotten a CTRL-C. - * As a result typing CTRL-C in insert mode will - * really insert a CTRL-C. - */ + // flush all input + c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L); + // If inchar() returns TRUE (script file was active) or we + // are inside a mapping, get out of insert mode. + // Otherwise we behave like having gotten a CTRL-C. + // As a result typing CTRL-C in insert mode will + // really insert a CTRL-C. if ((c || typebuf.tb_maplen) - && (State & (INSERT + CMDLINE))) + && (State & (INSERT + CMDLINE))) { c = ESC; - else + } else { c = Ctrl_C; - flush_buffers(TRUE); /* flush all typeahead */ + } + flush_buffers(true); // flush all typeahead if (advance) { /* Also record this character, it might be needed to @@ -2075,18 +2072,17 @@ static int vgetorpeek(int advance) c = 0; new_wcol = curwin->w_wcol; new_wrow = curwin->w_wrow; - if ( advance - && typebuf.tb_len == 1 - && typebuf.tb_buf[typebuf.tb_off] == ESC - && !no_mapping - && ex_normal_busy == 0 - && typebuf.tb_maplen == 0 - && (State & INSERT) - && (p_timeout - || (keylen == KEYLEN_PART_KEY && p_ttimeout)) - && (c = inchar(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_len, 3, 25L, - typebuf.tb_change_cnt)) == 0) { + if (advance + && typebuf.tb_len == 1 + && typebuf.tb_buf[typebuf.tb_off] == ESC + && !no_mapping + && ex_normal_busy == 0 + && typebuf.tb_maplen == 0 + && (State & INSERT) + && (p_timeout + || (keylen == KEYLEN_PART_KEY && p_ttimeout)) + && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, + 3, 25L)) == 0) { colnr_T col = 0, vcol; char_u *ptr; @@ -2258,6 +2254,11 @@ static int vgetorpeek(int advance) /* * get a character: 3. from the user - get it */ + if (typebuf.tb_len == 0) { + // timedout may have been set while waiting for a mapping + // that has a <Nop> RHS. + timedout = false; + } wait_tb_len = typebuf.tb_len; c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1, @@ -2269,7 +2270,7 @@ static int vgetorpeek(int advance) ? -1L : ((keylen == KEYLEN_PART_KEY && p_ttm >= 0) ? p_ttm - : p_tm)), typebuf.tb_change_cnt); + : p_tm))); if (i != 0) pop_showcmd(); @@ -2350,16 +2351,15 @@ static int vgetorpeek(int advance) * Return the number of obtained characters. * Return -1 when end of input script reached. */ -int -inchar ( +int inchar( char_u *buf, int maxlen, - long wait_time, /* milli seconds */ - int tb_change_cnt + long wait_time // milli seconds ) { int len = 0; // Init for GCC. int retesc = false; // Return ESC with gotint. + const int tb_change_cnt = typebuf.tb_change_cnt; if (wait_time == -1L || wait_time > 100L) { // flush output before waiting @@ -2430,10 +2430,19 @@ inchar ( len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt); } + // If the typebuf was changed further down, it is like nothing was added by + // this call. if (typebuf_changed(tb_change_cnt)) { return 0; } + // Note the change in the typeahead buffer, this matters for when + // vgetorpeek() is called recursively, e.g. using getchar(1) in a timer + // function. + if (len > 0 && ++typebuf.tb_change_cnt == 0) { + typebuf.tb_change_cnt = 1; + } + return fix_input_buffer(buf, len); } diff --git a/src/nvim/main.c b/src/nvim/main.c index af7c194edc..ab8b33aa12 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -892,6 +892,7 @@ static void command_line_scan(mparm_T *parmp) set_option_value("rl", 1L, NULL, 0); break; } + case '?': // "-?" give help message (for MS-Windows) case 'h': { // "-h" give help message usage(); mch_exit(0); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 0bf93ee001..09444ace0f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4771,6 +4771,10 @@ static void nv_ident(cmdarg_T *cap) assert(*kp != NUL); // option.c:do_set() should default to ":help" if empty. bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command bool kp_help = (STRCMP(kp, ":he") == 0 || STRCMP(kp, ":help") == 0); + if (kp_help && *skipwhite(ptr) == NUL) { + EMSG(_(e_noident)); // found white space only + return; + } size_t buf_size = n * 2 + 30 + STRLEN(kp); char *buf = xmalloc(buf_size); buf[0] = NUL; diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim index 26edc16107..ed3181564c 100644 --- a/src/nvim/testdir/test_help.vim +++ b/src/nvim/testdir/test_help.vim @@ -13,4 +13,40 @@ endfunc func Test_help_errors() call assert_fails('help doesnotexist', 'E149:') call assert_fails('help!', 'E478:') + + new + set keywordprg=:help + call setline(1, " ") + call assert_fails('normal VK', 'E349:') + bwipe! +endfunc + +func Test_help_keyword() + new + set keywordprg=:help + call setline(1, " Visual ") + normal VK + call assert_match('^Visual mode', getline('.')) + call assert_equal('help', &ft) + close + bwipe! +endfunc + +func Test_help_local_additions() + call mkdir('Xruntime/doc', 'p') + call writefile(['*mydoc.txt* my awesome doc'], 'Xruntime/doc/mydoc.txt') + call writefile(['*mydoc-ext.txt* my extended awesome doc'], 'Xruntime/doc/mydoc-ext.txt') + let rtp_save = &rtp + set rtp+=./Xruntime + help + 1 + call search('mydoc.txt') + call assert_equal('|mydoc.txt| my awesome doc', getline('.')) + 1 + call search('mydoc-ext.txt') + call assert_equal('|mydoc-ext.txt| my extended awesome doc', getline('.')) + close + + call delete('Xruntime', 'rf') + let &rtp = rtp_save endfunc diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim index 4d4a902031..c873487b92 100644 --- a/src/nvim/testdir/test_help_tagjump.vim +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -38,6 +38,34 @@ func Test_help_tagjump() call assert_true(getline('.') =~ '\*:?\*') helpclose + help q? + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*q?\*') + call assert_true(expand('<cword>') == 'q?') + helpclose + + help -? + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*-?\*') + helpclose + + help v_g? + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*v_g?\*') + helpclose + + help expr-!=? + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*expr-!=?\*') + call assert_true(expand('<cword>') == 'expr-!=?') + helpclose + + help expr-isnot? + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*expr-isnot?\*') + call assert_true(expand('<cword>') == 'expr-isnot?') + helpclose + help FileW*Post call assert_equal("help", &filetype) call assert_true(getline('.') =~ '\*FileWritePost\*') diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 4774cf4af5..9ba264deb6 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -19,7 +19,8 @@ func Test_mksession() \ 'two tabs in one line', \ 'one multibyteCharacter', \ 'a two multiByte characters', - \ 'A three mulTibyte characters' + \ 'A three mulTibyte characters', + \ 'short line', \ ]) let tmpfile = 'Xtemp' exec 'w! ' . tmpfile @@ -41,6 +42,8 @@ func Test_mksession() norm! j16| split norm! j16| + split + norm! j$ wincmd l set nowrap @@ -63,7 +66,7 @@ func Test_mksession() split call wincol() mksession! Xtest_mks.out - let li = filter(readfile('Xtest_mks.out'), 'v:val =~# "\\(^ *normal! 0\\|^ *exe ''normal!\\)"') + let li = filter(readfile('Xtest_mks.out'), 'v:val =~# "\\(^ *normal! [0$]\\|^ *exe ''normal!\\)"') let expected = [ \ 'normal! 016|', \ 'normal! 016|', @@ -73,6 +76,7 @@ func Test_mksession() \ 'normal! 016|', \ 'normal! 016|', \ 'normal! 016|', + \ 'normal! $', \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", \ " normal! 016|", \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", @@ -151,5 +155,87 @@ func Test_mksession_one_buffer_two_windows() call delete('Xtest_mks.out') endfunc +" Test :mkview with a file argument. +func Test_mkview_file() + " Create a view with line number and a fold. + help :mkview + set number + norm! V}zf0 + let pos = getpos('.') + let linefoldclosed1 = foldclosed('.') + mkview! Xview + set nonumber + norm! zrj + " We can close the help window, as mkview with a file name should + " generate a command to edit the file. + helpclose + + source Xview + call assert_equal(1, &number) + call assert_match('\*:mkview\*$', getline('.')) + call assert_equal(pos, getpos('.')) + call assert_equal(linefoldclosed1, foldclosed('.')) + + " Creating a view again with the same file name should fail (file + " already exists). But with a !, the previous view should be + " overwritten without error. + help :loadview + call assert_fails('mkview Xview', 'E189:') + call assert_match('\*:loadview\*$', getline('.')) + mkview! Xview + call assert_match('\*:loadview\*$', getline('.')) + + call delete('Xview') + bwipe +endfunc + +" Test :mkview and :loadview with a custom 'viewdir'. +func Test_mkview_loadview_with_viewdir() + set viewdir=Xviewdir + + help :mkview + set number + norm! V}zf + let pos = getpos('.') + let linefoldclosed1 = foldclosed('.') + mkview 1 + set nonumber + norm! zrj + + loadview 1 + + " The directory Xviewdir/ should have been created and the view + " should be stored in that directory. + let pathsep = has('win32') ? '\' : '/' + call assert_equal('Xviewdir' . pathsep . + \ substitute( + \ substitute( + \ expand('%:p'), pathsep, '=+', 'g'), ':', '=-', 'g') . '=1.vim', + \ glob('Xviewdir/*')) + call assert_equal(1, &number) + call assert_match('\*:mkview\*$', getline('.')) + call assert_equal(pos, getpos('.')) + call assert_equal(linefoldclosed1, foldclosed('.')) + + call delete('Xviewdir', 'rf') + set viewdir& + helpclose +endfunc + +func Test_mkview_no_file_name() + new + " :mkview or :mkview {nr} should fail in a unnamed buffer. + call assert_fails('mkview', 'E32:') + call assert_fails('mkview 1', 'E32:') + + " :mkview {file} should succeed in a unnamed buffer. + mkview Xview + help + source Xview + call assert_equal('', bufname('%')) + + call delete('Xview') + %bwipe +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 81ac2b6171..6450bf02e8 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -169,5 +169,25 @@ func Test_stop_all_in_callback() call assert_equal(0, len(info)) endfunc +func FeedAndPeek(timer) + call test_feedinput('a') + call getchar(1) +endfunc + +func Interrupt(timer) + call test_feedinput("\<C-C>") +endfunc + +func Test_peek_and_get_char() + throw 'skipped: Nvim does not support test_feedinput()' + if !has('unix') && !has('gui_running') + return + endif + call timer_start(0, 'FeedAndPeek') + let intr = timer_start(100, 'Interrupt') + let c = getchar() + call assert_equal(char2nr('a'), c) + call timer_stop(intr) +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 958e2f68fc..271e196103 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -309,7 +309,7 @@ describe('api/buf', function() eq(1, funcs.exists('b:lua')) curbufmeths.del_var('lua') eq(0, funcs.exists('b:lua')) - eq({false, 'Key does not exist: lua'}, meth_pcall(curbufmeths.del_var, 'lua')) + eq({false, 'Key not found: lua'}, meth_pcall(curbufmeths.del_var, 'lua')) curbufmeths.set_var('lua', 1) command('lockvar b:lua') eq({false, 'Key is locked: lua'}, meth_pcall(curbufmeths.del_var, 'lua')) diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua index 260a91a80c..c49091db02 100644 --- a/test/functional/api/tabpage_spec.lua +++ b/test/functional/api/tabpage_spec.lua @@ -34,7 +34,7 @@ describe('api/tabpage', function() eq(1, funcs.exists('t:lua')) curtabmeths.del_var('lua') eq(0, funcs.exists('t:lua')) - eq({false, 'Key does not exist: lua'}, meth_pcall(curtabmeths.del_var, 'lua')) + eq({false, 'Key not found: lua'}, meth_pcall(curtabmeths.del_var, 'lua')) curtabmeths.set_var('lua', 1) command('lockvar t:lua') eq({false, 'Key is locked: lua'}, meth_pcall(curtabmeths.del_var, 'lua')) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index ae8a8488d4..5261f57ca7 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -24,13 +24,25 @@ describe('API', function() before_each(clear) it('validates requests', function() - expect_err('Invalid method: bogus', + -- RPC + expect_err('Invalid method: bogus$', request, 'bogus') - expect_err('Invalid method: … の り 。…', + expect_err('Invalid method: … の り 。…$', request, '… の り 。…') - expect_err('Invalid method: <empty>', + expect_err('Invalid method: <empty>$', request, '') - expect_err("can't serialize object", + + -- Non-RPC: rpcrequest(v:servername) uses internal channel. + expect_err('Invalid method: … の り 。…$', + request, 'nvim_eval', + [=[rpcrequest(sockconnect('pipe', v:servername, {'rpc':1}), '… の り 。…')]=]) + expect_err('Invalid method: bogus$', + request, 'nvim_eval', + [=[rpcrequest(sockconnect('pipe', v:servername, {'rpc':1}), 'bogus')]=]) + + -- XXX: This must be the last one, else next one will fail: + -- "Packer instance already working. Use another Packer ..." + expect_err("can't serialize object$", request, nil) end) @@ -158,7 +170,7 @@ describe('API', function() end) it("VimL error: returns error details, does NOT update v:errmsg", function() - expect_err('E121: Undefined variable: bogus', request, + expect_err('E121: Undefined variable: bogus$', request, 'nvim_eval', 'bogus expression') eq('', eval('v:errmsg')) -- v:errmsg was not updated. end) @@ -173,7 +185,7 @@ describe('API', function() end) it("VimL validation error: returns specific error, does NOT update v:errmsg", function() - expect_err('E117: Unknown function: bogus function', request, + expect_err('E117: Unknown function: bogus function$', request, 'nvim_call_function', 'bogus function', {'arg1'}) expect_err('E119: Not enough arguments for function: atan', request, 'nvim_call_function', 'atan', {}) @@ -182,11 +194,11 @@ describe('API', function() end) it("VimL error: returns error details, does NOT update v:errmsg", function() - expect_err('E808: Number or Float required', request, + expect_err('E808: Number or Float required$', request, 'nvim_call_function', 'atan', {'foo'}) - expect_err('Invalid channel stream "xxx"', request, + expect_err('Invalid channel stream "xxx"$', request, 'nvim_call_function', 'chanclose', {999, 'xxx'}) - expect_err('E900: Invalid channel id', request, + expect_err('E900: Invalid channel id$', request, 'nvim_call_function', 'chansend', {999, 'foo'}) eq('', eval('v:exception')) eq('', eval('v:errmsg')) -- v:errmsg was not updated. @@ -198,7 +210,7 @@ describe('API', function() throw 'wtf' endfunction ]]) - expect_err('wtf', request, + expect_err('wtf$', request, 'nvim_call_function', 'Foo', {}) eq('', eval('v:exception')) eq('', eval('v:errmsg')) -- v:errmsg was not updated. @@ -212,7 +224,7 @@ describe('API', function() endfunction ]]) -- E740 - expect_err('Function called with too many arguments', request, + expect_err('Function called with too many arguments$', request, 'nvim_call_function', 'Foo', too_many_args) end) end) @@ -248,23 +260,23 @@ describe('API', function() it('validates args', function() command('let g:d={"baz":"zub","meep":[]}') - expect_err('Not found: bogus', request, + expect_err('Not found: bogus$', request, 'nvim_call_dict_function', 'g:d', 'bogus', {1,2}) - expect_err('Not a function: baz', request, + expect_err('Not a function: baz$', request, 'nvim_call_dict_function', 'g:d', 'baz', {1,2}) - expect_err('Not a function: meep', request, + expect_err('Not a function: meep$', request, 'nvim_call_dict_function', 'g:d', 'meep', {1,2}) - expect_err('E117: Unknown function: f', request, + expect_err('E117: Unknown function: f$', request, 'nvim_call_dict_function', { f = '' }, 'f', {1,2}) - expect_err('Not a function: f', request, + expect_err('Not a function: f$', request, 'nvim_call_dict_function', "{ 'f': '' }", 'f', {1,2}) - expect_err('dict argument type must be String or Dictionary', request, + expect_err('dict argument type must be String or Dictionary$', request, 'nvim_call_dict_function', 42, 'f', {1,2}) - expect_err('Failed to evaluate dict expression', request, + expect_err('Failed to evaluate dict expression$', request, 'nvim_call_dict_function', 'foo', 'f', {1,2}) - expect_err('dict not found', request, + expect_err('dict not found$', request, 'nvim_call_dict_function', '42', 'f', {1,2}) - expect_err('Invalid %(empty%) function name', request, + expect_err('Invalid %(empty%) function name$', request, 'nvim_call_dict_function', "{ 'f': '' }", '', {1,2}) end) end) @@ -337,7 +349,7 @@ describe('API', function() eq(1, funcs.exists('g:lua')) meths.del_var('lua') eq(0, funcs.exists('g:lua')) - eq({false, 'Key does not exist: lua'}, meth_pcall(meths.del_var, 'lua')) + eq({false, "Key not found: lua"}, meth_pcall(meths.del_var, 'lua')) meths.set_var('lua', 1) command('lockvar lua') eq({false, 'Key is locked: lua'}, meth_pcall(meths.del_var, 'lua')) @@ -948,7 +960,7 @@ describe('API', function() } local status, err = pcall(meths.call_atomic, req) eq(false, status) - ok(err:match(' All items in calls array must be arrays of size 2') ~= nil) + ok(err:match('Items in calls array must be arrays of size 2') ~= nil) -- call before was done, but not after eq(1, meths.get_var('avar')) @@ -958,7 +970,7 @@ describe('API', function() } status, err = pcall(meths.call_atomic, req) eq(false, status) - ok(err:match('All items in calls array must be arrays') ~= nil) + ok(err:match('Items in calls array must be arrays') ~= nil) eq({2,3}, meths.get_var('bvar')) req = { diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index d404ef5426..27d7aa11b4 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -169,7 +169,7 @@ describe('api/win', function() eq(1, funcs.exists('w:lua')) curwinmeths.del_var('lua') eq(0, funcs.exists('w:lua')) - eq({false, 'Key does not exist: lua'}, meth_pcall(curwinmeths.del_var, 'lua')) + eq({false, 'Key not found: lua'}, meth_pcall(curwinmeths.del_var, 'lua')) curwinmeths.set_var('lua', 1) command('lockvar w:lua') eq({false, 'Key is locked: lua'}, meth_pcall(curwinmeths.del_var, 'lua')) diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua index 2dd9968a01..8afc3592cc 100644 --- a/test/functional/eval/timer_spec.lua +++ b/test/functional/eval/timer_spec.lua @@ -96,6 +96,7 @@ describe('timers', function() source([[ func! AddItem(timer) call nvim_buf_set_lines(0, 2, 2, v:true, ['ITEM 3']) + call getchar(1) redraw endfunc call timer_start(200, 'AddItem') |