summaryrefslogtreecommitdiff
path: root/usr.bin/vim/help.c
diff options
context:
space:
mode:
authorJason Downs <downsj@cvs.openbsd.org>1996-09-07 21:40:33 +0000
committerJason Downs <downsj@cvs.openbsd.org>1996-09-07 21:40:33 +0000
commitc224fc199c25dd257673c273eb344786b9bf532c (patch)
tree8f8ed1297120c537480d9e5d46bfe7452bd8505b /usr.bin/vim/help.c
parentd0d91e2d3d6569e4defdd5178241f28fa678d753 (diff)
Initial import of vim 4.2.
This is meant to replace nvi in the tree. Vim, in general, works better, provides more features, and does not suffer from the license problems being imposed upon nvi. On the other hand, vim lacks a non-visual ex mode, in addition to open mode. This includes the GUI (X11) code, but doesn't try to compile it.
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;
+}