Mercurial > vim
comparison src/ex_cmds.c @ 21423:5db63c2c6929 v8.2.1262
patch 8.2.1262: src/ex_cmds.c file is too big
Commit: https://github.com/vim/vim/commit/f868ba89039045b25efe83d12ca501d657e170e8
Author: Bram Moolenaar <Bram@vim.org>
Date: Tue Jul 21 21:07:20 2020 +0200
patch 8.2.1262: src/ex_cmds.c file is too big
Problem: src/ex_cmds.c file is too big.
Solution: Move help related code to src/help.c. (Yegappan Lakshmanan,
closes #6506)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Tue, 21 Jul 2020 21:15:06 +0200 |
parents | 9064044fd4f6 |
children | 4dfd00f481fb |
comparison
equal
deleted
inserted
replaced
21422:af29e2470306 | 21423:5db63c2c6929 |
---|---|
21 static int linelen(int *has_tab); | 21 static int linelen(int *has_tab); |
22 static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, int do_in, int do_out); | 22 static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, int do_in, int do_out); |
23 static int not_writing(void); | 23 static int not_writing(void); |
24 static int check_readonly(int *forceit, buf_T *buf); | 24 static int check_readonly(int *forceit, buf_T *buf); |
25 static void delbuf_msg(char_u *name); | 25 static void delbuf_msg(char_u *name); |
26 static int help_compare(const void *s1, const void *s2); | |
27 static void prepare_help_buffer(void); | |
28 | 26 |
29 /* | 27 /* |
30 * ":ascii" and "ga". | 28 * ":ascii" and "ga". |
31 */ | 29 */ |
32 void | 30 void |
5033 return FALSE; | 5031 return FALSE; |
5034 } | 5032 } |
5035 | 5033 |
5036 #endif | 5034 #endif |
5037 | 5035 |
5038 | |
5039 /* | |
5040 * ":help": open a read-only window on a help file | |
5041 */ | |
5042 void | |
5043 ex_help(exarg_T *eap) | |
5044 { | |
5045 char_u *arg; | |
5046 char_u *tag; | |
5047 FILE *helpfd; // file descriptor of help file | |
5048 int n; | |
5049 int i; | |
5050 win_T *wp; | |
5051 int num_matches; | |
5052 char_u **matches; | |
5053 char_u *p; | |
5054 int empty_fnum = 0; | |
5055 int alt_fnum = 0; | |
5056 buf_T *buf; | |
5057 #ifdef FEAT_MULTI_LANG | |
5058 int len; | |
5059 char_u *lang; | |
5060 #endif | |
5061 #ifdef FEAT_FOLDING | |
5062 int old_KeyTyped = KeyTyped; | |
5063 #endif | |
5064 | |
5065 if (eap != NULL) | |
5066 { | |
5067 /* | |
5068 * A ":help" command ends at the first LF, or at a '|' that is | |
5069 * followed by some text. Set nextcmd to the following command. | |
5070 */ | |
5071 for (arg = eap->arg; *arg; ++arg) | |
5072 { | |
5073 if (*arg == '\n' || *arg == '\r' | |
5074 || (*arg == '|' && arg[1] != NUL && arg[1] != '|')) | |
5075 { | |
5076 *arg++ = NUL; | |
5077 eap->nextcmd = arg; | |
5078 break; | |
5079 } | |
5080 } | |
5081 arg = eap->arg; | |
5082 | |
5083 if (eap->forceit && *arg == NUL && !curbuf->b_help) | |
5084 { | |
5085 emsg(_("E478: Don't panic!")); | |
5086 return; | |
5087 } | |
5088 | |
5089 if (eap->skip) // not executing commands | |
5090 return; | |
5091 } | |
5092 else | |
5093 arg = (char_u *)""; | |
5094 | |
5095 // remove trailing blanks | |
5096 p = arg + STRLEN(arg) - 1; | |
5097 while (p > arg && VIM_ISWHITE(*p) && p[-1] != '\\') | |
5098 *p-- = NUL; | |
5099 | |
5100 #ifdef FEAT_MULTI_LANG | |
5101 // Check for a specified language | |
5102 lang = check_help_lang(arg); | |
5103 #endif | |
5104 | |
5105 // When no argument given go to the index. | |
5106 if (*arg == NUL) | |
5107 arg = (char_u *)"help.txt"; | |
5108 | |
5109 /* | |
5110 * Check if there is a match for the argument. | |
5111 */ | |
5112 n = find_help_tags(arg, &num_matches, &matches, | |
5113 eap != NULL && eap->forceit); | |
5114 | |
5115 i = 0; | |
5116 #ifdef FEAT_MULTI_LANG | |
5117 if (n != FAIL && lang != NULL) | |
5118 // Find first item with the requested language. | |
5119 for (i = 0; i < num_matches; ++i) | |
5120 { | |
5121 len = (int)STRLEN(matches[i]); | |
5122 if (len > 3 && matches[i][len - 3] == '@' | |
5123 && STRICMP(matches[i] + len - 2, lang) == 0) | |
5124 break; | |
5125 } | |
5126 #endif | |
5127 if (i >= num_matches || n == FAIL) | |
5128 { | |
5129 #ifdef FEAT_MULTI_LANG | |
5130 if (lang != NULL) | |
5131 semsg(_("E661: Sorry, no '%s' help for %s"), lang, arg); | |
5132 else | |
5133 #endif | |
5134 semsg(_("E149: Sorry, no help for %s"), arg); | |
5135 if (n != FAIL) | |
5136 FreeWild(num_matches, matches); | |
5137 return; | |
5138 } | |
5139 | |
5140 // The first match (in the requested language) is the best match. | |
5141 tag = vim_strsave(matches[i]); | |
5142 FreeWild(num_matches, matches); | |
5143 | |
5144 #ifdef FEAT_GUI | |
5145 need_mouse_correct = TRUE; | |
5146 #endif | |
5147 | |
5148 /* | |
5149 * Re-use an existing help window or open a new one. | |
5150 * Always open a new one for ":tab help". | |
5151 */ | |
5152 if (!bt_help(curwin->w_buffer) || cmdmod.tab != 0) | |
5153 { | |
5154 if (cmdmod.tab != 0) | |
5155 wp = NULL; | |
5156 else | |
5157 FOR_ALL_WINDOWS(wp) | |
5158 if (bt_help(wp->w_buffer)) | |
5159 break; | |
5160 if (wp != NULL && wp->w_buffer->b_nwindows > 0) | |
5161 win_enter(wp, TRUE); | |
5162 else | |
5163 { | |
5164 /* | |
5165 * There is no help window yet. | |
5166 * Try to open the file specified by the "helpfile" option. | |
5167 */ | |
5168 if ((helpfd = mch_fopen((char *)p_hf, READBIN)) == NULL) | |
5169 { | |
5170 smsg(_("Sorry, help file \"%s\" not found"), p_hf); | |
5171 goto erret; | |
5172 } | |
5173 fclose(helpfd); | |
5174 | |
5175 // Split off help window; put it at far top if no position | |
5176 // specified, the current window is vertically split and | |
5177 // narrow. | |
5178 n = WSP_HELP; | |
5179 if (cmdmod.split == 0 && curwin->w_width != Columns | |
5180 && curwin->w_width < 80) | |
5181 n |= WSP_TOP; | |
5182 if (win_split(0, n) == FAIL) | |
5183 goto erret; | |
5184 | |
5185 if (curwin->w_height < p_hh) | |
5186 win_setheight((int)p_hh); | |
5187 | |
5188 /* | |
5189 * Open help file (do_ecmd() will set b_help flag, readfile() will | |
5190 * set b_p_ro flag). | |
5191 * Set the alternate file to the previously edited file. | |
5192 */ | |
5193 alt_fnum = curbuf->b_fnum; | |
5194 (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL, | |
5195 ECMD_HIDE + ECMD_SET_HELP, | |
5196 NULL); // buffer is still open, don't store info | |
5197 if (!cmdmod.keepalt) | |
5198 curwin->w_alt_fnum = alt_fnum; | |
5199 empty_fnum = curbuf->b_fnum; | |
5200 } | |
5201 } | |
5202 | |
5203 if (!p_im) | |
5204 restart_edit = 0; // don't want insert mode in help file | |
5205 | |
5206 #ifdef FEAT_FOLDING | |
5207 // Restore KeyTyped, setting 'filetype=help' may reset it. | |
5208 // It is needed for do_tag top open folds under the cursor. | |
5209 KeyTyped = old_KeyTyped; | |
5210 #endif | |
5211 | |
5212 if (tag != NULL) | |
5213 do_tag(tag, DT_HELP, 1, FALSE, TRUE); | |
5214 | |
5215 // Delete the empty buffer if we're not using it. Careful: autocommands | |
5216 // may have jumped to another window, check that the buffer is not in a | |
5217 // window. | |
5218 if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) | |
5219 { | |
5220 buf = buflist_findnr(empty_fnum); | |
5221 if (buf != NULL && buf->b_nwindows == 0) | |
5222 wipe_buffer(buf, TRUE); | |
5223 } | |
5224 | |
5225 // keep the previous alternate file | |
5226 if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum && !cmdmod.keepalt) | |
5227 curwin->w_alt_fnum = alt_fnum; | |
5228 | |
5229 erret: | |
5230 vim_free(tag); | |
5231 } | |
5232 | |
5233 /* | |
5234 * ":helpclose": Close one help window | |
5235 */ | |
5236 void | |
5237 ex_helpclose(exarg_T *eap UNUSED) | |
5238 { | |
5239 win_T *win; | |
5240 | |
5241 FOR_ALL_WINDOWS(win) | |
5242 { | |
5243 if (bt_help(win->w_buffer)) | |
5244 { | |
5245 win_close(win, FALSE); | |
5246 return; | |
5247 } | |
5248 } | |
5249 } | |
5250 | |
5251 #if defined(FEAT_MULTI_LANG) || defined(PROTO) | |
5252 /* | |
5253 * In an argument search for a language specifiers in the form "@xx". | |
5254 * Changes the "@" to NUL if found, and returns a pointer to "xx". | |
5255 * Returns NULL if not found. | |
5256 */ | |
5257 char_u * | |
5258 check_help_lang(char_u *arg) | |
5259 { | |
5260 int len = (int)STRLEN(arg); | |
5261 | |
5262 if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2]) | |
5263 && ASCII_ISALPHA(arg[len - 1])) | |
5264 { | |
5265 arg[len - 3] = NUL; // remove the '@' | |
5266 return arg + len - 2; | |
5267 } | |
5268 return NULL; | |
5269 } | |
5270 #endif | |
5271 | |
5272 /* | |
5273 * Return a heuristic indicating how well the given string matches. The | |
5274 * smaller the number, the better the match. This is the order of priorities, | |
5275 * from best match to worst match: | |
5276 * - Match with least alphanumeric characters is better. | |
5277 * - Match with least total characters is better. | |
5278 * - Match towards the start is better. | |
5279 * - Match starting with "+" is worse (feature instead of command) | |
5280 * Assumption is made that the matched_string passed has already been found to | |
5281 * match some string for which help is requested. webb. | |
5282 */ | |
5283 int | |
5284 help_heuristic( | |
5285 char_u *matched_string, | |
5286 int offset, // offset for match | |
5287 int wrong_case) // no matching case | |
5288 { | |
5289 int num_letters; | |
5290 char_u *p; | |
5291 | |
5292 num_letters = 0; | |
5293 for (p = matched_string; *p; p++) | |
5294 if (ASCII_ISALNUM(*p)) | |
5295 num_letters++; | |
5296 | |
5297 /* | |
5298 * Multiply the number of letters by 100 to give it a much bigger | |
5299 * weighting than the number of characters. | |
5300 * If there only is a match while ignoring case, add 5000. | |
5301 * If the match starts in the middle of a word, add 10000 to put it | |
5302 * somewhere in the last half. | |
5303 * If the match is more than 2 chars from the start, multiply by 200 to | |
5304 * put it after matches at the start. | |
5305 */ | |
5306 if (ASCII_ISALNUM(matched_string[offset]) && offset > 0 | |
5307 && ASCII_ISALNUM(matched_string[offset - 1])) | |
5308 offset += 10000; | |
5309 else if (offset > 2) | |
5310 offset *= 200; | |
5311 if (wrong_case) | |
5312 offset += 5000; | |
5313 // Features are less interesting than the subjects themselves, but "+" | |
5314 // alone is not a feature. | |
5315 if (matched_string[0] == '+' && matched_string[1] != NUL) | |
5316 offset += 100; | |
5317 return (int)(100 * num_letters + STRLEN(matched_string) + offset); | |
5318 } | |
5319 | |
5320 /* | |
5321 * Compare functions for qsort() below, that checks the help heuristics number | |
5322 * that has been put after the tagname by find_tags(). | |
5323 */ | |
5324 static int | |
5325 help_compare(const void *s1, const void *s2) | |
5326 { | |
5327 char *p1; | |
5328 char *p2; | |
5329 int cmp; | |
5330 | |
5331 p1 = *(char **)s1 + strlen(*(char **)s1) + 1; | |
5332 p2 = *(char **)s2 + strlen(*(char **)s2) + 1; | |
5333 | |
5334 // Compare by help heuristic number first. | |
5335 cmp = strcmp(p1, p2); | |
5336 if (cmp != 0) | |
5337 return cmp; | |
5338 | |
5339 // Compare by strings as tie-breaker when same heuristic number. | |
5340 return strcmp(*(char **)s1, *(char **)s2); | |
5341 } | |
5342 | |
5343 /* | |
5344 * Find all help tags matching "arg", sort them and return in matches[], with | |
5345 * the number of matches in num_matches. | |
5346 * The matches will be sorted with a "best" match algorithm. | |
5347 * When "keep_lang" is TRUE try keeping the language of the current buffer. | |
5348 */ | |
5349 int | |
5350 find_help_tags( | |
5351 char_u *arg, | |
5352 int *num_matches, | |
5353 char_u ***matches, | |
5354 int keep_lang) | |
5355 { | |
5356 char_u *s, *d; | |
5357 int i; | |
5358 static char *(mtable[]) = {"*", "g*", "[*", "]*", ":*", | |
5359 "/*", "/\\*", "\"*", "**", | |
5360 "cpo-*", "/\\(\\)", "/\\%(\\)", | |
5361 "?", ":?", "?<CR>", "g?", "g?g?", "g??", | |
5362 "-?", "q?", "v_g?", | |
5363 "/\\?", "/\\z(\\)", "\\=", ":s\\=", | |
5364 "[count]", "[quotex]", | |
5365 "[range]", ":[range]", | |
5366 "[pattern]", "\\|", "\\%$", | |
5367 "s/\\~", "s/\\U", "s/\\L", | |
5368 "s/\\1", "s/\\2", "s/\\3", "s/\\9"}; | |
5369 static char *(rtable[]) = {"star", "gstar", "[star", "]star", ":star", | |
5370 "/star", "/\\\\star", "quotestar", "starstar", | |
5371 "cpo-star", "/\\\\(\\\\)", "/\\\\%(\\\\)", | |
5372 "?", ":?", "?<CR>", "g?", "g?g?", "g??", | |
5373 "-?", "q?", "v_g?", | |
5374 "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", | |
5375 "\\[count]", "\\[quotex]", | |
5376 "\\[range]", ":\\[range]", | |
5377 "\\[pattern]", "\\\\bar", "/\\\\%\\$", | |
5378 "s/\\\\\\~", "s/\\\\U", "s/\\\\L", | |
5379 "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"}; | |
5380 static char *(expr_table[]) = {"!=?", "!~?", "<=?", "<?", "==?", "=~?", | |
5381 ">=?", ">?", "is?", "isnot?"}; | |
5382 int flags; | |
5383 | |
5384 d = IObuff; // assume IObuff is long enough! | |
5385 | |
5386 if (STRNICMP(arg, "expr-", 5) == 0) | |
5387 { | |
5388 // When the string starting with "expr-" and containing '?' and matches | |
5389 // the table, it is taken literally (but ~ is escaped). Otherwise '?' | |
5390 // is recognized as a wildcard. | |
5391 for (i = (int)(sizeof(expr_table) / sizeof(char *)); --i >= 0; ) | |
5392 if (STRCMP(arg + 5, expr_table[i]) == 0) | |
5393 { | |
5394 int si = 0, di = 0; | |
5395 | |
5396 for (;;) | |
5397 { | |
5398 if (arg[si] == '~') | |
5399 d[di++] = '\\'; | |
5400 d[di++] = arg[si]; | |
5401 if (arg[si] == NUL) | |
5402 break; | |
5403 ++si; | |
5404 } | |
5405 break; | |
5406 } | |
5407 } | |
5408 else | |
5409 { | |
5410 // Recognize a few exceptions to the rule. Some strings that contain | |
5411 // '*' with "star". Otherwise '*' is recognized as a wildcard. | |
5412 for (i = (int)(sizeof(mtable) / sizeof(char *)); --i >= 0; ) | |
5413 if (STRCMP(arg, mtable[i]) == 0) | |
5414 { | |
5415 STRCPY(d, rtable[i]); | |
5416 break; | |
5417 } | |
5418 } | |
5419 | |
5420 if (i < 0) // no match in table | |
5421 { | |
5422 // Replace "\S" with "/\\S", etc. Otherwise every tag is matched. | |
5423 // Also replace "\%^" and "\%(", they match every tag too. | |
5424 // Also "\zs", "\z1", etc. | |
5425 // Also "\@<", "\@=", "\@<=", etc. | |
5426 // And also "\_$" and "\_^". | |
5427 if (arg[0] == '\\' | |
5428 && ((arg[1] != NUL && arg[2] == NUL) | |
5429 || (vim_strchr((char_u *)"%_z@", arg[1]) != NULL | |
5430 && arg[2] != NUL))) | |
5431 { | |
5432 STRCPY(d, "/\\\\"); | |
5433 STRCPY(d + 3, arg + 1); | |
5434 // Check for "/\\_$", should be "/\\_\$" | |
5435 if (d[3] == '_' && d[4] == '$') | |
5436 STRCPY(d + 4, "\\$"); | |
5437 } | |
5438 else | |
5439 { | |
5440 // Replace: | |
5441 // "[:...:]" with "\[:...:]" | |
5442 // "[++...]" with "\[++...]" | |
5443 // "\{" with "\\{" -- matching "} \}" | |
5444 if ((arg[0] == '[' && (arg[1] == ':' | |
5445 || (arg[1] == '+' && arg[2] == '+'))) | |
5446 || (arg[0] == '\\' && arg[1] == '{')) | |
5447 *d++ = '\\'; | |
5448 | |
5449 /* | |
5450 * If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'. | |
5451 */ | |
5452 if (*arg == '(' && arg[1] == '\'') | |
5453 arg++; | |
5454 for (s = arg; *s; ++s) | |
5455 { | |
5456 /* | |
5457 * Replace "|" with "bar" and '"' with "quote" to match the name of | |
5458 * the tags for these commands. | |
5459 * Replace "*" with ".*" and "?" with "." to match command line | |
5460 * completion. | |
5461 * Insert a backslash before '~', '$' and '.' to avoid their | |
5462 * special meaning. | |
5463 */ | |
5464 if (d - IObuff > IOSIZE - 10) // getting too long!? | |
5465 break; | |
5466 switch (*s) | |
5467 { | |
5468 case '|': STRCPY(d, "bar"); | |
5469 d += 3; | |
5470 continue; | |
5471 case '"': STRCPY(d, "quote"); | |
5472 d += 5; | |
5473 continue; | |
5474 case '*': *d++ = '.'; | |
5475 break; | |
5476 case '?': *d++ = '.'; | |
5477 continue; | |
5478 case '$': | |
5479 case '.': | |
5480 case '~': *d++ = '\\'; | |
5481 break; | |
5482 } | |
5483 | |
5484 /* | |
5485 * Replace "^x" by "CTRL-X". Don't do this for "^_" to make | |
5486 * ":help i_^_CTRL-D" work. | |
5487 * Insert '-' before and after "CTRL-X" when applicable. | |
5488 */ | |
5489 if (*s < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1]) | |
5490 || vim_strchr((char_u *)"?@[\\]^", s[1]) != NULL))) | |
5491 { | |
5492 if (d > IObuff && d[-1] != '_' && d[-1] != '\\') | |
5493 *d++ = '_'; // prepend a '_' to make x_CTRL-x | |
5494 STRCPY(d, "CTRL-"); | |
5495 d += 5; | |
5496 if (*s < ' ') | |
5497 { | |
5498 #ifdef EBCDIC | |
5499 *d++ = CtrlChar(*s); | |
5500 #else | |
5501 *d++ = *s + '@'; | |
5502 #endif | |
5503 if (d[-1] == '\\') | |
5504 *d++ = '\\'; // double a backslash | |
5505 } | |
5506 else | |
5507 *d++ = *++s; | |
5508 if (s[1] != NUL && s[1] != '_') | |
5509 *d++ = '_'; // append a '_' | |
5510 continue; | |
5511 } | |
5512 else if (*s == '^') // "^" or "CTRL-^" or "^_" | |
5513 *d++ = '\\'; | |
5514 | |
5515 /* | |
5516 * Insert a backslash before a backslash after a slash, for search | |
5517 * pattern tags: "/\|" --> "/\\|". | |
5518 */ | |
5519 else if (s[0] == '\\' && s[1] != '\\' | |
5520 && *arg == '/' && s == arg + 1) | |
5521 *d++ = '\\'; | |
5522 | |
5523 // "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in | |
5524 // "CTRL-\_CTRL-N" | |
5525 if (STRNICMP(s, "CTRL-\\_", 7) == 0) | |
5526 { | |
5527 STRCPY(d, "CTRL-\\\\"); | |
5528 d += 7; | |
5529 s += 6; | |
5530 } | |
5531 | |
5532 *d++ = *s; | |
5533 | |
5534 /* | |
5535 * If tag contains "({" or "([", tag terminates at the "(". | |
5536 * This is for help on functions, e.g.: abs({expr}). | |
5537 */ | |
5538 if (*s == '(' && (s[1] == '{' || s[1] =='[')) | |
5539 break; | |
5540 | |
5541 /* | |
5542 * If tag starts with ', toss everything after a second '. Fixes | |
5543 * CTRL-] on 'option'. (would include the trailing '.'). | |
5544 */ | |
5545 if (*s == '\'' && s > arg && *arg == '\'') | |
5546 break; | |
5547 // Also '{' and '}'. | |
5548 if (*s == '}' && s > arg && *arg == '{') | |
5549 break; | |
5550 } | |
5551 *d = NUL; | |
5552 | |
5553 if (*IObuff == '`') | |
5554 { | |
5555 if (d > IObuff + 2 && d[-1] == '`') | |
5556 { | |
5557 // remove the backticks from `command` | |
5558 mch_memmove(IObuff, IObuff + 1, STRLEN(IObuff)); | |
5559 d[-2] = NUL; | |
5560 } | |
5561 else if (d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') | |
5562 { | |
5563 // remove the backticks and comma from `command`, | |
5564 mch_memmove(IObuff, IObuff + 1, STRLEN(IObuff)); | |
5565 d[-3] = NUL; | |
5566 } | |
5567 else if (d > IObuff + 4 && d[-3] == '`' | |
5568 && d[-2] == '\\' && d[-1] == '.') | |
5569 { | |
5570 // remove the backticks and dot from `command`\. | |
5571 mch_memmove(IObuff, IObuff + 1, STRLEN(IObuff)); | |
5572 d[-4] = NUL; | |
5573 } | |
5574 } | |
5575 } | |
5576 } | |
5577 | |
5578 *matches = (char_u **)""; | |
5579 *num_matches = 0; | |
5580 flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE | TAG_NO_TAGFUNC; | |
5581 if (keep_lang) | |
5582 flags |= TAG_KEEP_LANG; | |
5583 if (find_tags(IObuff, num_matches, matches, flags, (int)MAXCOL, NULL) == OK | |
5584 && *num_matches > 0) | |
5585 { | |
5586 // Sort the matches found on the heuristic number that is after the | |
5587 // tag name. | |
5588 qsort((void *)*matches, (size_t)*num_matches, | |
5589 sizeof(char_u *), help_compare); | |
5590 // Delete more than TAG_MANY to reduce the size of the listing. | |
5591 while (*num_matches > TAG_MANY) | |
5592 vim_free((*matches)[--*num_matches]); | |
5593 } | |
5594 return OK; | |
5595 } | |
5596 | |
5597 /* | |
5598 * Called when starting to edit a buffer for a help file. | |
5599 */ | |
5600 static void | |
5601 prepare_help_buffer(void) | |
5602 { | |
5603 char_u *p; | |
5604 | |
5605 curbuf->b_help = TRUE; | |
5606 #ifdef FEAT_QUICKFIX | |
5607 set_string_option_direct((char_u *)"buftype", -1, | |
5608 (char_u *)"help", OPT_FREE|OPT_LOCAL, 0); | |
5609 #endif | |
5610 | |
5611 /* | |
5612 * Always set these options after jumping to a help tag, because the | |
5613 * user may have an autocommand that gets in the way. | |
5614 * Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and | |
5615 * latin1 word characters (for translated help files). | |
5616 * Only set it when needed, buf_init_chartab() is some work. | |
5617 */ | |
5618 p = | |
5619 #ifdef EBCDIC | |
5620 (char_u *)"65-255,^*,^|,^\""; | |
5621 #else | |
5622 (char_u *)"!-~,^*,^|,^\",192-255"; | |
5623 #endif | |
5624 if (STRCMP(curbuf->b_p_isk, p) != 0) | |
5625 { | |
5626 set_string_option_direct((char_u *)"isk", -1, p, OPT_FREE|OPT_LOCAL, 0); | |
5627 check_buf_options(curbuf); | |
5628 (void)buf_init_chartab(curbuf, FALSE); | |
5629 } | |
5630 | |
5631 #ifdef FEAT_FOLDING | |
5632 // Don't use the global foldmethod. | |
5633 set_string_option_direct((char_u *)"fdm", -1, (char_u *)"manual", | |
5634 OPT_FREE|OPT_LOCAL, 0); | |
5635 #endif | |
5636 | |
5637 curbuf->b_p_ts = 8; // 'tabstop' is 8 | |
5638 curwin->w_p_list = FALSE; // no list mode | |
5639 | |
5640 curbuf->b_p_ma = FALSE; // not modifiable | |
5641 curbuf->b_p_bin = FALSE; // reset 'bin' before reading file | |
5642 curwin->w_p_nu = 0; // no line numbers | |
5643 curwin->w_p_rnu = 0; // no relative line numbers | |
5644 RESET_BINDING(curwin); // no scroll or cursor binding | |
5645 #ifdef FEAT_ARABIC | |
5646 curwin->w_p_arab = FALSE; // no arabic mode | |
5647 #endif | |
5648 #ifdef FEAT_RIGHTLEFT | |
5649 curwin->w_p_rl = FALSE; // help window is left-to-right | |
5650 #endif | |
5651 #ifdef FEAT_FOLDING | |
5652 curwin->w_p_fen = FALSE; // No folding in the help window | |
5653 #endif | |
5654 #ifdef FEAT_DIFF | |
5655 curwin->w_p_diff = FALSE; // No 'diff' | |
5656 #endif | |
5657 #ifdef FEAT_SPELL | |
5658 curwin->w_p_spell = FALSE; // No spell checking | |
5659 #endif | |
5660 | |
5661 set_buflisted(FALSE); | |
5662 } | |
5663 | |
5664 /* | |
5665 * After reading a help file: May cleanup a help buffer when syntax | |
5666 * highlighting is not used. | |
5667 */ | |
5668 void | |
5669 fix_help_buffer(void) | |
5670 { | |
5671 linenr_T lnum; | |
5672 char_u *line; | |
5673 int in_example = FALSE; | |
5674 int len; | |
5675 char_u *fname; | |
5676 char_u *p; | |
5677 char_u *rt; | |
5678 int mustfree; | |
5679 | |
5680 // Set filetype to "help" if still needed. | |
5681 if (STRCMP(curbuf->b_p_ft, "help") != 0) | |
5682 { | |
5683 ++curbuf_lock; | |
5684 set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL); | |
5685 --curbuf_lock; | |
5686 } | |
5687 | |
5688 #ifdef FEAT_SYN_HL | |
5689 if (!syntax_present(curwin)) | |
5690 #endif | |
5691 { | |
5692 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) | |
5693 { | |
5694 line = ml_get_buf(curbuf, lnum, FALSE); | |
5695 len = (int)STRLEN(line); | |
5696 if (in_example && len > 0 && !VIM_ISWHITE(line[0])) | |
5697 { | |
5698 // End of example: non-white or '<' in first column. | |
5699 if (line[0] == '<') | |
5700 { | |
5701 // blank-out a '<' in the first column | |
5702 line = ml_get_buf(curbuf, lnum, TRUE); | |
5703 line[0] = ' '; | |
5704 } | |
5705 in_example = FALSE; | |
5706 } | |
5707 if (!in_example && len > 0) | |
5708 { | |
5709 if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) | |
5710 { | |
5711 // blank-out a '>' in the last column (start of example) | |
5712 line = ml_get_buf(curbuf, lnum, TRUE); | |
5713 line[len - 1] = ' '; | |
5714 in_example = TRUE; | |
5715 } | |
5716 else if (line[len - 1] == '~') | |
5717 { | |
5718 // blank-out a '~' at the end of line (header marker) | |
5719 line = ml_get_buf(curbuf, lnum, TRUE); | |
5720 line[len - 1] = ' '; | |
5721 } | |
5722 } | |
5723 } | |
5724 } | |
5725 | |
5726 /* | |
5727 * In the "help.txt" and "help.abx" file, add the locally added help | |
5728 * files. This uses the very first line in the help file. | |
5729 */ | |
5730 fname = gettail(curbuf->b_fname); | |
5731 if (fnamecmp(fname, "help.txt") == 0 | |
5732 #ifdef FEAT_MULTI_LANG | |
5733 || (fnamencmp(fname, "help.", 5) == 0 | |
5734 && ASCII_ISALPHA(fname[5]) | |
5735 && ASCII_ISALPHA(fname[6]) | |
5736 && TOLOWER_ASC(fname[7]) == 'x' | |
5737 && fname[8] == NUL) | |
5738 #endif | |
5739 ) | |
5740 { | |
5741 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum) | |
5742 { | |
5743 line = ml_get_buf(curbuf, lnum, FALSE); | |
5744 if (strstr((char *)line, "*local-additions*") == NULL) | |
5745 continue; | |
5746 | |
5747 // Go through all directories in 'runtimepath', skipping | |
5748 // $VIMRUNTIME. | |
5749 p = p_rtp; | |
5750 while (*p != NUL) | |
5751 { | |
5752 copy_option_part(&p, NameBuff, MAXPATHL, ","); | |
5753 mustfree = FALSE; | |
5754 rt = vim_getenv((char_u *)"VIMRUNTIME", &mustfree); | |
5755 if (rt != NULL && | |
5756 fullpathcmp(rt, NameBuff, FALSE, TRUE) != FPC_SAME) | |
5757 { | |
5758 int fcount; | |
5759 char_u **fnames; | |
5760 FILE *fd; | |
5761 char_u *s; | |
5762 int fi; | |
5763 vimconv_T vc; | |
5764 char_u *cp; | |
5765 | |
5766 // Find all "doc/ *.txt" files in this directory. | |
5767 add_pathsep(NameBuff); | |
5768 #ifdef FEAT_MULTI_LANG | |
5769 STRCAT(NameBuff, "doc/*.??[tx]"); | |
5770 #else | |
5771 STRCAT(NameBuff, "doc/*.txt"); | |
5772 #endif | |
5773 if (gen_expand_wildcards(1, &NameBuff, &fcount, | |
5774 &fnames, EW_FILE|EW_SILENT) == OK | |
5775 && fcount > 0) | |
5776 { | |
5777 #ifdef FEAT_MULTI_LANG | |
5778 int i1, i2; | |
5779 char_u *f1, *f2; | |
5780 char_u *t1, *t2; | |
5781 char_u *e1, *e2; | |
5782 | |
5783 // If foo.abx is found use it instead of foo.txt in | |
5784 // the same directory. | |
5785 for (i1 = 0; i1 < fcount; ++i1) | |
5786 { | |
5787 for (i2 = 0; i2 < fcount; ++i2) | |
5788 { | |
5789 if (i1 == i2) | |
5790 continue; | |
5791 if (fnames[i1] == NULL || fnames[i2] == NULL) | |
5792 continue; | |
5793 f1 = fnames[i1]; | |
5794 f2 = fnames[i2]; | |
5795 t1 = gettail(f1); | |
5796 t2 = gettail(f2); | |
5797 e1 = vim_strrchr(t1, '.'); | |
5798 e2 = vim_strrchr(t2, '.'); | |
5799 if (e1 == NULL || e2 == NULL) | |
5800 continue; | |
5801 if (fnamecmp(e1, ".txt") != 0 | |
5802 && fnamecmp(e1, fname + 4) != 0) | |
5803 { | |
5804 // Not .txt and not .abx, remove it. | |
5805 VIM_CLEAR(fnames[i1]); | |
5806 continue; | |
5807 } | |
5808 if (e1 - f1 != e2 - f2 | |
5809 || fnamencmp(f1, f2, e1 - f1) != 0) | |
5810 continue; | |
5811 if (fnamecmp(e1, ".txt") == 0 | |
5812 && fnamecmp(e2, fname + 4) == 0) | |
5813 // use .abx instead of .txt | |
5814 VIM_CLEAR(fnames[i1]); | |
5815 } | |
5816 } | |
5817 #endif | |
5818 for (fi = 0; fi < fcount; ++fi) | |
5819 { | |
5820 if (fnames[fi] == NULL) | |
5821 continue; | |
5822 fd = mch_fopen((char *)fnames[fi], "r"); | |
5823 if (fd != NULL) | |
5824 { | |
5825 vim_fgets(IObuff, IOSIZE, fd); | |
5826 if (IObuff[0] == '*' | |
5827 && (s = vim_strchr(IObuff + 1, '*')) | |
5828 != NULL) | |
5829 { | |
5830 int this_utf = MAYBE; | |
5831 | |
5832 // Change tag definition to a | |
5833 // reference and remove <CR>/<NL>. | |
5834 IObuff[0] = '|'; | |
5835 *s = '|'; | |
5836 while (*s != NUL) | |
5837 { | |
5838 if (*s == '\r' || *s == '\n') | |
5839 *s = NUL; | |
5840 // The text is utf-8 when a byte | |
5841 // above 127 is found and no | |
5842 // illegal byte sequence is found. | |
5843 if (*s >= 0x80 && this_utf != FALSE) | |
5844 { | |
5845 int l; | |
5846 | |
5847 this_utf = TRUE; | |
5848 l = utf_ptr2len(s); | |
5849 if (l == 1) | |
5850 this_utf = FALSE; | |
5851 s += l - 1; | |
5852 } | |
5853 ++s; | |
5854 } | |
5855 | |
5856 // The help file is latin1 or utf-8; | |
5857 // conversion to the current | |
5858 // 'encoding' may be required. | |
5859 vc.vc_type = CONV_NONE; | |
5860 convert_setup(&vc, (char_u *)( | |
5861 this_utf == TRUE ? "utf-8" | |
5862 : "latin1"), p_enc); | |
5863 if (vc.vc_type == CONV_NONE) | |
5864 // No conversion needed. | |
5865 cp = IObuff; | |
5866 else | |
5867 { | |
5868 // Do the conversion. If it fails | |
5869 // use the unconverted text. | |
5870 cp = string_convert(&vc, IObuff, | |
5871 NULL); | |
5872 if (cp == NULL) | |
5873 cp = IObuff; | |
5874 } | |
5875 convert_setup(&vc, NULL, NULL); | |
5876 | |
5877 ml_append(lnum, cp, (colnr_T)0, FALSE); | |
5878 if (cp != IObuff) | |
5879 vim_free(cp); | |
5880 ++lnum; | |
5881 } | |
5882 fclose(fd); | |
5883 } | |
5884 } | |
5885 FreeWild(fcount, fnames); | |
5886 } | |
5887 } | |
5888 if (mustfree) | |
5889 vim_free(rt); | |
5890 } | |
5891 break; | |
5892 } | |
5893 } | |
5894 } | |
5895 | |
5896 /* | |
5897 * ":exusage" | |
5898 */ | |
5899 void | |
5900 ex_exusage(exarg_T *eap UNUSED) | |
5901 { | |
5902 do_cmdline_cmd((char_u *)"help ex-cmd-index"); | |
5903 } | |
5904 | |
5905 /* | |
5906 * ":viusage" | |
5907 */ | |
5908 void | |
5909 ex_viusage(exarg_T *eap UNUSED) | |
5910 { | |
5911 do_cmdline_cmd((char_u *)"help normal-index"); | |
5912 } | |
5913 | |
5914 /* | |
5915 * Generate tags in one help directory. | |
5916 */ | |
5917 static void | |
5918 helptags_one( | |
5919 char_u *dir, // doc directory | |
5920 char_u *ext, // suffix, ".txt", ".itx", ".frx", etc. | |
5921 char_u *tagfname, // "tags" for English, "tags-fr" for French. | |
5922 int add_help_tags, // add "help-tags" tag | |
5923 int ignore_writeerr) // ignore write error | |
5924 { | |
5925 FILE *fd_tags; | |
5926 FILE *fd; | |
5927 garray_T ga; | |
5928 int filecount; | |
5929 char_u **files; | |
5930 char_u *p1, *p2; | |
5931 int fi; | |
5932 char_u *s; | |
5933 int i; | |
5934 char_u *fname; | |
5935 int dirlen; | |
5936 int utf8 = MAYBE; | |
5937 int this_utf8; | |
5938 int firstline; | |
5939 int mix = FALSE; // detected mixed encodings | |
5940 | |
5941 /* | |
5942 * Find all *.txt files. | |
5943 */ | |
5944 dirlen = (int)STRLEN(dir); | |
5945 STRCPY(NameBuff, dir); | |
5946 STRCAT(NameBuff, "/**/*"); | |
5947 STRCAT(NameBuff, ext); | |
5948 if (gen_expand_wildcards(1, &NameBuff, &filecount, &files, | |
5949 EW_FILE|EW_SILENT) == FAIL | |
5950 || filecount == 0) | |
5951 { | |
5952 if (!got_int) | |
5953 semsg(_("E151: No match: %s"), NameBuff); | |
5954 return; | |
5955 } | |
5956 | |
5957 /* | |
5958 * Open the tags file for writing. | |
5959 * Do this before scanning through all the files. | |
5960 */ | |
5961 STRCPY(NameBuff, dir); | |
5962 add_pathsep(NameBuff); | |
5963 STRCAT(NameBuff, tagfname); | |
5964 fd_tags = mch_fopen((char *)NameBuff, "w"); | |
5965 if (fd_tags == NULL) | |
5966 { | |
5967 if (!ignore_writeerr) | |
5968 semsg(_("E152: Cannot open %s for writing"), NameBuff); | |
5969 FreeWild(filecount, files); | |
5970 return; | |
5971 } | |
5972 | |
5973 /* | |
5974 * If using the "++t" argument or generating tags for "$VIMRUNTIME/doc" | |
5975 * add the "help-tags" tag. | |
5976 */ | |
5977 ga_init2(&ga, (int)sizeof(char_u *), 100); | |
5978 if (add_help_tags || fullpathcmp((char_u *)"$VIMRUNTIME/doc", | |
5979 dir, FALSE, TRUE) == FPC_SAME) | |
5980 { | |
5981 if (ga_grow(&ga, 1) == FAIL) | |
5982 got_int = TRUE; | |
5983 else | |
5984 { | |
5985 s = alloc(18 + (unsigned)STRLEN(tagfname)); | |
5986 if (s == NULL) | |
5987 got_int = TRUE; | |
5988 else | |
5989 { | |
5990 sprintf((char *)s, "help-tags\t%s\t1\n", tagfname); | |
5991 ((char_u **)ga.ga_data)[ga.ga_len] = s; | |
5992 ++ga.ga_len; | |
5993 } | |
5994 } | |
5995 } | |
5996 | |
5997 /* | |
5998 * Go over all the files and extract the tags. | |
5999 */ | |
6000 for (fi = 0; fi < filecount && !got_int; ++fi) | |
6001 { | |
6002 fd = mch_fopen((char *)files[fi], "r"); | |
6003 if (fd == NULL) | |
6004 { | |
6005 semsg(_("E153: Unable to open %s for reading"), files[fi]); | |
6006 continue; | |
6007 } | |
6008 fname = files[fi] + dirlen + 1; | |
6009 | |
6010 firstline = TRUE; | |
6011 while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) | |
6012 { | |
6013 if (firstline) | |
6014 { | |
6015 // Detect utf-8 file by a non-ASCII char in the first line. | |
6016 this_utf8 = MAYBE; | |
6017 for (s = IObuff; *s != NUL; ++s) | |
6018 if (*s >= 0x80) | |
6019 { | |
6020 int l; | |
6021 | |
6022 this_utf8 = TRUE; | |
6023 l = utf_ptr2len(s); | |
6024 if (l == 1) | |
6025 { | |
6026 // Illegal UTF-8 byte sequence. | |
6027 this_utf8 = FALSE; | |
6028 break; | |
6029 } | |
6030 s += l - 1; | |
6031 } | |
6032 if (this_utf8 == MAYBE) // only ASCII characters found | |
6033 this_utf8 = FALSE; | |
6034 if (utf8 == MAYBE) // first file | |
6035 utf8 = this_utf8; | |
6036 else if (utf8 != this_utf8) | |
6037 { | |
6038 semsg(_("E670: Mix of help file encodings within a language: %s"), files[fi]); | |
6039 mix = !got_int; | |
6040 got_int = TRUE; | |
6041 } | |
6042 firstline = FALSE; | |
6043 } | |
6044 p1 = vim_strchr(IObuff, '*'); // find first '*' | |
6045 while (p1 != NULL) | |
6046 { | |
6047 // Use vim_strbyte() instead of vim_strchr() so that when | |
6048 // 'encoding' is dbcs it still works, don't find '*' in the | |
6049 // second byte. | |
6050 p2 = vim_strbyte(p1 + 1, '*'); // find second '*' | |
6051 if (p2 != NULL && p2 > p1 + 1) // skip "*" and "**" | |
6052 { | |
6053 for (s = p1 + 1; s < p2; ++s) | |
6054 if (*s == ' ' || *s == '\t' || *s == '|') | |
6055 break; | |
6056 | |
6057 /* | |
6058 * Only accept a *tag* when it consists of valid | |
6059 * characters, there is white space before it and is | |
6060 * followed by a white character or end-of-line. | |
6061 */ | |
6062 if (s == p2 | |
6063 && (p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t') | |
6064 && (vim_strchr((char_u *)" \t\n\r", s[1]) != NULL | |
6065 || s[1] == '\0')) | |
6066 { | |
6067 *p2 = '\0'; | |
6068 ++p1; | |
6069 if (ga_grow(&ga, 1) == FAIL) | |
6070 { | |
6071 got_int = TRUE; | |
6072 break; | |
6073 } | |
6074 s = alloc(p2 - p1 + STRLEN(fname) + 2); | |
6075 if (s == NULL) | |
6076 { | |
6077 got_int = TRUE; | |
6078 break; | |
6079 } | |
6080 ((char_u **)ga.ga_data)[ga.ga_len] = s; | |
6081 ++ga.ga_len; | |
6082 sprintf((char *)s, "%s\t%s", p1, fname); | |
6083 | |
6084 // find next '*' | |
6085 p2 = vim_strchr(p2 + 1, '*'); | |
6086 } | |
6087 } | |
6088 p1 = p2; | |
6089 } | |
6090 line_breakcheck(); | |
6091 } | |
6092 | |
6093 fclose(fd); | |
6094 } | |
6095 | |
6096 FreeWild(filecount, files); | |
6097 | |
6098 if (!got_int) | |
6099 { | |
6100 /* | |
6101 * Sort the tags. | |
6102 */ | |
6103 if (ga.ga_data != NULL) | |
6104 sort_strings((char_u **)ga.ga_data, ga.ga_len); | |
6105 | |
6106 /* | |
6107 * Check for duplicates. | |
6108 */ | |
6109 for (i = 1; i < ga.ga_len; ++i) | |
6110 { | |
6111 p1 = ((char_u **)ga.ga_data)[i - 1]; | |
6112 p2 = ((char_u **)ga.ga_data)[i]; | |
6113 while (*p1 == *p2) | |
6114 { | |
6115 if (*p2 == '\t') | |
6116 { | |
6117 *p2 = NUL; | |
6118 vim_snprintf((char *)NameBuff, MAXPATHL, | |
6119 _("E154: Duplicate tag \"%s\" in file %s/%s"), | |
6120 ((char_u **)ga.ga_data)[i], dir, p2 + 1); | |
6121 emsg((char *)NameBuff); | |
6122 *p2 = '\t'; | |
6123 break; | |
6124 } | |
6125 ++p1; | |
6126 ++p2; | |
6127 } | |
6128 } | |
6129 | |
6130 if (utf8 == TRUE) | |
6131 fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n"); | |
6132 | |
6133 /* | |
6134 * Write the tags into the file. | |
6135 */ | |
6136 for (i = 0; i < ga.ga_len; ++i) | |
6137 { | |
6138 s = ((char_u **)ga.ga_data)[i]; | |
6139 if (STRNCMP(s, "help-tags\t", 10) == 0) | |
6140 // help-tags entry was added in formatted form | |
6141 fputs((char *)s, fd_tags); | |
6142 else | |
6143 { | |
6144 fprintf(fd_tags, "%s\t/*", s); | |
6145 for (p1 = s; *p1 != '\t'; ++p1) | |
6146 { | |
6147 // insert backslash before '\\' and '/' | |
6148 if (*p1 == '\\' || *p1 == '/') | |
6149 putc('\\', fd_tags); | |
6150 putc(*p1, fd_tags); | |
6151 } | |
6152 fprintf(fd_tags, "*\n"); | |
6153 } | |
6154 } | |
6155 } | |
6156 if (mix) | |
6157 got_int = FALSE; // continue with other languages | |
6158 | |
6159 for (i = 0; i < ga.ga_len; ++i) | |
6160 vim_free(((char_u **)ga.ga_data)[i]); | |
6161 ga_clear(&ga); | |
6162 fclose(fd_tags); // there is no check for an error... | |
6163 } | |
6164 | |
6165 /* | |
6166 * Generate tags in one help directory, taking care of translations. | |
6167 */ | |
6168 static void | |
6169 do_helptags(char_u *dirname, int add_help_tags, int ignore_writeerr) | |
6170 { | |
6171 #ifdef FEAT_MULTI_LANG | |
6172 int len; | |
6173 int i, j; | |
6174 garray_T ga; | |
6175 char_u lang[2]; | |
6176 char_u ext[5]; | |
6177 char_u fname[8]; | |
6178 int filecount; | |
6179 char_u **files; | |
6180 | |
6181 // Get a list of all files in the help directory and in subdirectories. | |
6182 STRCPY(NameBuff, dirname); | |
6183 add_pathsep(NameBuff); | |
6184 STRCAT(NameBuff, "**"); | |
6185 if (gen_expand_wildcards(1, &NameBuff, &filecount, &files, | |
6186 EW_FILE|EW_SILENT) == FAIL | |
6187 || filecount == 0) | |
6188 { | |
6189 semsg(_("E151: No match: %s"), NameBuff); | |
6190 return; | |
6191 } | |
6192 | |
6193 // Go over all files in the directory to find out what languages are | |
6194 // present. | |
6195 ga_init2(&ga, 1, 10); | |
6196 for (i = 0; i < filecount; ++i) | |
6197 { | |
6198 len = (int)STRLEN(files[i]); | |
6199 if (len > 4) | |
6200 { | |
6201 if (STRICMP(files[i] + len - 4, ".txt") == 0) | |
6202 { | |
6203 // ".txt" -> language "en" | |
6204 lang[0] = 'e'; | |
6205 lang[1] = 'n'; | |
6206 } | |
6207 else if (files[i][len - 4] == '.' | |
6208 && ASCII_ISALPHA(files[i][len - 3]) | |
6209 && ASCII_ISALPHA(files[i][len - 2]) | |
6210 && TOLOWER_ASC(files[i][len - 1]) == 'x') | |
6211 { | |
6212 // ".abx" -> language "ab" | |
6213 lang[0] = TOLOWER_ASC(files[i][len - 3]); | |
6214 lang[1] = TOLOWER_ASC(files[i][len - 2]); | |
6215 } | |
6216 else | |
6217 continue; | |
6218 | |
6219 // Did we find this language already? | |
6220 for (j = 0; j < ga.ga_len; j += 2) | |
6221 if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) | |
6222 break; | |
6223 if (j == ga.ga_len) | |
6224 { | |
6225 // New language, add it. | |
6226 if (ga_grow(&ga, 2) == FAIL) | |
6227 break; | |
6228 ((char_u *)ga.ga_data)[ga.ga_len++] = lang[0]; | |
6229 ((char_u *)ga.ga_data)[ga.ga_len++] = lang[1]; | |
6230 } | |
6231 } | |
6232 } | |
6233 | |
6234 /* | |
6235 * Loop over the found languages to generate a tags file for each one. | |
6236 */ | |
6237 for (j = 0; j < ga.ga_len; j += 2) | |
6238 { | |
6239 STRCPY(fname, "tags-xx"); | |
6240 fname[5] = ((char_u *)ga.ga_data)[j]; | |
6241 fname[6] = ((char_u *)ga.ga_data)[j + 1]; | |
6242 if (fname[5] == 'e' && fname[6] == 'n') | |
6243 { | |
6244 // English is an exception: use ".txt" and "tags". | |
6245 fname[4] = NUL; | |
6246 STRCPY(ext, ".txt"); | |
6247 } | |
6248 else | |
6249 { | |
6250 // Language "ab" uses ".abx" and "tags-ab". | |
6251 STRCPY(ext, ".xxx"); | |
6252 ext[1] = fname[5]; | |
6253 ext[2] = fname[6]; | |
6254 } | |
6255 helptags_one(dirname, ext, fname, add_help_tags, ignore_writeerr); | |
6256 } | |
6257 | |
6258 ga_clear(&ga); | |
6259 FreeWild(filecount, files); | |
6260 | |
6261 #else | |
6262 // No language support, just use "*.txt" and "tags". | |
6263 helptags_one(dirname, (char_u *)".txt", (char_u *)"tags", add_help_tags, | |
6264 ignore_writeerr); | |
6265 #endif | |
6266 } | |
6267 | |
6268 static void | |
6269 helptags_cb(char_u *fname, void *cookie) | |
6270 { | |
6271 do_helptags(fname, *(int *)cookie, TRUE); | |
6272 } | |
6273 | |
6274 /* | |
6275 * ":helptags" | |
6276 */ | |
6277 void | |
6278 ex_helptags(exarg_T *eap) | |
6279 { | |
6280 expand_T xpc; | |
6281 char_u *dirname; | |
6282 int add_help_tags = FALSE; | |
6283 | |
6284 // Check for ":helptags ++t {dir}". | |
6285 if (STRNCMP(eap->arg, "++t", 3) == 0 && VIM_ISWHITE(eap->arg[3])) | |
6286 { | |
6287 add_help_tags = TRUE; | |
6288 eap->arg = skipwhite(eap->arg + 3); | |
6289 } | |
6290 | |
6291 if (STRCMP(eap->arg, "ALL") == 0) | |
6292 { | |
6293 do_in_path(p_rtp, (char_u *)"doc", DIP_ALL + DIP_DIR, | |
6294 helptags_cb, &add_help_tags); | |
6295 } | |
6296 else | |
6297 { | |
6298 ExpandInit(&xpc); | |
6299 xpc.xp_context = EXPAND_DIRECTORIES; | |
6300 dirname = ExpandOne(&xpc, eap->arg, NULL, | |
6301 WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); | |
6302 if (dirname == NULL || !mch_isdir(dirname)) | |
6303 semsg(_("E150: Not a directory: %s"), eap->arg); | |
6304 else | |
6305 do_helptags(dirname, add_help_tags, FALSE); | |
6306 vim_free(dirname); | |
6307 } | |
6308 } | |
6309 | |
6310 /* | 5036 /* |
6311 * Make the user happy. | 5037 * Make the user happy. |
6312 */ | 5038 */ |
6313 void | 5039 void |
6314 ex_smile(exarg_T *eap UNUSED) | 5040 ex_smile(exarg_T *eap UNUSED) |