summaryrefslogtreecommitdiff
path: root/usr.bin/vim/help.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/vim/help.c')
-rw-r--r--usr.bin/vim/help.c302
1 files changed, 302 insertions, 0 deletions
diff --git a/usr.bin/vim/help.c b/usr.bin/vim/help.c
new file mode 100644
index 00000000000..b3d3a8cd4de
--- /dev/null
+++ b/usr.bin/vim/help.c
@@ -0,0 +1,302 @@
+/* $OpenBSD: help.c,v 1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * help.c: open a read-only window on the vim_help.txt file
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+ void
+do_help(arg)
+ char_u *arg;
+{
+ char_u *fnamep;
+ FILE *helpfd; /* file descriptor of help file */
+ int n;
+ WIN *wp;
+ int num_matches;
+ char_u **matches;
+ int need_free = FALSE;
+
+ /*
+ * If an argument is given, check if there is a match for it.
+ */
+ if (*arg != NUL)
+ {
+ n = find_help_tags(arg, &num_matches, &matches);
+ if (num_matches == 0 || n == FAIL)
+ {
+ EMSG2("Sorry, no help for %s", arg);
+ return;
+ }
+
+ /* The first file is the best match */
+ arg = strsave(matches[0]);
+ need_free = TRUE;
+ FreeWild(num_matches, matches);
+ }
+
+ /*
+ * If there is already a help window open, use that one.
+ */
+ if (!curwin->w_buffer->b_help)
+ {
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (wp->w_buffer != NULL && wp->w_buffer->b_help)
+ break;
+ if (wp != NULL && wp->w_buffer->b_nwindows > 0)
+ win_enter(wp, TRUE);
+ else
+ {
+ /*
+ * There is no help buffer yet.
+ * Try to open the file specified by the "helpfile" option.
+ */
+ fnamep = p_hf;
+ if ((helpfd = fopen((char *)p_hf, READBIN)) == NULL)
+ {
+#if defined(MSDOS)
+ /*
+ * for MSDOS: try the DOS search path
+ */
+ fnamep = searchpath("vim_help.txt");
+ if (fnamep == NULL ||
+ (helpfd = fopen((char *)fnamep, READBIN)) == NULL)
+ {
+ smsg((char_u *)"Sorry, help file \"%s\" and \"vim_help.txt\" not found", p_hf);
+ goto erret;
+ }
+#else
+ smsg((char_u *)"Sorry, help file \"%s\" not found", p_hf);
+ goto erret;
+#endif
+ }
+ fclose(helpfd);
+
+ if (win_split(0, FALSE) == FAIL)
+ goto erret;
+
+ if (curwin->w_height < p_hh)
+ win_setheight((int)p_hh);
+
+#ifdef RIGHTLEFT
+ curwin->w_p_rl = 0; /* help window is left-to-right */
+#endif
+ curwin->w_p_nu = 0; /* no line numbers */
+
+ /*
+ * open help file (do_ecmd() will set b_help flag, readfile() will
+ * set b_p_ro flag)
+ */
+ (void)do_ecmd(0, fnamep, NULL, NULL, TRUE, (linenr_t)0, TRUE);
+
+ /* save the values of the options we change */
+ vim_free(help_save_isk);
+ help_save_isk = strsave(curbuf->b_p_isk);
+ help_save_ts = curbuf->b_p_ts;
+
+ /* accept all chars for keywords, except ' ', '*', '"', '|' */
+ set_string_option((char_u *)"isk", -1,
+ (char_u *)"!-~,^*,^|,^\"", TRUE);
+ curbuf->b_p_ts = 8;
+ check_buf_options(curbuf);
+ (void)init_chartab(); /* needed because 'isk' changed */
+ }
+ }
+
+ restart_edit = 0; /* don't want insert mode in help file */
+
+ stuffReadbuff((char_u *)":ta ");
+ if (arg != NULL && *arg != NUL)
+ stuffReadbuff(arg);
+ else
+ stuffReadbuff((char_u *)"vim_help.txt"); /* go to the index */
+ stuffcharReadbuff('\n');
+
+erret:
+ if (need_free)
+ vim_free(arg);
+}
+
+/*
+ * Return a heuristic indicating how well the given string matches. The
+ * smaller the number, the better the match. This is the order of priorities,
+ * from best match to worst match:
+ * - Match with least alpha-numeric characters is better.
+ * - Match with least total characters is better.
+ * - Match towards the start is better.
+ * Assumption is made that the matched_string passed has already been found to
+ * match some string for which help is requested. webb.
+ */
+ int
+help_heuristic(matched_string, offset)
+ char_u *matched_string;
+ int offset; /* offset for match */
+{
+ int num_letters;
+ char_u *p;
+
+ num_letters = 0;
+ for (p = matched_string; *p; p++)
+ if (isalnum(*p))
+ num_letters++;
+
+ /*
+ * Multiply the number of letters by 100 to give it a much bigger
+ * weighting than the number of characters.
+ * If the match starts in the middle of a word, add 10000 to put it
+ * somewhere in the last half.
+ * If the match is more than 2 chars from the start, multiply by 200 to
+ * put it after matches at the start.
+ */
+ if (isalnum(matched_string[offset]) && offset > 0 &&
+ isalnum(matched_string[offset - 1]))
+ offset += 10000;
+ else if (offset > 2)
+ offset *= 200;
+ return (int)(100 * num_letters + STRLEN(matched_string) + offset);
+}
+
+static int help_compare __ARGS((const void *s1, const void *s2));
+
+/*
+ * Compare functions for qsort() below, that checks the help heuristics number
+ * that has been put after the tagname by find_tags().
+ */
+ static int
+help_compare(s1, s2)
+ const void *s1;
+ const void *s2;
+{
+ char *p1;
+ char *p2;
+
+ p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
+ p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
+ return strcmp(p1, p2);
+}
+
+/*
+ * Find all help tags matching "arg", sort them and return in matches[], with
+ * the number of matches in num_matches.
+ * We try first with case, and then ignoring case. Then we try to choose the
+ * "best" match from the ones found.
+ */
+ int
+find_help_tags(arg, num_matches, matches)
+ char_u *arg;
+ int *num_matches;
+ char_u ***matches;
+{
+ char_u *s, *d;
+ regexp *prog;
+ int attempt;
+ int retval = FAIL;
+
+ reg_magic = p_magic;
+ d = IObuff; /* assume IObuff is long enough! */
+
+ /*
+ * Replace "|" with "bar", """ with "quote" and "*" with "star" to
+ * match the name of the tags for these commands.
+ * Replace "*" with ".*" and "?" with "." to match command line
+ * completion.
+ * Insert a backslash before '~', '$' and '.' to avoid their
+ * special meaning.
+ * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
+ * ":help i_^_CTRL-D" work.
+ * If tag starts with ', toss everything after a second '. Fixes
+ * CTRL-] on 'option'. (would include the trailing '.').
+ */
+ if (STRCMP(arg, "*") == 0 || STRCMP(arg, "[*") == 0 ||
+ STRCMP(arg, "]*") == 0)
+ {
+ if (*arg != '*')
+ *d++ = *arg;
+ STRCPY(d, "star");
+ d += 4;
+ }
+ else
+ {
+ for (s = arg; *s; ++s)
+ {
+ if (d - IObuff > IOSIZE - 10) /* getting too long!? */
+ break;
+ switch (*s)
+ {
+ case '|': STRCPY(d, "bar");
+ d += 3;
+ continue;
+ case '\"': STRCPY(d, "quote");
+ d += 5;
+ continue;
+ case '*': *d++ = '.';
+ break;
+ /* "?", ":?" and "?<CR>" are real tags */
+ case '?': if (arg[1] == NUL ||
+ STRCMP(arg, ":?") == 0 ||
+ STRCMP(arg, "?<CR>") == 0)
+ break;
+ *d++ = '.';
+ continue;
+ case '$':
+ case '.':
+ case '~': *d++ = '\\';
+ break;
+ }
+ if (*s < ' ' || (*s == '^' && s[1] && s[1] != '_')) /* ^x */
+ {
+ STRCPY(d, "CTRL-");
+ d += 5;
+ if (*s < ' ')
+ {
+ *d++ = *s + '@';
+ continue;
+ }
+ ++s;
+ }
+ else if (*s == '^') /* "^" or "CTRL-^" or "^_" */
+ *d++ = '\\';
+ *d++ = *s;
+ if (*s == '\'' && s > arg && *arg == '\'')
+ break;
+ }
+ }
+ *d = NUL;
+
+ reg_ic = FALSE;
+ prog = vim_regcomp(IObuff);
+ if (prog == NULL)
+ return FAIL;
+
+ /* First try to match with case, then without */
+ for (attempt = 0; attempt < 2; ++attempt, reg_ic = TRUE)
+ {
+ *matches = (char_u **)"";
+ *num_matches = 0;
+ retval = find_tags(NULL, prog, num_matches, matches, TRUE);
+ if (retval == FAIL || *num_matches)
+ break;
+ }
+ vim_free(prog);
+
+#ifdef HAVE_QSORT
+ /*
+ * Sort the matches found on the heuristic number that is after the
+ * tag name. If there is no qsort, the output will be messy!
+ */
+ qsort((void *)*matches, (size_t)*num_matches,
+ sizeof(char_u *), help_compare);
+#endif
+ return OK;
+}