Annotation of src/usr.bin/vim/help.c, Revision 1.1.1.1
1.1 downsj 1: /* $OpenBSD$ */
2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: *
6: * Do ":help uganda" in Vim to read copying and usage conditions.
7: * Do ":help credits" in Vim to see a list of people who contributed.
8: */
9:
10: /*
11: * help.c: open a read-only window on the vim_help.txt file
12: */
13:
14: #include "vim.h"
15: #include "globals.h"
16: #include "proto.h"
17: #include "option.h"
18:
19: void
20: do_help(arg)
21: char_u *arg;
22: {
23: char_u *fnamep;
24: FILE *helpfd; /* file descriptor of help file */
25: int n;
26: WIN *wp;
27: int num_matches;
28: char_u **matches;
29: int need_free = FALSE;
30:
31: /*
32: * If an argument is given, check if there is a match for it.
33: */
34: if (*arg != NUL)
35: {
36: n = find_help_tags(arg, &num_matches, &matches);
37: if (num_matches == 0 || n == FAIL)
38: {
39: EMSG2("Sorry, no help for %s", arg);
40: return;
41: }
42:
43: /* The first file is the best match */
44: arg = strsave(matches[0]);
45: need_free = TRUE;
46: FreeWild(num_matches, matches);
47: }
48:
49: /*
50: * If there is already a help window open, use that one.
51: */
52: if (!curwin->w_buffer->b_help)
53: {
54: for (wp = firstwin; wp != NULL; wp = wp->w_next)
55: if (wp->w_buffer != NULL && wp->w_buffer->b_help)
56: break;
57: if (wp != NULL && wp->w_buffer->b_nwindows > 0)
58: win_enter(wp, TRUE);
59: else
60: {
61: /*
62: * There is no help buffer yet.
63: * Try to open the file specified by the "helpfile" option.
64: */
65: fnamep = p_hf;
66: if ((helpfd = fopen((char *)p_hf, READBIN)) == NULL)
67: {
68: #if defined(MSDOS)
69: /*
70: * for MSDOS: try the DOS search path
71: */
72: fnamep = searchpath("vim_help.txt");
73: if (fnamep == NULL ||
74: (helpfd = fopen((char *)fnamep, READBIN)) == NULL)
75: {
76: smsg((char_u *)"Sorry, help file \"%s\" and \"vim_help.txt\" not found", p_hf);
77: goto erret;
78: }
79: #else
80: smsg((char_u *)"Sorry, help file \"%s\" not found", p_hf);
81: goto erret;
82: #endif
83: }
84: fclose(helpfd);
85:
86: if (win_split(0, FALSE) == FAIL)
87: goto erret;
88:
89: if (curwin->w_height < p_hh)
90: win_setheight((int)p_hh);
91:
92: #ifdef RIGHTLEFT
93: curwin->w_p_rl = 0; /* help window is left-to-right */
94: #endif
95: curwin->w_p_nu = 0; /* no line numbers */
96:
97: /*
98: * open help file (do_ecmd() will set b_help flag, readfile() will
99: * set b_p_ro flag)
100: */
101: (void)do_ecmd(0, fnamep, NULL, NULL, TRUE, (linenr_t)0, TRUE);
102:
103: /* save the values of the options we change */
104: vim_free(help_save_isk);
105: help_save_isk = strsave(curbuf->b_p_isk);
106: help_save_ts = curbuf->b_p_ts;
107:
108: /* accept all chars for keywords, except ' ', '*', '"', '|' */
109: set_string_option((char_u *)"isk", -1,
110: (char_u *)"!-~,^*,^|,^\"", TRUE);
111: curbuf->b_p_ts = 8;
112: check_buf_options(curbuf);
113: (void)init_chartab(); /* needed because 'isk' changed */
114: }
115: }
116:
117: restart_edit = 0; /* don't want insert mode in help file */
118:
119: stuffReadbuff((char_u *)":ta ");
120: if (arg != NULL && *arg != NUL)
121: stuffReadbuff(arg);
122: else
123: stuffReadbuff((char_u *)"vim_help.txt"); /* go to the index */
124: stuffcharReadbuff('\n');
125:
126: erret:
127: if (need_free)
128: vim_free(arg);
129: }
130:
131: /*
132: * Return a heuristic indicating how well the given string matches. The
133: * smaller the number, the better the match. This is the order of priorities,
134: * from best match to worst match:
135: * - Match with least alpha-numeric characters is better.
136: * - Match with least total characters is better.
137: * - Match towards the start is better.
138: * Assumption is made that the matched_string passed has already been found to
139: * match some string for which help is requested. webb.
140: */
141: int
142: help_heuristic(matched_string, offset)
143: char_u *matched_string;
144: int offset; /* offset for match */
145: {
146: int num_letters;
147: char_u *p;
148:
149: num_letters = 0;
150: for (p = matched_string; *p; p++)
151: if (isalnum(*p))
152: num_letters++;
153:
154: /*
155: * Multiply the number of letters by 100 to give it a much bigger
156: * weighting than the number of characters.
157: * If the match starts in the middle of a word, add 10000 to put it
158: * somewhere in the last half.
159: * If the match is more than 2 chars from the start, multiply by 200 to
160: * put it after matches at the start.
161: */
162: if (isalnum(matched_string[offset]) && offset > 0 &&
163: isalnum(matched_string[offset - 1]))
164: offset += 10000;
165: else if (offset > 2)
166: offset *= 200;
167: return (int)(100 * num_letters + STRLEN(matched_string) + offset);
168: }
169:
170: static int help_compare __ARGS((const void *s1, const void *s2));
171:
172: /*
173: * Compare functions for qsort() below, that checks the help heuristics number
174: * that has been put after the tagname by find_tags().
175: */
176: static int
177: help_compare(s1, s2)
178: const void *s1;
179: const void *s2;
180: {
181: char *p1;
182: char *p2;
183:
184: p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
185: p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
186: return strcmp(p1, p2);
187: }
188:
189: /*
190: * Find all help tags matching "arg", sort them and return in matches[], with
191: * the number of matches in num_matches.
192: * We try first with case, and then ignoring case. Then we try to choose the
193: * "best" match from the ones found.
194: */
195: int
196: find_help_tags(arg, num_matches, matches)
197: char_u *arg;
198: int *num_matches;
199: char_u ***matches;
200: {
201: char_u *s, *d;
202: regexp *prog;
203: int attempt;
204: int retval = FAIL;
205:
206: reg_magic = p_magic;
207: d = IObuff; /* assume IObuff is long enough! */
208:
209: /*
210: * Replace "|" with "bar", """ with "quote" and "*" with "star" to
211: * match the name of the tags for these commands.
212: * Replace "*" with ".*" and "?" with "." to match command line
213: * completion.
214: * Insert a backslash before '~', '$' and '.' to avoid their
215: * special meaning.
216: * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
217: * ":help i_^_CTRL-D" work.
218: * If tag starts with ', toss everything after a second '. Fixes
219: * CTRL-] on 'option'. (would include the trailing '.').
220: */
221: if (STRCMP(arg, "*") == 0 || STRCMP(arg, "[*") == 0 ||
222: STRCMP(arg, "]*") == 0)
223: {
224: if (*arg != '*')
225: *d++ = *arg;
226: STRCPY(d, "star");
227: d += 4;
228: }
229: else
230: {
231: for (s = arg; *s; ++s)
232: {
233: if (d - IObuff > IOSIZE - 10) /* getting too long!? */
234: break;
235: switch (*s)
236: {
237: case '|': STRCPY(d, "bar");
238: d += 3;
239: continue;
240: case '\"': STRCPY(d, "quote");
241: d += 5;
242: continue;
243: case '*': *d++ = '.';
244: break;
245: /* "?", ":?" and "?<CR>" are real tags */
246: case '?': if (arg[1] == NUL ||
247: STRCMP(arg, ":?") == 0 ||
248: STRCMP(arg, "?<CR>") == 0)
249: break;
250: *d++ = '.';
251: continue;
252: case '$':
253: case '.':
254: case '~': *d++ = '\\';
255: break;
256: }
257: if (*s < ' ' || (*s == '^' && s[1] && s[1] != '_')) /* ^x */
258: {
259: STRCPY(d, "CTRL-");
260: d += 5;
261: if (*s < ' ')
262: {
263: *d++ = *s + '@';
264: continue;
265: }
266: ++s;
267: }
268: else if (*s == '^') /* "^" or "CTRL-^" or "^_" */
269: *d++ = '\\';
270: *d++ = *s;
271: if (*s == '\'' && s > arg && *arg == '\'')
272: break;
273: }
274: }
275: *d = NUL;
276:
277: reg_ic = FALSE;
278: prog = vim_regcomp(IObuff);
279: if (prog == NULL)
280: return FAIL;
281:
282: /* First try to match with case, then without */
283: for (attempt = 0; attempt < 2; ++attempt, reg_ic = TRUE)
284: {
285: *matches = (char_u **)"";
286: *num_matches = 0;
287: retval = find_tags(NULL, prog, num_matches, matches, TRUE);
288: if (retval == FAIL || *num_matches)
289: break;
290: }
291: vim_free(prog);
292:
293: #ifdef HAVE_QSORT
294: /*
295: * Sort the matches found on the heuristic number that is after the
296: * tag name. If there is no qsort, the output will be messy!
297: */
298: qsort((void *)*matches, (size_t)*num_matches,
299: sizeof(char_u *), help_compare);
300: #endif
301: return OK;
302: }