aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/starting.txt3
-rw-r--r--src/nvim/ex_cmds.c114
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/main.c1
-rw-r--r--src/nvim/normal.c4
-rw-r--r--src/nvim/testdir/test_help.vim36
-rw-r--r--src/nvim/testdir/test_help_tagjump.vim28
7 files changed, 138 insertions, 50 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/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_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/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\*')