People using ctags while working on code which is edited by multiple
people in various editors will be familiar with this situation: the code
ends up with a mix of line endings – some of them are Unix-style, some
of them are DOS-style.
The problem is that if Vim reads such a file with ff=unix, it will fail
to find tags if the tag pattern searched should match on a DOS-style
line. The attached patch handles the problem in a naïve but surprisingly
effective way: if a pattern search fails, Vim will try putting "\r\*"
before the last "$" in the pattern and will retry the search.
Cheers,
Lech
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.
diff --git a/src/tag.c b/src/tag.c
index 34e9c4f..14d5f22 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -3055,7 +3055,7 @@ jumpto_tag(lbuf, forceit, keep_help)
int old_KeyTyped = KeyTyped; /* getting the file may reset it */
#endif
- pbuf = alloc(LSIZE);
+ pbuf = alloc(LSIZE + 3);
/* parse the match line into the tagp structure */
if (pbuf == NULL || parse_match(lbuf, &tagp) == FAIL)
@@ -3220,6 +3220,8 @@ jumpto_tag(lbuf, forceit, keep_help)
str = skip_regexp(pbuf + 1, pbuf[0], FALSE, NULL) + 1;
if (str > pbuf_end - 1) /* search command with nothing following */
{
+ int found;
+
save_p_ws = p_ws;
save_p_ic = p_ic;
save_p_scs = p_scs;
@@ -3234,64 +3236,140 @@ jumpto_tag(lbuf, forceit, keep_help)
#endif
save_lnum = curwin->w_cursor.lnum;
curwin->w_cursor.lnum = 0; /* start search before first line */
- if (do_search(NULL, pbuf[0], pbuf + 1, (long)1,
- search_options, NULL))
- retval = OK;
- else
+
+ enum {
+ TAG_SEARCH_FAILED = 0,
+ TAG_SEARCH_NORMAL = 1,
+ TAG_SEARCH_IGNORE_CASE = 2,
+ TAG_SEARCH_IGNORE_CR = 3,
+ TAG_SEARCH_IGNORE_CASE_CR = 4,
+ TAG_SEARCH_GUESS = 5,
+ };
+
+ for (found = TAG_SEARCH_NORMAL;
+ found <= TAG_SEARCH_IGNORE_CASE_CR;
+ ++found)
{
- int found = 1;
- int cc;
+ if (do_search(NULL, pbuf[0], pbuf + 1, (long)1,
+ search_options, NULL))
+ {
+ retval = OK;
+ break;
+ }
+ switch (found)
+ {
+ case TAG_SEARCH_NORMAL:
+ /*
+ * Try again, ignore case now.
+ */
+ p_ic = TRUE;
+ break;
+ case TAG_SEARCH_IGNORE_CASE:
+ /*
+ * Try again but this time try to ignore any trailing
+ * carriage return characters at the end of the line.
+ * This might help us handle the case when we have
+ * mixed line endings in a file.
+ * Let's append "\r\*" to the regexp but only if it
+ * ends with one of:
+ * - $
+ * - $/
+ * - $/;
+ * - $/;"
+ * We will put \r\* before the $
+ * Note that if the regexp is not a straightforward one
+ * (i.e. /^line-contents$/;" )
+ * we might break it so we only do it if we failed
+ * matching the original regexp.
+ */
+ p_ic = FALSE;
+ if (pbuf_end >= pbuf + 1)
+ {
+ const char *regexp_end = "$/;\"";
+ int len = pbuf_end - pbuf;
+ int i;
+ int j;
+
+ if (len > 4)
+ len = 4;
+
+ for (j = len; j > 0; --j)
+ {
+ if (!STRNCMP(pbuf_end - j, regexp_end, j))
+ {
+ *(pbuf_end - j) = '\r';
+ *(pbuf_end - j + 1) = '\\';
+ *(pbuf_end - j + 2) = '*';
+
+ for (i = 0; i < j; ++i)
+ *(pbuf_end - j + 3 + i) = regexp_end[i];
+ pbuf_end += 3;
+ break;
+ }
+ }
+ }
+ *pbuf_end = NUL;
+ break;
+ case TAG_SEARCH_IGNORE_CR:
+ /*
+ * yet again, ignoring the case
+ */
+ p_ic = TRUE;
+ break;
+ }
+ }
+
+ if (found == TAG_SEARCH_GUESS)
+ {
+ int cc;
+ p_ic = TRUE;
/*
- * try again, ignore case now
+ * Failed to find pattern, take a guess: "^func ("
*/
- p_ic = TRUE;
- if (!do_search(NULL, pbuf[0], pbuf + 1, (long)1,
- search_options, NULL))
+ (void)test_for_static(&tagp);
+ cc = *tagp.tagname_end;
+ *tagp.tagname_end = NUL;
+ sprintf((char *)pbuf, "^%s\\s\\*(", tagp.tagname);
+ if (!do_search(NULL, '/', pbuf, (long)1,
+ search_options, NULL))
{
- /*
- * Failed to find pattern, take a guess: "^func ("
- */
- found = 2;
- (void)test_for_static(&tagp);
- cc = *tagp.tagname_end;
- *tagp.tagname_end = NUL;
- sprintf((char *)pbuf, "^%s\\s\\*(", tagp.tagname);
+ /* Guess again: "^char * \<func (" */
+ sprintf((char *)pbuf, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
+ tagp.tagname);
if (!do_search(NULL, '/', pbuf, (long)1,
- search_options, NULL))
- {
- /* Guess again: "^char * \<func (" */
- sprintf((char *)pbuf, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
- tagp.tagname);
- if (!do_search(NULL, '/', pbuf, (long)1,
- search_options, NULL))
- found = 0;
- }
- *tagp.tagname_end = cc;
+ search_options, NULL))
+ found = TAG_SEARCH_FAILED;
}
- if (found == 0)
- {
- EMSG(_("E434: Can't find tag pattern"));
- curwin->w_cursor.lnum = save_lnum;
- }
- else
+ *tagp.tagname_end = cc;
+ }
+
+ if (found == TAG_SEARCH_FAILED)
+ {
+ EMSG(_("E434: Can't find tag pattern"));
+ curwin->w_cursor.lnum = save_lnum;
+ }
+ else
+ {
+ /*
+ * Only give a message when we guessed or found the match while
+ * ignoring case and 'ignorecase' was not set.
+ */
+ if (found >= TAG_SEARCH_GUESS
+ || (!save_p_ic &&
+ (found == TAG_SEARCH_IGNORE_CASE
+ || found == TAG_SEARCH_IGNORE_CASE_CR)))
{
- /*
- * Only give a message when really guessed, not when 'ic'
- * is set and match found while ignoring case.
- */
- if (found == 2 || !save_p_ic)
+ MSG(_("E435: Couldn't find tag, just guessing!"));
+ if (!msg_scrolled && msg_silent == 0)
{
- MSG(_("E435: Couldn't find tag, just guessing!"));
- if (!msg_scrolled && msg_silent == 0)
- {
- out_flush();
- ui_delay(1000L, TRUE);
- }
+ out_flush();
+ ui_delay(1000L, TRUE);
}
- retval = OK;
}
+ retval = OK;
}
+
p_ws = save_p_ws;
p_ic = save_p_ic;
p_scs = save_p_scs;
diff --git a/src/testdir/Makefile b/src/testdir/Makefile
index 43acc45..bcaed91 100644
--- a/src/testdir/Makefile
+++ b/src/testdir/Makefile
@@ -28,7 +28,8 @@ SCRIPTS = test1.out test2.out test3.out test4.out test5.out test6.out \
test74.out test75.out test76.out test77.out test78.out \
test79.out test80.out test81.out test82.out test83.out \
test84.out test85.out test86.out test87.out test88.out \
- test89.out test90.out test91.out test92.out test93.out
+ test89.out test90.out test91.out test92.out test93.out \
+ test94.out
SCRIPTS_GUI = test16.out
diff --git a/src/testdir/test94.in b/src/testdir/test94.in
new file mode 100644
index 0000000..5cceccb
--- /dev/null
+++ b/src/testdir/test94.in
@@ -0,0 +1,70 @@
+Tests for tags in lines with trailing carriage return characters.
+
+STARTTEST
+:set ffs= ff=unix
+:"write tags to Xtags file
+:/^tags$/+1,/^tags-end$/-1w! Xtags
+:"write file contents to Xcontents.txt file
+:/^file-contents/+1,/^file-contents-end$/-1w! ++ff=unix Xcontents.txt
+
+ggdG
+:call setline('.', 'Results of test 94:')
+:set tags=Xtags
+
+:for tagname in ['foo', 'bar', 'baz', 'quux']
+: new
+: exe 'tag ' . tagname
+: let currline=line('.')
+: redir => messages
+: messages
+: redir END
+: close
+: unlet! lmessages
+: let lmessages=split(messages,'\n')
+: put =(tagname . ' found at line ' . string(currline))
+: put =' total messages number: ' . string(len(lmessages))
+: put =' last one starts with: \"' . split(lmessages[-1])[0] . '\"'
+:endfor
+
+:put ='Results of test 94 with case ignored:'
+:set ignorecase
+:for tagname in ['foo', 'bar', 'baz', 'quux']
+: new
+: exe 'tag ' . tagname
+: let currline=line('.')
+: redir => messages
+: messages
+: redir END
+: close
+: let lmessages=split(messages,'\n')
+: put =(tagname . ' found at line ' . string(currline))
+: put =' total messages number: ' . string(len(lmessages))
+: put =' last one starts with: \"' . split(lmessages[-1])[0] . '\"'
+:endfor
+
+:wq! test.out
+ENDTEST
+
+file-contents
+foo usage
+bar usage
+baz usage
+quux usage
+
+foo definition
+bar definition
+baz definition
+quux definition
+
+foo definition it is not
+bar definition it is not
+baz definition it is not
+quux definition it is not
+file-contents-end
+
+tags
+bar Xcontents.txt /bar definition$/;"
+baz Xcontents.txt /BAZ definition$
+foo Xcontents.txt /foo definition$/;"
+quux Xcontents.txt /QUUX definition$
+tags-end
diff --git a/src/testdir/test94.ok b/src/testdir/test94.ok
new file mode 100644
index 0000000..c3ff539
--- /dev/null
+++ b/src/testdir/test94.ok
@@ -0,0 +1,26 @@
+Results of test 94:
+foo found at line 6
+ total messages number: 10
+ last one starts with: ""Xcontents.txt""
+bar found at line 7
+ total messages number: 11
+ last one starts with: ""Xcontents.txt""
+baz found at line 8
+ total messages number: 13
+ last one starts with: "E435:"
+quux found at line 9
+ total messages number: 15
+ last one starts with: "E435:"
+Results of test 94 with case ignored:
+foo found at line 6
+ total messages number: 16
+ last one starts with: ""Xcontents.txt""
+bar found at line 7
+ total messages number: 17
+ last one starts with: ""Xcontents.txt""
+baz found at line 8
+ total messages number: 18
+ last one starts with: ""Xcontents.txt""
+quux found at line 9
+ total messages number: 19
+ last one starts with: ""Xcontents.txt""