To: vim_dev@googlegroups.com Subject: Patch 8.2.4512 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4512 Problem: The find_tags_in_file() function is much too long. Solution: Refactor into multiple smaller functions. (Yegappan Lakshmanan, closes #9892) Files: Filelist, src/Makefile, src/quickfix.c, src/tag.c, src/testdir/test83-tags2, src/testdir/test83-tags3, src/testdir/test_tagjump.vim *** ../vim-8.2.4511/Filelist 2022-02-02 15:19:08.652845890 +0000 --- Filelist 2022-03-05 14:32:34.775107817 +0000 *************** *** 198,204 **** src/testdir/view_util.vim \ src/testdir/test[0-9]*.ok \ src/testdir/test77a.ok \ - src/testdir/test83-tags? \ src/testdir/test77a.com \ src/testdir/test_*.vim \ src/testdir/python2/*.py \ --- 198,203 ---- *************** *** 1000,1005 **** --- 999,1005 ---- runtime/doc/*-tr.1 \ runtime/doc/*-tr.UTF-8.1 \ runtime/lang/README.txt \ + runtime/lang/Makefile \ runtime/lang/menu_*.vim \ runtime/keymap/README.txt \ runtime/keymap/*.vim \ *** ../vim-8.2.4511/src/Makefile 2022-02-23 18:07:34.361914993 +0000 --- src/Makefile 2022-03-05 14:32:34.775107817 +0000 *************** *** 3010,3016 **** ../../testdir/sautest \ ../../testdir/samples \ ../../testdir/dumps \ - ../../testdir/test83-tags? \ ../../testdir/*.ok \ ../../testdir/testluaplugin \ . --- 3010,3015 ---- *** ../vim-8.2.4511/src/quickfix.c 2022-03-02 20:29:31.606116677 +0000 --- src/quickfix.c 2022-03-05 14:32:34.775107817 +0000 *************** *** 8106,8113 **** // Convert a line if 'encoding' is not utf-8 and // the line contains a non-ASCII character. ! if (p_vc->vc_type != CONV_NONE ! && has_non_ascii(IObuff)) { line = string_convert(p_vc, IObuff, NULL); if (line == NULL) --- 8106,8112 ---- // Convert a line if 'encoding' is not utf-8 and // the line contains a non-ASCII character. ! if (p_vc->vc_type != CONV_NONE && has_non_ascii(IObuff)) { line = string_convert(p_vc, IObuff, NULL); if (line == NULL) *** ../vim-8.2.4511/src/tag.c 2022-03-03 10:44:13.646217707 +0000 --- src/tag.c 2022-03-05 14:32:34.779107820 +0000 *************** *** 1580,1587 **** * State information used during a tag search */ typedef struct { ! char_u *tag_fname; // name of tag file pat_T orgpat; // holds unconverted pattern info #ifdef FEAT_MULTI_LANG char_u *help_lang_find; // lang to be found int is_txt; // flag of file extension --- 1580,1590 ---- * State information used during a tag search */ typedef struct { ! char_u *tag_fname; // name of the tag file pat_T orgpat; // holds unconverted pattern info + int name_only; // get only tag names + int get_searchpat; // used for 'showfulltag' + int help_only; // only search for help tags #ifdef FEAT_MULTI_LANG char_u *help_lang_find; // lang to be found int is_txt; // flag of file extension *************** *** 1607,1613 **** * Initialize the state used by find_tags() */ static int ! findtags_state_init(findtags_state_T *st, char_u *pat, int mincount) { int mtt; --- 1610,1620 ---- * Initialize the state used by find_tags() */ static int ! findtags_state_init( ! findtags_state_T *st, ! char_u *pat, ! int flags, ! int mincount) { int mtt; *************** *** 1615,1620 **** --- 1622,1630 ---- st->orgpat.pat = pat; st->orgpat.len = (int)STRLEN(pat); st->orgpat.regmatch.regprog = NULL; + st->help_only = (flags & TAG_HELP); + st->name_only = (flags & TAG_NAMES); + st->get_searchpat = FALSE; #ifdef FEAT_MULTI_LANG st->help_lang_find = NULL; st->is_txt = FALSE; *************** *** 1648,1653 **** --- 1658,2249 ---- } /* + * Free the state used by find_tags() + */ + static void + findtags_state_free(findtags_state_T *st) + { + vim_free(st->tag_fname); + vim_free(st->lbuf); + vim_regfree(st->orgpat.regmatch.regprog); + #ifdef FEAT_EMACS_TAGS + vim_free(st->ebuf); + #endif + } + + #ifdef FEAT_MULTI_LANG + /* + * Initialize the state for searching tags in a Vim help file. + * Returns TRUE to process the help file and FALSE to skip the file. + */ + static int + findtags_in_help_init( + findtags_state_T *st, + int flags, + char_u *help_lang, + int *help_pri) + { + int i; + char_u *s; + + // Keep en if the file extension is .txt + if (st->is_txt) + STRCPY(help_lang, "en"); + else + { + // Prefer help tags according to 'helplang'. Put the + // two-letter language name in help_lang[]. + i = (int)STRLEN(st->tag_fname); + if (i > 3 && st->tag_fname[i - 3] == '-') + STRCPY(help_lang, st->tag_fname + i - 2); + else + STRCPY(help_lang, "en"); + } + // When searching for a specific language skip tags files + // for other languages. + if (st->help_lang_find != NULL + && STRICMP(help_lang, st->help_lang_find) != 0) + return FALSE; + + // For CTRL-] in a help file prefer a match with the same + // language. + if ((flags & TAG_KEEP_LANG) + && st->help_lang_find == NULL + && curbuf->b_fname != NULL + && (i = (int)STRLEN(curbuf->b_fname)) > 4 + && curbuf->b_fname[i - 1] == 'x' + && curbuf->b_fname[i - 4] == '.' + && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0) + *help_pri = 0; + else + { + *help_pri = 1; + for (s = p_hlg; *s != NUL; ++s) + { + if (STRNICMP(s, help_lang, 2) == 0) + break; + ++*help_pri; + if ((s = vim_strchr(s, ',')) == NULL) + break; + } + if (s == NULL || *s == NUL) + { + // Language not in 'helplang': use last, prefer English, + // unless found already. + ++*help_pri; + if (STRICMP(help_lang, "en") != 0) + ++*help_pri; + } + } + + return TRUE; + } + #endif + + #ifdef FEAT_EVAL + /* + * Use the 'tagfunc' (if configured and enabled) to get the tags. + * Return OK if at least 1 tag has been successfully found, NOTDONE if the + * 'tagfunc' is not used or the 'tagfunc' returns v:null and FAIL otherwise. + */ + static int + findtags_apply_tfu( + char_u *pat, + findtags_state_T *st, + int flags, + char_u *buf_ffname) + { + int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0); + int retval; + + if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL) + return NOTDONE; + + tfu_in_use = TRUE; + retval = find_tagfunc_tags(pat, st->ga_match, &st->match_count, + flags, buf_ffname); + tfu_in_use = FALSE; + + return retval; + } + #endif + + #ifdef FEAT_EMACS_TAGS + /* + * Stack for included emacs-tags file. + * It has a fixed size, to truncate cyclic includes. jw + */ + # define INCSTACK_SIZE 42 + static struct + { + FILE *fp; + char_u *etag_fname; + } incstack[INCSTACK_SIZE]; + static int incstack_idx = 0; // index in incstack + + /* + * Free the include tags file stack. + */ + static void + emacs_tags_incstack_free(void) + { + while (incstack_idx) + { + --incstack_idx; + fclose(incstack[incstack_idx].fp); + incstack[incstack_idx].fp = NULL; + VIM_CLEAR(incstack[incstack_idx].etag_fname); + } + } + + /* + * Emacs tags line with CTRL-L: New file name on next line. + * The file name is followed by a ','. Remember etag file name in ebuf. + * Returns a FILE pointer to the tags file. If another tags file is included, + * then returns a pointer to the new tags file. The old file pointer is saved + * in incstack. + */ + static FILE * + emacs_tags_new_filename(findtags_state_T *st, FILE *fp, int *is_etag) + { + char_u *p; + char_u *fullpath_ebuf; + + if (vim_fgets(st->ebuf, LSIZE, fp)) + return fp; + + for (p = st->ebuf; *p && *p != ','; p++) + ; + *p = NUL; + + // check for an included tags file. + // atoi(p+1) is the number of bytes before the next ^L unless it is an + // include statement. Skip the included tags file if it exceeds the + // maximum. + if (STRNCMP(p + 1, "include", 7) != 0 || incstack_idx >= INCSTACK_SIZE) + return fp; + + // Save current "fp" and "tag_fname" in the stack. + incstack[incstack_idx].etag_fname = vim_strsave(st->tag_fname); + if (incstack[incstack_idx].etag_fname == NULL) + return fp; + + incstack[incstack_idx].fp = fp; + fp = NULL; + + // Figure out "tag_fname" and "fp" to use for + // included file. + fullpath_ebuf = expand_tag_fname(st->ebuf, st->tag_fname, FALSE); + if (fullpath_ebuf != NULL) + { + fp = mch_fopen((char *)fullpath_ebuf, "r"); + if (fp != NULL) + { + if (STRLEN(fullpath_ebuf) > LSIZE) + semsg(_(e_tag_file_path_truncated_for_str), st->ebuf); + vim_strncpy(st->tag_fname, fullpath_ebuf, MAXPATHL); + ++incstack_idx; + *is_etag = 0; // we can include anything + } + vim_free(fullpath_ebuf); + } + if (fp == NULL) + { + // Can't open the included file, skip it and + // restore old value of "fp". + fp = incstack[incstack_idx].fp; + vim_free(incstack[incstack_idx].etag_fname); + } + + return fp; + } + + /* + * Reached the end of an emacs-style tags file. If this is an included tags + * file, then pop it from the incstack and continue processing the parent tags + * file. Otherwise, processed all the tags. + * Returns TRUE if an included tags file is popped and processing should + * continue with the parent tags file. Otherwise returns FALSE. + */ + static int + emacs_tags_file_eof(findtags_state_T *st, FILE **fp) + { + if (!incstack_idx) // reached end of file. stop processing. + return FALSE; + + // reached the end of an included tags file. pop it. + --incstack_idx; + fclose(*fp); // end of this file ... + *fp = incstack[incstack_idx].fp; + STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname); + vim_free(incstack[incstack_idx].etag_fname); + + return TRUE; + } + + /* + * Parse a line from an emacs-style tags file. + * Returns OK is the line is parsed successfully, otherwise FALSE. + */ + static int + emacs_tags_parse_line(char_u *lbuf, tagptrs_T *tagp) + { + char_u *p_7f; + char_u *p; + + // There are two formats for an emacs tag line: + // 1: struct EnvBase ^?EnvBase^A139,4627 + // 2: #define ARPB_WILD_WORLD ^?153,5194 + p_7f = vim_strchr(lbuf, 0x7f); + if (p_7f == NULL) + { + etag_fail: + if (vim_strchr(lbuf, '\n') != NULL) + return FAIL; + + // Truncated line. Ignore it. + if (p_verbose >= 5) + { + verbose_enter(); + msg(_("Ignoring long line in tags file")); + verbose_leave(); + } + tagp->command = lbuf; + tagp->tagname = lbuf; + tagp->tagname_end = lbuf; + return OK; + } + + // Find ^A. If not found the line number is after the 0x7f + p = vim_strchr(p_7f, Ctrl_A); + if (p == NULL) + p = p_7f + 1; + else + ++p; + + if (!VIM_ISDIGIT(*p)) // check for start of line number + goto etag_fail; + tagp->command = p; + + if (p[-1] == Ctrl_A) // first format: explicit tagname given + { + tagp->tagname = p_7f + 1; + tagp->tagname_end = p - 1; + } + else // second format: isolate tagname + { + // find end of tagname + for (p = p_7f - 1; !vim_iswordc(*p); --p) + if (p == lbuf) + goto etag_fail; + tagp->tagname_end = p + 1; + while (p >= lbuf && vim_iswordc(*p)) + --p; + tagp->tagname = p + 1; + } + + return OK; + } + #endif + + /* + * Parse a tags file header line in 'st->lbuf'. + * Returns TRUE to read the next header line and FALSE to process the line. + */ + static int + tags_file_hdr_parse(findtags_state_T *st, vimconv_T *vcp, int *sorted_file) + { + char_u *p; + + if (STRNCMP(st->lbuf, "!_TAG_", 6) != 0) + // Non-header item before the header, e.g. "!" itself. + return FALSE; + + // Read header line. + #ifdef FEAT_TAG_BINS + if (STRNCMP(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) + *sorted_file = st->lbuf[18]; + #endif + if (STRNCMP(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) + { + // Prepare to convert every line from the specified + // encoding to 'encoding'. + for (p = st->lbuf + 20; *p > ' ' && *p < 127; ++p) + ; + *p = NUL; + convert_setup(vcp, st->lbuf + 20, p_enc); + } + + // Read the next line. Unrecognized flags are ignored. + return TRUE; + } + + /* + * Convert the encoding of a line read from a tags file in 'st->lbuf'. + * Converting the pattern from 'enc' to the tags file encoding doesn't work, + * because characters are not recognized. + */ + static void + findtags_string_convert(findtags_state_T *st, vimconv_T *vcp) + { + char_u *conv_line; + int len; + + conv_line = string_convert(vcp, st->lbuf, NULL); + if (conv_line == NULL) + return; + + // Copy or swap lbuf and conv_line. + len = (int)STRLEN(conv_line) + 1; + if (len > st->lbuf_size) + { + vim_free(st->lbuf); + st->lbuf = conv_line; + st->lbuf_size = len; + } + else + { + STRCPY(st->lbuf, conv_line); + vim_free(conv_line); + } + } + + static int + findtags_add_match( + findtags_state_T *st, + tagptrs_T *tagp, + char_u *buf_ffname, + int flags UNUSED, + hash_T *hash, + int match_re, + int match_no_ic, + int matchoff, + int is_etag UNUSED, + char_u *help_lang UNUSED, + int help_pri UNUSED) + { + #ifdef FEAT_CSCOPE + int use_cscope = (flags & TAG_CSCOPE); + #endif + int mtt; + int len = 0; + int is_current; // file name matches + int is_static; // current tag line is static + char_u *mfp; + char_u *p; + char_u *s; + + #ifdef FEAT_CSCOPE + if (use_cscope) + { + // Don't change the ordering, always use the same table. + mtt = MT_GL_OTH; + } + else + #endif + { + // Decide in which array to store this match. + is_current = test_for_current( + #ifdef FEAT_EMACS_TAGS + is_etag, + #endif + tagp->fname, tagp->fname_end, st->tag_fname, + buf_ffname); + #ifdef FEAT_EMACS_TAGS + is_static = FALSE; + if (!is_etag) // emacs tags are never static + #endif + is_static = test_for_static(tagp); + + // decide in which of the sixteen tables to store this + // match + if (is_static) + { + if (is_current) + mtt = MT_ST_CUR; + else + mtt = MT_ST_OTH; + } + else + { + if (is_current) + mtt = MT_GL_CUR; + else + mtt = MT_GL_OTH; + } + if (st->orgpat.regmatch.rm_ic && !match_no_ic) + mtt += MT_IC_OFF; + if (match_re) + mtt += MT_RE_OFF; + } + + // Add the found match in ht_match[mtt] and ga_match[mtt]. + // Store the info we need later, which depends on the kind of + // tags we are dealing with. + if (st->help_only) + { + #ifdef FEAT_MULTI_LANG + # define ML_EXTRA 3 + #else + # define ML_EXTRA 0 + #endif + // Append the help-heuristic number after the tagname, for + // sorting it later. The heuristic is ignored for + // detecting duplicates. + // The format is {tagname}@{lang}NUL{heuristic}NUL + *tagp->tagname_end = NUL; + len = (int)(tagp->tagname_end - tagp->tagname); + mfp = alloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1); + if (mfp != NULL) + { + int heuristic; + + p = mfp; + STRCPY(p, tagp->tagname); + #ifdef FEAT_MULTI_LANG + p[len] = '@'; + STRCPY(p + len + 1, help_lang); + #endif + + heuristic = help_heuristic(tagp->tagname, + match_re ? matchoff : 0, !match_no_ic); + #ifdef FEAT_MULTI_LANG + heuristic += help_pri; + #endif + sprintf((char *)p + len + 1 + ML_EXTRA, "%06d", + heuristic); + } + *tagp->tagname_end = TAB; + } + else if (st->name_only) + { + if (st->get_searchpat) + { + char_u *temp_end = tagp->command; + + if (*temp_end == '/') + while (*temp_end && *temp_end != '\r' + && *temp_end != '\n' + && *temp_end != '$') + temp_end++; + + if (tagp->command + 2 < temp_end) + { + len = (int)(temp_end - tagp->command - 2); + mfp = alloc(len + 2); + if (mfp != NULL) + vim_strncpy(mfp, tagp->command + 2, len); + } + else + mfp = NULL; + st->get_searchpat = FALSE; + } + else + { + len = (int)(tagp->tagname_end - tagp->tagname); + mfp = alloc(sizeof(char_u) + len + 1); + if (mfp != NULL) + vim_strncpy(mfp, tagp->tagname, len); + + // if wanted, re-read line to get long form too + if (State & INSERT) + st->get_searchpat = p_sft; + } + } + else + { + size_t tag_fname_len = STRLEN(st->tag_fname); + #ifdef FEAT_EMACS_TAGS + size_t ebuf_len = 0; + #endif + + // Save the tag in a buffer. + // Use 0x02 to separate fields (Can't use NUL because the + // hash key is terminated by NUL, or Ctrl_A because that is + // part of some Emacs tag files -- see parse_tag_line). + // Emacs tag: <0x02><0x02> + // other tag: <0x02><0x02> + // without Emacs tags: <0x02> + // Here is the "mtt" value plus 1 to avoid NUL. + len = (int)tag_fname_len + (int)STRLEN(st->lbuf) + 3; + #ifdef FEAT_EMACS_TAGS + if (is_etag) + { + ebuf_len = STRLEN(st->ebuf); + len += (int)ebuf_len + 1; + } + else + ++len; + #endif + mfp = alloc(sizeof(char_u) + len + 1); + if (mfp != NULL) + { + p = mfp; + p[0] = mtt + 1; + STRCPY(p + 1, st->tag_fname); + #ifdef BACKSLASH_IN_FILENAME + // Ignore differences in slashes, avoid adding + // both path/file and path\file. + slash_adjust(p + 1); + #endif + p[tag_fname_len + 1] = TAG_SEP; + s = p + 1 + tag_fname_len + 1; + #ifdef FEAT_EMACS_TAGS + if (is_etag) + { + STRCPY(s, st->ebuf); + s[ebuf_len] = TAG_SEP; + s += ebuf_len + 1; + } + else + *s++ = TAG_SEP; + #endif + STRCPY(s, st->lbuf); + } + } + + if (mfp != NULL) + { + hashitem_T *hi; + + // Don't add identical matches. + // Add all cscope tags, because they are all listed. + // "mfp" is used as a hash key, there is a NUL byte to end + // the part that matters for comparing, more bytes may + // follow after it. E.g. help tags store the priority + // after the NUL. + #ifdef FEAT_CSCOPE + if (use_cscope) + ++*hash; + else + #endif + *hash = hash_hash(mfp); + hi = hash_lookup(&st->ht_match[mtt], mfp, *hash); + if (HASHITEM_EMPTY(hi)) + { + if (hash_add_item(&st->ht_match[mtt], hi, mfp, *hash) == FAIL + || ga_grow(&st->ga_match[mtt], 1) != OK) + { + // Out of memory! Just forget about the rest. + st->stop_searching = TRUE; + return FAIL; + } + else + { + ((char_u **)(st->ga_match[mtt].ga_data)) + [st->ga_match[mtt].ga_len++] = mfp; + st->match_count++; + } + } + else + // duplicate tag, drop it + vim_free(mfp); + } + + return OK; + } + + /* * Search for tags matching 'st->orgpat.pat' in the 'st->tag_fname' tags file. * Information needed to search for the tags is in the 'st' state structure. * The matching tags are returned in 'st'. *************** *** 1662,1672 **** { FILE *fp = NULL; tagptrs_T tagp; - int is_static; // current tag line is static - int is_current; // file name matches int eof = FALSE; // found end-of-file - char_u *p; - char_u *s; int i; #ifdef FEAT_MULTI_LANG int help_pri = 0; --- 2258,2264 ---- *************** *** 1709,1732 **** int match_re; // match with regexp int matchoff = 0; - #ifdef FEAT_EMACS_TAGS - /* - * Stack for included emacs-tags file. - * It has a fixed size, to truncate cyclic includes. jw - */ - # define INCSTACK_SIZE 42 - struct - { - FILE *fp; - char_u *etag_fname; - } incstack[INCSTACK_SIZE]; - - int incstack_idx = 0; // index in incstack int is_etag; // current file is emaces style - #endif - char_u *mfp; - int mtt; hash_T hash = 0; #ifdef FEAT_TAG_BINS --- 2301,2308 ---- *************** *** 1736,1747 **** #endif int line_error = FALSE; // syntax error int has_re = (flags & TAG_REGEXP); // regexp used - int help_only = (flags & TAG_HELP); - int name_only = (flags & TAG_NAMES); #ifdef FEAT_CSCOPE int use_cscope = (flags & TAG_CSCOPE); #endif - int get_it_again = FALSE; vimconv_T vimconv; vimconv.vc_type = CONV_NONE; --- 2312,2320 ---- *************** *** 1752,1761 **** CLEAR_FIELD(search_info); #endif ! /* ! * A file that doesn't exist is silently ignored. Only when not a ! * single file is found, an error message is given (further on). ! */ #ifdef FEAT_CSCOPE if (use_cscope) fp = NULL; // avoid GCC warning --- 2325,2332 ---- CLEAR_FIELD(search_info); #endif ! // A file that doesn't exist is silently ignored. Only when not a ! // single file is found, an error message is given (further on). #ifdef FEAT_CSCOPE if (use_cscope) fp = NULL; // avoid GCC warning *************** *** 1765,1819 **** #ifdef FEAT_MULTI_LANG if (curbuf->b_help) { ! // Keep en if the file extension is .txt ! if (st->is_txt) ! STRCPY(help_lang, "en"); ! else ! { ! // Prefer help tags according to 'helplang'. Put the ! // two-letter language name in help_lang[]. ! i = (int)STRLEN(st->tag_fname); ! if (i > 3 && st->tag_fname[i - 3] == '-') ! STRCPY(help_lang, st->tag_fname + i - 2); ! else ! STRCPY(help_lang, "en"); ! } ! // When searching for a specific language skip tags files ! // for other languages. ! if (st->help_lang_find != NULL ! && STRICMP(help_lang, st->help_lang_find) != 0) return OK; - - // For CTRL-] in a help file prefer a match with the same - // language. - if ((flags & TAG_KEEP_LANG) - && st->help_lang_find == NULL - && curbuf->b_fname != NULL - && (i = (int)STRLEN(curbuf->b_fname)) > 4 - && curbuf->b_fname[i - 1] == 'x' - && curbuf->b_fname[i - 4] == '.' - && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0) - help_pri = 0; - else - { - help_pri = 1; - for (s = p_hlg; *s != NUL; ++s) - { - if (STRNICMP(s, help_lang, 2) == 0) - break; - ++help_pri; - if ((s = vim_strchr(s, ',')) == NULL) - break; - } - if (s == NULL || *s == NUL) - { - // Language not in 'helplang': use last, prefer English, - // unless found already. - ++help_pri; - if (STRICMP(help_lang, "en") != 0) - ++help_pri; - } - } } #endif --- 2336,2343 ---- #ifdef FEAT_MULTI_LANG if (curbuf->b_help) { ! if (!findtags_in_help_init(st, flags, help_lang, &help_pri)) return OK; } #endif *************** *** 1827,1842 **** verbose_leave(); } } ! st->did_open = TRUE; // remember that we found at least one file ! state = TS_START; // we're at the start of the file ! #ifdef FEAT_EMACS_TAGS ! is_etag = 0; // default is: not emacs style ! #endif ! /* ! * Read and parse the lines in the file one by one ! */ for (;;) { #ifdef FEAT_TAG_BINS --- 2351,2362 ---- verbose_leave(); } } ! st->did_open = TRUE; // remember that we found at least one file ! state = TS_START; // we're at the start of the file ! is_etag = 0; // default is: not emacs style ! // Read and parse the lines in the file one by one for (;;) { #ifdef FEAT_TAG_BINS *************** *** 1860,1871 **** st->stop_searching = TRUE; break; } ! if (get_it_again) goto line_read_in; #ifdef FEAT_TAG_BINS ! /* ! * For binary search: compute the next offset to use. ! */ if (state == TS_BINARY) { offset = search_info.low_offset + ((search_info.high_offset --- 2380,2389 ---- st->stop_searching = TRUE; break; } ! if (st->get_searchpat) goto line_read_in; #ifdef FEAT_TAG_BINS ! // For binary search: compute the next offset to use. if (state == TS_BINARY) { offset = search_info.low_offset + ((search_info.high_offset *************** *** 1952,2001 **** if (eof) { #ifdef FEAT_EMACS_TAGS ! if (incstack_idx) // this was an included file { ! --incstack_idx; ! fclose(fp); // end of this file ... ! fp = incstack[incstack_idx].fp; ! STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname); ! vim_free(incstack[incstack_idx].etag_fname); is_etag = 1; // (only etags can include) ! continue; // ... continue with parent file } - else #endif ! break; // end of file } } line_read_in: if (vimconv.vc_type != CONV_NONE) ! { ! char_u *conv_line; ! int len; ! ! // Convert every line. Converting the pattern from 'enc' to ! // the tags file encoding doesn't work, because characters are ! // not recognized. ! conv_line = string_convert(&vimconv, st->lbuf, NULL); ! if (conv_line != NULL) ! { ! // Copy or swap lbuf and conv_line. ! len = (int)STRLEN(conv_line) + 1; ! if (len > st->lbuf_size) ! { ! vim_free(st->lbuf); ! st->lbuf = conv_line; ! st->lbuf_size = len; ! } ! else ! { ! STRCPY(st->lbuf, conv_line); ! vim_free(conv_line); ! } ! } ! } ! #ifdef FEAT_EMACS_TAGS /* --- 2470,2490 ---- if (eof) { #ifdef FEAT_EMACS_TAGS ! if (emacs_tags_file_eof(st, &fp) == TRUE) { ! // an included tags file. Continue processing the parent ! // tags file. is_etag = 1; // (only etags can include) ! continue; } #endif ! break; // end of file } } line_read_in: if (vimconv.vc_type != CONV_NONE) ! findtags_string_convert(st, &vimconv); #ifdef FEAT_EMACS_TAGS /* *************** *** 2011,2066 **** { is_etag = 1; // in case at the start state = TS_LINEAR; ! if (!vim_fgets(st->ebuf, LSIZE, fp)) ! { ! for (p = st->ebuf; *p && *p != ','; p++) ! ; ! *p = NUL; ! ! /* ! * atoi(p+1) is the number of bytes before the next ^L ! * unless it is an include statement. ! */ ! if (STRNCMP(p + 1, "include", 7) == 0 ! && incstack_idx < INCSTACK_SIZE) ! { ! // Save current "fp" and "tag_fname" in the stack. ! if ((incstack[incstack_idx].etag_fname = ! vim_strsave(st->tag_fname)) != NULL) ! { ! char_u *fullpath_ebuf; ! ! incstack[incstack_idx].fp = fp; ! fp = NULL; ! ! // Figure out "tag_fname" and "fp" to use for ! // included file. ! fullpath_ebuf = expand_tag_fname(st->ebuf, ! st->tag_fname, FALSE); ! if (fullpath_ebuf != NULL) ! { ! fp = mch_fopen((char *)fullpath_ebuf, "r"); ! if (fp != NULL) ! { ! if (STRLEN(fullpath_ebuf) > LSIZE) ! semsg(_(e_tag_file_path_truncated_for_str), st->ebuf); ! vim_strncpy(st->tag_fname, fullpath_ebuf, ! MAXPATHL); ! ++incstack_idx; ! is_etag = 0; // we can include anything ! } ! vim_free(fullpath_ebuf); ! } ! if (fp == NULL) ! { ! // Can't open the included file, skip it and ! // restore old value of "fp". ! fp = incstack[incstack_idx].fp; ! vim_free(incstack[incstack_idx].etag_fname); ! } ! } ! } ! } continue; } #endif --- 2500,2506 ---- { is_etag = 1; // in case at the start state = TS_LINEAR; ! fp = emacs_tags_new_filename(st, fp, &is_etag); continue; } #endif *************** *** 2076,2104 **** if (STRNCMP(st->lbuf, "!_TAG_", 6) <= 0 || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1]))) { ! if (STRNCMP(st->lbuf, "!_TAG_", 6) != 0) ! // Non-header item before the header, e.g. "!" itself. ! goto parse_line; ! ! /* ! * Read header line. ! */ ! #ifdef FEAT_TAG_BINS ! if (STRNCMP(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) ! tag_file_sorted = st->lbuf[18]; ! #endif ! if (STRNCMP(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) ! { ! // Prepare to convert every line from the specified ! // encoding to 'encoding'. ! for (p = st->lbuf + 20; *p > ' ' && *p < 127; ++p) ! ; ! *p = NUL; ! convert_setup(&vimconv, st->lbuf + 20, p_enc); ! } ! // Read the next line. Unrecognized flags are ignored. ! continue; } // Headers ends. --- 2516,2526 ---- if (STRNCMP(st->lbuf, "!_TAG_", 6) <= 0 || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1]))) { ! if (tags_file_hdr_parse(st, &vimconv, &tag_file_sorted)) ! // Read the next line. Unrecognized flags are ignored. ! continue; ! goto parse_line; } // Headers ends. *************** *** 2338,2348 **** */ tagp.fname = tagp.tagname_end + 1; tagp.fname_end = vim_strchr(tagp.fname, TAB); - tagp.command = tagp.fname_end + 1; if (tagp.fname_end == NULL) i = FAIL; else i = OK; } else i = parse_tag_line(st->lbuf, --- 2760,2772 ---- */ tagp.fname = tagp.tagname_end + 1; tagp.fname_end = vim_strchr(tagp.fname, TAB); if (tagp.fname_end == NULL) i = FAIL; else + { + tagp.command = tagp.fname_end + 1; i = OK; + } } else i = parse_tag_line(st->lbuf, *************** *** 2409,2634 **** match_re = TRUE; } ! /* ! * If a match is found, add it to ht_match[] and ga_match[]. ! */ if (match) { ! int len = 0; ! ! #ifdef FEAT_CSCOPE ! if (use_cscope) ! { ! // Don't change the ordering, always use the same table. ! mtt = MT_GL_OTH; ! } ! else ! #endif ! { ! // Decide in which array to store this match. ! is_current = test_for_current( ! #ifdef FEAT_EMACS_TAGS is_etag, - #endif - tagp.fname, tagp.fname_end, st->tag_fname, - buf_ffname); - #ifdef FEAT_EMACS_TAGS - is_static = FALSE; - if (!is_etag) // emacs tags are never static - #endif - is_static = test_for_static(&tagp); - - // decide in which of the sixteen tables to store this - // match - if (is_static) - { - if (is_current) - mtt = MT_ST_CUR; - else - mtt = MT_ST_OTH; - } - else - { - if (is_current) - mtt = MT_GL_CUR; - else - mtt = MT_GL_OTH; - } - if (st->orgpat.regmatch.rm_ic && !match_no_ic) - mtt += MT_IC_OFF; - if (match_re) - mtt += MT_RE_OFF; - } - - /* - * Add the found match in ht_match[mtt] and ga_match[mtt]. - * Store the info we need later, which depends on the kind of - * tags we are dealing with. - */ - if (help_only) - { #ifdef FEAT_MULTI_LANG ! # define ML_EXTRA 3 #else ! # define ML_EXTRA 0 #endif ! /* ! * Append the help-heuristic number after the tagname, for ! * sorting it later. The heuristic is ignored for ! * detecting duplicates. ! * The format is {tagname}@{lang}NUL{heuristic}NUL ! */ ! *tagp.tagname_end = NUL; ! len = (int)(tagp.tagname_end - tagp.tagname); ! mfp = alloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1); ! if (mfp != NULL) ! { ! int heuristic; ! ! p = mfp; ! STRCPY(p, tagp.tagname); ! #ifdef FEAT_MULTI_LANG ! p[len] = '@'; ! STRCPY(p + len + 1, help_lang); ! #endif ! ! heuristic = help_heuristic(tagp.tagname, ! match_re ? matchoff : 0, !match_no_ic); ! #ifdef FEAT_MULTI_LANG ! heuristic += help_pri; ! #endif ! sprintf((char *)p + len + 1 + ML_EXTRA, "%06d", ! heuristic); ! } ! *tagp.tagname_end = TAB; ! } ! else if (name_only) ! { ! if (get_it_again) ! { ! char_u *temp_end = tagp.command; ! ! if (*temp_end == '/') ! while (*temp_end && *temp_end != '\r' ! && *temp_end != '\n' ! && *temp_end != '$') ! temp_end++; ! ! if (tagp.command + 2 < temp_end) ! { ! len = (int)(temp_end - tagp.command - 2); ! mfp = alloc(len + 2); ! if (mfp != NULL) ! vim_strncpy(mfp, tagp.command + 2, len); ! } ! else ! mfp = NULL; ! get_it_again = FALSE; ! } ! else ! { ! len = (int)(tagp.tagname_end - tagp.tagname); ! mfp = alloc(sizeof(char_u) + len + 1); ! if (mfp != NULL) ! vim_strncpy(mfp, tagp.tagname, len); ! ! // if wanted, re-read line to get long form too ! if (State & INSERT) ! get_it_again = p_sft; ! } ! } ! else ! { ! size_t tag_fname_len = STRLEN(st->tag_fname); ! #ifdef FEAT_EMACS_TAGS ! size_t ebuf_len = 0; ! #endif ! ! // Save the tag in a buffer. ! // Use 0x02 to separate fields (Can't use NUL because the ! // hash key is terminated by NUL, or Ctrl_A because that is ! // part of some Emacs tag files -- see parse_tag_line). ! // Emacs tag: <0x02><0x02> ! // other tag: <0x02><0x02> ! // without Emacs tags: <0x02> ! // Here is the "mtt" value plus 1 to avoid NUL. ! len = (int)tag_fname_len + (int)STRLEN(st->lbuf) + 3; ! #ifdef FEAT_EMACS_TAGS ! if (is_etag) ! { ! ebuf_len = STRLEN(st->ebuf); ! len += (int)ebuf_len + 1; ! } ! else ! ++len; ! #endif ! mfp = alloc(sizeof(char_u) + len + 1); ! if (mfp != NULL) ! { ! p = mfp; ! p[0] = mtt + 1; ! STRCPY(p + 1, st->tag_fname); ! #ifdef BACKSLASH_IN_FILENAME ! // Ignore differences in slashes, avoid adding ! // both path/file and path\file. ! slash_adjust(p + 1); ! #endif ! p[tag_fname_len + 1] = TAG_SEP; ! s = p + 1 + tag_fname_len + 1; ! #ifdef FEAT_EMACS_TAGS ! if (is_etag) ! { ! STRCPY(s, st->ebuf); ! s[ebuf_len] = TAG_SEP; ! s += ebuf_len + 1; ! } ! else ! *s++ = TAG_SEP; ! #endif ! STRCPY(s, st->lbuf); ! } ! } ! ! if (mfp != NULL) ! { ! hashitem_T *hi; ! ! /* ! * Don't add identical matches. ! * Add all cscope tags, because they are all listed. ! * "mfp" is used as a hash key, there is a NUL byte to end ! * the part that matters for comparing, more bytes may ! * follow after it. E.g. help tags store the priority ! * after the NUL. ! */ ! #ifdef FEAT_CSCOPE ! if (use_cscope) ! hash++; ! else ! #endif ! hash = hash_hash(mfp); ! hi = hash_lookup(&st->ht_match[mtt], mfp, hash); ! if (HASHITEM_EMPTY(hi)) ! { ! if (hash_add_item(&st->ht_match[mtt], hi, mfp, hash) ! == FAIL ! || ga_grow(&st->ga_match[mtt], 1) != OK) ! { ! // Out of memory! Just forget about the rest. ! st->stop_searching = TRUE; ! break; ! } ! else ! { ! ((char_u **)(st->ga_match[mtt].ga_data)) ! [st->ga_match[mtt].ga_len++] = mfp; ! st->match_count++; ! } ! } ! else ! // duplicate tag, drop it ! vim_free(mfp); ! } } #ifdef FEAT_CSCOPE if (use_cscope && eof) --- 2833,2851 ---- match_re = TRUE; } ! // If a match is found, add it to ht_match[] and ga_match[]. if (match) { ! if (findtags_add_match(st, &tagp, buf_ffname, flags, &hash, ! match_re, match_no_ic, matchoff, is_etag, #ifdef FEAT_MULTI_LANG ! help_lang, help_pri #else ! (char_u *)"", 0 #endif ! ) == FAIL) ! break; } #ifdef FEAT_CSCOPE if (use_cscope && eof) *************** *** 2652,2663 **** #endif fclose(fp); #ifdef FEAT_EMACS_TAGS ! while (incstack_idx) ! { ! --incstack_idx; ! fclose(incstack[incstack_idx].fp); ! vim_free(incstack[incstack_idx].etag_fname); ! } #endif if (vimconv.vc_type != CONV_NONE) convert_setup(&vimconv, NULL, NULL); --- 2869,2875 ---- #endif fclose(fp); #ifdef FEAT_EMACS_TAGS ! emacs_tags_incstack_free(); #endif if (vimconv.vc_type != CONV_NONE) convert_setup(&vimconv, NULL, NULL); *************** *** 2687,2694 **** findtags_copy_matches( findtags_state_T *st, char_u ***matchesp, ! int *num_matches, ! int name_only) { char_u **matches; int mtt; --- 2899,2905 ---- findtags_copy_matches( findtags_state_T *st, char_u ***matchesp, ! int *num_matches) { char_u **matches; int mtt; *************** *** 2710,2716 **** vim_free(mfp); else { ! if (!name_only) { // Change mtt back to zero-based. *mfp = *mfp - 1; --- 2921,2927 ---- vim_free(mfp); else { ! if (!st->name_only) { // Change mtt back to zero-based. *mfp = *mfp - 1; *************** *** 2789,2804 **** // find all matching tags #endif int has_re = (flags & TAG_REGEXP); // regexp used - int help_only = (flags & TAG_HELP); - int name_only = (flags & TAG_NAMES); int noic = (flags & TAG_NOIC); #ifdef FEAT_CSCOPE int use_cscope = (flags & TAG_CSCOPE); #endif int verbose = (flags & TAG_VERBOSE); - #ifdef FEAT_EVAL - int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0); - #endif int save_p_ic = p_ic; /* --- 3000,3010 ---- *************** *** 2816,2822 **** help_save = curbuf->b_help; ! if (findtags_state_init(&st, pat, mincount) == FAIL) goto findtag_end; #ifdef FEAT_CSCOPE --- 3022,3028 ---- help_save = curbuf->b_help; ! if (findtags_state_init(&st, pat, flags, mincount) == FAIL) goto findtag_end; #ifdef FEAT_CSCOPE *************** *** 2826,2838 **** /* * Initialize a few variables */ ! if (help_only) // want tags from help file curbuf->b_help = TRUE; // will be restored later #ifdef FEAT_CSCOPE else if (use_cscope) { // Make sure we don't mix help and cscope, confuses Coverity. ! help_only = FALSE; curbuf->b_help = FALSE; } #endif --- 3032,3044 ---- /* * Initialize a few variables */ ! if (st.help_only) // want tags from help file curbuf->b_help = TRUE; // will be restored later #ifdef FEAT_CSCOPE else if (use_cscope) { // Make sure we don't mix help and cscope, confuses Coverity. ! st.help_only = FALSE; curbuf->b_help = FALSE; } #endif *************** *** 2867,2881 **** goto findtag_end; #ifdef FEAT_EVAL ! if (*curbuf->b_p_tfu != NUL && use_tfu && !tfu_in_use) ! { ! tfu_in_use = TRUE; ! retval = find_tagfunc_tags(pat, &st.ga_match[0], &st.match_count, ! flags, buf_ffname); ! tfu_in_use = FALSE; ! if (retval != NOTDONE) ! goto findtag_end; ! } #endif /* --- 3073,3094 ---- goto findtag_end; #ifdef FEAT_EVAL ! retval = findtags_apply_tfu(pat, &st, flags, buf_ffname); ! if (retval != NOTDONE) ! goto findtag_end; ! ! // re-initialize the default return value ! retval = FAIL; ! #endif ! ! #ifdef FEAT_MULTI_LANG ! // Set a flag if the file extension is .txt ! if ((flags & TAG_KEEP_LANG) ! && st.help_lang_find == NULL ! && curbuf->b_fname != NULL ! && (i = (int)STRLEN(curbuf->b_fname)) > 4 ! && STRICMP(curbuf->b_fname + i - 4, ".txt") == 0) ! st.is_txt = TRUE; #endif /* *************** *** 2888,2902 **** * When the tag file is case-fold sorted, it is either one or the other. * Only ignore case when TAG_NOIC not used or 'ignorecase' set. */ - #ifdef FEAT_MULTI_LANG - // Set a flag if the file extension is .txt - if ((flags & TAG_KEEP_LANG) - && st.help_lang_find == NULL - && curbuf->b_fname != NULL - && (i = (int)STRLEN(curbuf->b_fname)) > 4 - && STRICMP(curbuf->b_fname + i - 4, ".txt") == 0) - st.is_txt = TRUE; - #endif #ifdef FEAT_TAG_BINS st.orgpat.regmatch.rm_ic = ((p_ic || !noic) && (findall || st.orgpat.headlen == 0 || !p_tbs)); --- 3101,3106 ---- *************** *** 2959,2970 **** } findtag_end: ! vim_free(st.tag_fname); ! vim_free(st.lbuf); ! vim_regfree(st.orgpat.regmatch.regprog); ! #ifdef FEAT_EMACS_TAGS ! vim_free(st.ebuf); ! #endif /* * Move the matches from the ga_match[] arrays into one list of --- 3163,3169 ---- } findtag_end: ! findtags_state_free(&st); /* * Move the matches from the ga_match[] arrays into one list of *************** *** 2973,2979 **** if (retval == FAIL) st.match_count = 0; ! findtags_copy_matches(&st, matchesp, num_matches, name_only); curbuf->b_help = help_save; #ifdef FEAT_MULTI_LANG --- 3172,3178 ---- if (retval == FAIL) st.match_count = 0; ! findtags_copy_matches(&st, matchesp, num_matches); curbuf->b_help = help_save; #ifdef FEAT_MULTI_LANG *************** *** 3194,3286 **** char_u *p; #ifdef FEAT_EMACS_TAGS - char_u *p_7f; - if (is_etag) ! { ! /* ! * There are two formats for an emacs tag line: ! * 1: struct EnvBase ^?EnvBase^A139,4627 ! * 2: #define ARPB_WILD_WORLD ^?153,5194 ! */ ! p_7f = vim_strchr(lbuf, 0x7f); ! if (p_7f == NULL) ! { ! etag_fail: ! if (vim_strchr(lbuf, '\n') == NULL) ! { ! // Truncated line. Ignore it. ! if (p_verbose >= 5) ! { ! verbose_enter(); ! msg(_("Ignoring long line in tags file")); ! verbose_leave(); ! } ! tagp->command = lbuf; ! tagp->tagname = lbuf; ! tagp->tagname_end = lbuf; ! return OK; ! } ! return FAIL; ! } ! ! // Find ^A. If not found the line number is after the 0x7f ! p = vim_strchr(p_7f, Ctrl_A); ! if (p == NULL) ! p = p_7f + 1; ! else ! ++p; ! ! if (!VIM_ISDIGIT(*p)) // check for start of line number ! goto etag_fail; ! tagp->command = p; ! ! ! if (p[-1] == Ctrl_A) // first format: explicit tagname given ! { ! tagp->tagname = p_7f + 1; ! tagp->tagname_end = p - 1; ! } ! else // second format: isolate tagname ! { ! // find end of tagname ! for (p = p_7f - 1; !vim_iswordc(*p); --p) ! if (p == lbuf) ! goto etag_fail; ! tagp->tagname_end = p + 1; ! while (p >= lbuf && vim_iswordc(*p)) ! --p; ! tagp->tagname = p + 1; ! } ! } ! else // not an Emacs tag ! { #endif - // Isolate the tagname, from lbuf up to the first white - tagp->tagname = lbuf; - p = vim_strchr(lbuf, TAB); - if (p == NULL) - return FAIL; - tagp->tagname_end = p; ! // Isolate file name, from first to second white space ! if (*p != NUL) ! ++p; ! tagp->fname = p; ! p = vim_strchr(p, TAB); ! if (p == NULL) ! return FAIL; ! tagp->fname_end = p; ! // find start of search command, after second white space ! if (*p != NUL) ! ++p; ! if (*p == NUL) ! return FAIL; ! tagp->command = p; ! #ifdef FEAT_EMACS_TAGS ! } ! #endif return OK; } --- 3393,3425 ---- char_u *p; #ifdef FEAT_EMACS_TAGS if (is_etag) ! // emacs-style tag file ! return emacs_tags_parse_line(lbuf, tagp); #endif ! // Isolate the tagname, from lbuf up to the first white ! tagp->tagname = lbuf; ! p = vim_strchr(lbuf, TAB); ! if (p == NULL) ! return FAIL; ! tagp->tagname_end = p; ! // Isolate file name, from first to second white space ! if (*p != NUL) ! ++p; ! tagp->fname = p; ! p = vim_strchr(p, TAB); ! if (p == NULL) ! return FAIL; ! tagp->fname_end = p; ! ! // find start of search command, after second white space ! if (*p != NUL) ! ++p; ! if (*p == NUL) ! return FAIL; ! tagp->command = p; return OK; } *** ../vim-8.2.4511/src/testdir/test83-tags2 2011-10-12 18:34:15.000000000 +0100 --- src/testdir/test83-tags2 1970-01-01 00:00:00.000000000 +0000 *************** *** 1,2 **** - !_TAG_FILE_ENCODING cp932 // - ‚`‚a‚b Xtags2.txt /‚`‚a‚b --- 0 ---- *** ../vim-8.2.4511/src/testdir/test83-tags3 2011-10-12 18:35:42.000000000 +0100 --- src/testdir/test83-tags3 1970-01-01 00:00:00.000000000 +0000 *************** *** 1,102 **** - !_TAG_FILE_SORTED 1 // - !_TAG_FILE_ENCODING cp932 // - abc1 Xtags3.txt /‚`‚a‚b - abc2 Xtags3.txt /‚`‚a‚b - abc3 Xtags3.txt /‚`‚a‚b - abc4 Xtags3.txt /‚`‚a‚b - abc5 Xtags3.txt /‚`‚a‚b - abc6 Xtags3.txt /‚`‚a‚b - abc7 Xtags3.txt /‚`‚a‚b - abc8 Xtags3.txt /‚`‚a‚b - abc9 Xtags3.txt /‚`‚a‚b - abc10 Xtags3.txt /‚`‚a‚b - abc11 Xtags3.txt /‚`‚a‚b - abc12 Xtags3.txt /‚`‚a‚b - abc13 Xtags3.txt /‚`‚a‚b - abc14 Xtags3.txt /‚`‚a‚b - abc15 Xtags3.txt /‚`‚a‚b - abc16 Xtags3.txt /‚`‚a‚b - abc17 Xtags3.txt /‚`‚a‚b - abc18 Xtags3.txt /‚`‚a‚b - abc19 Xtags3.txt /‚`‚a‚b - abc20 Xtags3.txt /‚`‚a‚b - abc21 Xtags3.txt /‚`‚a‚b - abc22 Xtags3.txt /‚`‚a‚b - abc23 Xtags3.txt /‚`‚a‚b - abc24 Xtags3.txt /‚`‚a‚b - abc25 Xtags3.txt /‚`‚a‚b - abc26 Xtags3.txt /‚`‚a‚b - abc27 Xtags3.txt /‚`‚a‚b - abc28 Xtags3.txt /‚`‚a‚b - abc29 Xtags3.txt /‚`‚a‚b - abc30 Xtags3.txt /‚`‚a‚b - abc31 Xtags3.txt /‚`‚a‚b - abc32 Xtags3.txt /‚`‚a‚b - abc33 Xtags3.txt /‚`‚a‚b - abc34 Xtags3.txt /‚`‚a‚b - abc35 Xtags3.txt /‚`‚a‚b - abc36 Xtags3.txt /‚`‚a‚b - abc37 Xtags3.txt /‚`‚a‚b - abc38 Xtags3.txt /‚`‚a‚b - abc39 Xtags3.txt /‚`‚a‚b - abc40 Xtags3.txt /‚`‚a‚b - abc41 Xtags3.txt /‚`‚a‚b - abc42 Xtags3.txt /‚`‚a‚b - abc43 Xtags3.txt /‚`‚a‚b - abc44 Xtags3.txt /‚`‚a‚b - abc45 Xtags3.txt /‚`‚a‚b - abc46 Xtags3.txt /‚`‚a‚b - abc47 Xtags3.txt /‚`‚a‚b - abc48 Xtags3.txt /‚`‚a‚b - abc49 Xtags3.txt /‚`‚a‚b - abc50 Xtags3.txt /‚`‚a‚b - abc51 Xtags3.txt /‚`‚a‚b - abc52 Xtags3.txt /‚`‚a‚b - abc53 Xtags3.txt /‚`‚a‚b - abc54 Xtags3.txt /‚`‚a‚b - abc55 Xtags3.txt /‚`‚a‚b - abc56 Xtags3.txt /‚`‚a‚b - abc57 Xtags3.txt /‚`‚a‚b - abc58 Xtags3.txt /‚`‚a‚b - abc59 Xtags3.txt /‚`‚a‚b - abc60 Xtags3.txt /‚`‚a‚b - abc61 Xtags3.txt /‚`‚a‚b - abc62 Xtags3.txt /‚`‚a‚b - abc63 Xtags3.txt /‚`‚a‚b - abc64 Xtags3.txt /‚`‚a‚b - abc65 Xtags3.txt /‚`‚a‚b - abc66 Xtags3.txt /‚`‚a‚b - abc67 Xtags3.txt /‚`‚a‚b - abc68 Xtags3.txt /‚`‚a‚b - abc69 Xtags3.txt /‚`‚a‚b - abc70 Xtags3.txt /‚`‚a‚b - abc71 Xtags3.txt /‚`‚a‚b - abc72 Xtags3.txt /‚`‚a‚b - abc73 Xtags3.txt /‚`‚a‚b - abc74 Xtags3.txt /‚`‚a‚b - abc75 Xtags3.txt /‚`‚a‚b - abc76 Xtags3.txt /‚`‚a‚b - abc77 Xtags3.txt /‚`‚a‚b - abc78 Xtags3.txt /‚`‚a‚b - abc79 Xtags3.txt /‚`‚a‚b - abc80 Xtags3.txt /‚`‚a‚b - abc81 Xtags3.txt /‚`‚a‚b - abc82 Xtags3.txt /‚`‚a‚b - abc83 Xtags3.txt /‚`‚a‚b - abc84 Xtags3.txt /‚`‚a‚b - abc85 Xtags3.txt /‚`‚a‚b - abc86 Xtags3.txt /‚`‚a‚b - abc87 Xtags3.txt /‚`‚a‚b - abc88 Xtags3.txt /‚`‚a‚b - abc89 Xtags3.txt /‚`‚a‚b - abc90 Xtags3.txt /‚`‚a‚b - abc91 Xtags3.txt /‚`‚a‚b - abc92 Xtags3.txt /‚`‚a‚b - abc93 Xtags3.txt /‚`‚a‚b - abc94 Xtags3.txt /‚`‚a‚b - abc95 Xtags3.txt /‚`‚a‚b - abc96 Xtags3.txt /‚`‚a‚b - abc97 Xtags3.txt /‚`‚a‚b - abc98 Xtags3.txt /‚`‚a‚b - abc99 Xtags3.txt /‚`‚a‚b - abc100 Xtags3.txt /‚`‚a‚b --- 0 ---- *** ../vim-8.2.4511/src/testdir/test_tagjump.vim 2022-03-02 20:29:31.610116664 +0000 --- src/testdir/test_tagjump.vim 2022-03-05 14:32:34.779107820 +0000 *************** *** 230,236 **** endfunc " Tests for tag search with !_TAG_FILE_ENCODING. - " Depends on the test83-tags2 and test83-tags3 files. func Test_tag_file_encoding() if has('vms') throw 'Skipped: does not work on VMS' --- 230,235 ---- *************** *** 262,279 **** " case2: new ! set tags=test83-tags2 tag /.BC call assert_equal('Xtags2.txt', expand('%:t')) call assert_equal('ABC', getline('.')) close " case3: new ! set tags=test83-tags3 tag abc50 call assert_equal('Xtags3.txt', expand('%:t')) call assert_equal('ABC', getline('.')) close set tags& --- 261,291 ---- " case2: new ! let content = ['!_TAG_FILE_ENCODING cp932 //', ! \ "\x82`\x82a\x82b Xtags2.txt /\x82`\x82a\x82b"] ! call writefile(content, 'Xtags') ! set tags=Xtags tag /.BC call assert_equal('Xtags2.txt', expand('%:t')) call assert_equal('ABC', getline('.')) + call delete('Xtags') close " case3: new ! let contents = [ ! \ "!_TAG_FILE_SORTED 1 //", ! \ "!_TAG_FILE_ENCODING cp932 //"] ! for i in range(1, 100) ! call add(contents, 'abc' .. i ! \ .. " Xtags3.txt /\x82`\x82a\x82b") ! endfor ! call writefile(contents, 'Xtags') ! set tags=Xtags tag abc50 call assert_equal('Xtags3.txt', expand('%:t')) call assert_equal('ABC', getline('.')) + call delete('Xtags') close set tags& *************** *** 324,329 **** --- 336,342 ---- \ ], 'Xtags2') tag main call assert_equal(2, line('.')) + call assert_fails('tag bar', 'E426:') " corrupted tag line call writefile([ *************** *** 349,354 **** --- 362,388 ---- \ ], 'Xtags') call assert_fails('tag foo', 'E431:') + " end of file after a CTRL-L line + call writefile([ + \ "\x0c", + \ "Xmain.c,64", + \ "void foo() {}\x7ffoo\x011,0", + \ "\x0c", + \ ], 'Xtags') + call assert_fails('tag main', 'E426:') + + " error in an included tags file + call writefile([ + \ "\x0c", + \ "Xtags2,include" + \ ], 'Xtags') + call writefile([ + \ "\x0c", + \ "Xmain.c,64", + \ "void foo() {}", + \ ], 'Xtags2') + call assert_fails('tag foo', 'E431:') + call delete('Xtags') call delete('Xtags2') call delete('Xmain.c') *************** *** 1432,1437 **** --- 1466,1476 ---- \ "foo Xfile 1"], 'Xtags') call assert_fails('tag foo', 'E431:') + " file name and search pattern are not separated by a tab + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "foo\tXfile 1;"], 'Xtags') + call assert_fails('tag foo', 'E431:') + call delete('Xtags') call delete('Xfile') set tags& *** ../vim-8.2.4511/src/version.c 2022-03-05 13:45:52.736741405 +0000 --- src/version.c 2022-03-05 14:34:27.099161708 +0000 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 4512, /**/ -- hundred-and-one symptoms of being an internet addict: 171. You invent another person and chat with yourself in empty chat rooms. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///