Mercurial > vim
comparison src/tag.c @ 16188:848d4c6e391e v8.1.1099
patch 8.1.1099: the do_tag() function is too long
commit https://github.com/vim/vim/commit/b4a6020ac6a0638167013f1e45ff440ddc8a1671
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Mar 31 19:40:07 2019 +0200
patch 8.1.1099: the do_tag() function is too long
Problem: The do_tag() function is too long.
Solution: Factor parts out to separate functions. Move simplify_filename()
to a file where it fits better. (Andy Massimino, closes #4195)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 31 Mar 2019 19:45:05 +0200 |
parents | a8689ea2e869 |
children | d863beec391a |
comparison
equal
deleted
inserted
replaced
16187:97b99ffd8aeb | 16188:848d4c6e391e |
---|---|
72 static int test_for_current(int, char_u *, char_u *, char_u *, char_u *); | 72 static int test_for_current(int, char_u *, char_u *, char_u *, char_u *); |
73 #else | 73 #else |
74 static int test_for_current(char_u *, char_u *, char_u *, char_u *); | 74 static int test_for_current(char_u *, char_u *, char_u *, char_u *); |
75 #endif | 75 #endif |
76 static int find_extra(char_u **pp); | 76 static int find_extra(char_u **pp); |
77 static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_u **matches); | |
78 #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL) | |
79 static int add_llist_tags(char_u *tag, int num_matches, char_u **matches); | |
80 #endif | |
77 | 81 |
78 static char_u *bottommsg = (char_u *)N_("E555: at bottom of tag stack"); | 82 static char_u *bottommsg = (char_u *)N_("E555: at bottom of tag stack"); |
79 static char_u *topmsg = (char_u *)N_("E556: at top of tag stack"); | 83 static char_u *topmsg = (char_u *)N_("E556: at top of tag stack"); |
80 | 84 |
81 static char_u *tagmatchname = NULL; /* name of last used tag */ | 85 static char_u *tagmatchname = NULL; /* name of last used tag */ |
123 int cur_fnum = curbuf->b_fnum; | 127 int cur_fnum = curbuf->b_fnum; |
124 int oldtagstackidx = tagstackidx; | 128 int oldtagstackidx = tagstackidx; |
125 int prevtagstackidx = tagstackidx; | 129 int prevtagstackidx = tagstackidx; |
126 int prev_num_matches; | 130 int prev_num_matches; |
127 int new_tag = FALSE; | 131 int new_tag = FALSE; |
128 int other_name; | 132 int i; |
129 int i, j, k; | |
130 int idx; | |
131 int ic; | 133 int ic; |
132 char_u *p; | |
133 char_u *name; | |
134 int no_regexp = FALSE; | 134 int no_regexp = FALSE; |
135 int error_cur_match = 0; | 135 int error_cur_match = 0; |
136 char_u *command_end; | |
137 int save_pos = FALSE; | 136 int save_pos = FALSE; |
138 fmark_T saved_fmark; | 137 fmark_T saved_fmark; |
139 int taglen; | |
140 #ifdef FEAT_CSCOPE | 138 #ifdef FEAT_CSCOPE |
141 int jumped_to_tag = FALSE; | 139 int jumped_to_tag = FALSE; |
142 #endif | 140 #endif |
143 tagptrs_T tagp, tagp2; | |
144 int new_num_matches; | 141 int new_num_matches; |
145 char_u **new_matches; | 142 char_u **new_matches; |
146 int attr; | |
147 int use_tagstack; | 143 int use_tagstack; |
148 int skip_msg = FALSE; | 144 int skip_msg = FALSE; |
149 char_u *buf_ffname = curbuf->b_ffname; /* name to use for | 145 char_u *buf_ffname = curbuf->b_ffname; /* name to use for |
150 priority computation */ | 146 priority computation */ |
151 | 147 |
480 /* | 476 /* |
481 * Repeat searching for tags, when a file has not been found. | 477 * Repeat searching for tags, when a file has not been found. |
482 */ | 478 */ |
483 for (;;) | 479 for (;;) |
484 { | 480 { |
481 int other_name; | |
482 char_u *name; | |
483 | |
485 /* | 484 /* |
486 * When desired match not found yet, try to find it (and others). | 485 * When desired match not found yet, try to find it (and others). |
487 */ | 486 */ |
488 if (use_tagstack) | 487 if (use_tagstack) |
489 name = tagstack[tagstackidx].tagname; | 488 name = tagstack[tagstackidx].tagname; |
539 /* If there already were some matches for the same name, move them | 538 /* If there already were some matches for the same name, move them |
540 * to the start. Avoids that the order changes when using | 539 * to the start. Avoids that the order changes when using |
541 * ":tnext" and jumping to another file. */ | 540 * ":tnext" and jumping to another file. */ |
542 if (!new_tag && !other_name) | 541 if (!new_tag && !other_name) |
543 { | 542 { |
543 int j, k; | |
544 int idx = 0; | |
545 tagptrs_T tagp, tagp2; | |
546 | |
544 /* Find the position of each old match in the new list. Need | 547 /* Find the position of each old match in the new list. Need |
545 * to use parse_match() to find the tag line. */ | 548 * to use parse_match() to find the tag line. */ |
546 idx = 0; | |
547 for (j = 0; j < num_matches; ++j) | 549 for (j = 0; j < num_matches; ++j) |
548 { | 550 { |
549 parse_match(matches[j], &tagp); | 551 parse_match(matches[j], &tagp); |
550 for (i = idx; i < new_num_matches; ++i) | 552 for (i = idx; i < new_num_matches; ++i) |
551 { | 553 { |
552 parse_match(new_matches[i], &tagp2); | 554 parse_match(new_matches[i], &tagp2); |
553 if (STRCMP(tagp.tagname, tagp2.tagname) == 0) | 555 if (STRCMP(tagp.tagname, tagp2.tagname) == 0) |
554 { | 556 { |
555 p = new_matches[i]; | 557 char_u *p = new_matches[i]; |
556 for (k = i; k > idx; --k) | 558 for (k = i; k > idx; --k) |
557 new_matches[k] = new_matches[k - 1]; | 559 new_matches[k] = new_matches[k - 1]; |
558 new_matches[idx++] = p; | 560 new_matches[idx++] = p; |
559 break; | 561 break; |
560 } | 562 } |
585 ask_for_selection = TRUE; | 587 ask_for_selection = TRUE; |
586 } | 588 } |
587 else | 589 else |
588 #endif | 590 #endif |
589 if (type == DT_TAG && *tag != NUL) | 591 if (type == DT_TAG && *tag != NUL) |
590 /* | 592 // If a count is supplied to the ":tag <name>" command, then |
591 * If a count is supplied to the ":tag <name>" command, then | 593 // jump to count'th matching tag. |
592 * jump to count'th matching tag. | |
593 */ | |
594 cur_match = count > 0 ? count - 1 : 0; | 594 cur_match = count > 0 ? count - 1 : 0; |
595 else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) | 595 else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) |
596 { | 596 { |
597 /* | 597 print_tag_list(new_tag, use_tagstack, num_matches, matches); |
598 * List all the matching tags. | |
599 * Assume that the first match indicates how long the tags can | |
600 * be, and align the file names to that. | |
601 */ | |
602 parse_match(matches[0], &tagp); | |
603 taglen = (int)(tagp.tagname_end - tagp.tagname + 2); | |
604 if (taglen < 18) | |
605 taglen = 18; | |
606 if (taglen > Columns - 25) | |
607 taglen = MAXCOL; | |
608 if (msg_col == 0) | |
609 msg_didout = FALSE; /* overwrite previous message */ | |
610 msg_start(); | |
611 msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T)); | |
612 msg_clr_eos(); | |
613 taglen_advance(taglen); | |
614 msg_puts_attr(_("file\n"), HL_ATTR(HLF_T)); | |
615 | |
616 for (i = 0; i < num_matches && !got_int; ++i) | |
617 { | |
618 parse_match(matches[i], &tagp); | |
619 if (!new_tag && ( | |
620 #if defined(FEAT_QUICKFIX) | |
621 (g_do_tagpreview != 0 | |
622 && i == ptag_entry.cur_match) || | |
623 #endif | |
624 (use_tagstack | |
625 && i == tagstack[tagstackidx].cur_match))) | |
626 *IObuff = '>'; | |
627 else | |
628 *IObuff = ' '; | |
629 vim_snprintf((char *)IObuff + 1, IOSIZE - 1, | |
630 "%2d %s ", i + 1, | |
631 mt_names[matches[i][0] & MT_MASK]); | |
632 msg_puts((char *)IObuff); | |
633 if (tagp.tagkind != NULL) | |
634 msg_outtrans_len(tagp.tagkind, | |
635 (int)(tagp.tagkind_end - tagp.tagkind)); | |
636 msg_advance(13); | |
637 msg_outtrans_len_attr(tagp.tagname, | |
638 (int)(tagp.tagname_end - tagp.tagname), | |
639 HL_ATTR(HLF_T)); | |
640 msg_putchar(' '); | |
641 taglen_advance(taglen); | |
642 | |
643 /* Find out the actual file name. If it is long, truncate | |
644 * it and put "..." in the middle */ | |
645 p = tag_full_fname(&tagp); | |
646 if (p != NULL) | |
647 { | |
648 msg_outtrans_long_attr(p, HL_ATTR(HLF_D)); | |
649 vim_free(p); | |
650 } | |
651 if (msg_col > 0) | |
652 msg_putchar('\n'); | |
653 if (got_int) | |
654 break; | |
655 msg_advance(15); | |
656 | |
657 /* print any extra fields */ | |
658 command_end = tagp.command_end; | |
659 if (command_end != NULL) | |
660 { | |
661 p = command_end + 3; | |
662 while (*p && *p != '\r' && *p != '\n') | |
663 { | |
664 while (*p == TAB) | |
665 ++p; | |
666 | |
667 /* skip "file:" without a value (static tag) */ | |
668 if (STRNCMP(p, "file:", 5) == 0 | |
669 && vim_isspace(p[5])) | |
670 { | |
671 p += 5; | |
672 continue; | |
673 } | |
674 /* skip "kind:<kind>" and "<kind>" */ | |
675 if (p == tagp.tagkind | |
676 || (p + 5 == tagp.tagkind | |
677 && STRNCMP(p, "kind:", 5) == 0)) | |
678 { | |
679 p = tagp.tagkind_end; | |
680 continue; | |
681 } | |
682 /* print all other extra fields */ | |
683 attr = HL_ATTR(HLF_CM); | |
684 while (*p && *p != '\r' && *p != '\n') | |
685 { | |
686 if (msg_col + ptr2cells(p) >= Columns) | |
687 { | |
688 msg_putchar('\n'); | |
689 if (got_int) | |
690 break; | |
691 msg_advance(15); | |
692 } | |
693 p = msg_outtrans_one(p, attr); | |
694 if (*p == TAB) | |
695 { | |
696 msg_puts_attr(" ", attr); | |
697 break; | |
698 } | |
699 if (*p == ':') | |
700 attr = 0; | |
701 } | |
702 } | |
703 if (msg_col > 15) | |
704 { | |
705 msg_putchar('\n'); | |
706 if (got_int) | |
707 break; | |
708 msg_advance(15); | |
709 } | |
710 } | |
711 else | |
712 { | |
713 for (p = tagp.command; | |
714 *p && *p != '\r' && *p != '\n'; ++p) | |
715 ; | |
716 command_end = p; | |
717 } | |
718 | |
719 /* | |
720 * Put the info (in several lines) at column 15. | |
721 * Don't display "/^" and "?^". | |
722 */ | |
723 p = tagp.command; | |
724 if (*p == '/' || *p == '?') | |
725 { | |
726 ++p; | |
727 if (*p == '^') | |
728 ++p; | |
729 } | |
730 /* Remove leading whitespace from pattern */ | |
731 while (p != command_end && vim_isspace(*p)) | |
732 ++p; | |
733 | |
734 while (p != command_end) | |
735 { | |
736 if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) | |
737 msg_putchar('\n'); | |
738 if (got_int) | |
739 break; | |
740 msg_advance(15); | |
741 | |
742 /* skip backslash used for escaping a command char or | |
743 * a backslash */ | |
744 if (*p == '\\' && (*(p + 1) == *tagp.command | |
745 || *(p + 1) == '\\')) | |
746 ++p; | |
747 | |
748 if (*p == TAB) | |
749 { | |
750 msg_putchar(' '); | |
751 ++p; | |
752 } | |
753 else | |
754 p = msg_outtrans_one(p, 0); | |
755 | |
756 /* don't display the "$/;\"" and "$?;\"" */ | |
757 if (p == command_end - 2 && *p == '$' | |
758 && *(p + 1) == *tagp.command) | |
759 break; | |
760 /* don't display matching '/' or '?' */ | |
761 if (p == command_end - 1 && *p == *tagp.command | |
762 && (*p == '/' || *p == '?')) | |
763 break; | |
764 } | |
765 if (msg_col) | |
766 msg_putchar('\n'); | |
767 ui_breakcheck(); | |
768 } | |
769 if (got_int) | |
770 got_int = FALSE; /* only stop the listing */ | |
771 ask_for_selection = TRUE; | 598 ask_for_selection = TRUE; |
772 } | 599 } |
773 #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL) | 600 #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL) |
774 else if (type == DT_LTAG) | 601 else if (type == DT_LTAG) |
775 { | 602 { |
776 list_T *list; | 603 if (add_llist_tags(tag, num_matches, matches) == FAIL) |
777 char_u tag_name[128 + 1]; | |
778 char_u *fname; | |
779 char_u *cmd; | |
780 | |
781 /* | |
782 * Add the matching tags to the location list for the current | |
783 * window. | |
784 */ | |
785 | |
786 fname = alloc(MAXPATHL + 1); | |
787 cmd = alloc(CMDBUFFSIZE + 1); | |
788 list = list_alloc(); | |
789 if (list == NULL || fname == NULL || cmd == NULL) | |
790 { | |
791 vim_free(cmd); | |
792 vim_free(fname); | |
793 if (list != NULL) | |
794 list_free(list); | |
795 goto end_do_tag; | 604 goto end_do_tag; |
796 } | |
797 | |
798 for (i = 0; i < num_matches; ++i) | |
799 { | |
800 int len, cmd_len; | |
801 long lnum; | |
802 dict_T *dict; | |
803 | |
804 parse_match(matches[i], &tagp); | |
805 | |
806 /* Save the tag name */ | |
807 len = (int)(tagp.tagname_end - tagp.tagname); | |
808 if (len > 128) | |
809 len = 128; | |
810 vim_strncpy(tag_name, tagp.tagname, len); | |
811 tag_name[len] = NUL; | |
812 | |
813 /* Save the tag file name */ | |
814 p = tag_full_fname(&tagp); | |
815 if (p == NULL) | |
816 continue; | |
817 vim_strncpy(fname, p, MAXPATHL); | |
818 vim_free(p); | |
819 | |
820 /* | |
821 * Get the line number or the search pattern used to locate | |
822 * the tag. | |
823 */ | |
824 lnum = 0; | |
825 if (isdigit(*tagp.command)) | |
826 /* Line number is used to locate the tag */ | |
827 lnum = atol((char *)tagp.command); | |
828 else | |
829 { | |
830 char_u *cmd_start, *cmd_end; | |
831 | |
832 /* Search pattern is used to locate the tag */ | |
833 | |
834 /* Locate the end of the command */ | |
835 cmd_start = tagp.command; | |
836 cmd_end = tagp.command_end; | |
837 if (cmd_end == NULL) | |
838 { | |
839 for (p = tagp.command; | |
840 *p && *p != '\r' && *p != '\n'; ++p) | |
841 ; | |
842 cmd_end = p; | |
843 } | |
844 | |
845 /* | |
846 * Now, cmd_end points to the character after the | |
847 * command. Adjust it to point to the last | |
848 * character of the command. | |
849 */ | |
850 cmd_end--; | |
851 | |
852 /* | |
853 * Skip the '/' and '?' characters at the | |
854 * beginning and end of the search pattern. | |
855 */ | |
856 if (*cmd_start == '/' || *cmd_start == '?') | |
857 cmd_start++; | |
858 | |
859 if (*cmd_end == '/' || *cmd_end == '?') | |
860 cmd_end--; | |
861 | |
862 len = 0; | |
863 cmd[0] = NUL; | |
864 | |
865 /* | |
866 * If "^" is present in the tag search pattern, then | |
867 * copy it first. | |
868 */ | |
869 if (*cmd_start == '^') | |
870 { | |
871 STRCPY(cmd, "^"); | |
872 cmd_start++; | |
873 len++; | |
874 } | |
875 | |
876 /* | |
877 * Precede the tag pattern with \V to make it very | |
878 * nomagic. | |
879 */ | |
880 STRCAT(cmd, "\\V"); | |
881 len += 2; | |
882 | |
883 cmd_len = (int)(cmd_end - cmd_start + 1); | |
884 if (cmd_len > (CMDBUFFSIZE - 5)) | |
885 cmd_len = CMDBUFFSIZE - 5; | |
886 STRNCAT(cmd, cmd_start, cmd_len); | |
887 len += cmd_len; | |
888 | |
889 if (cmd[len - 1] == '$') | |
890 { | |
891 /* | |
892 * Replace '$' at the end of the search pattern | |
893 * with '\$' | |
894 */ | |
895 cmd[len - 1] = '\\'; | |
896 cmd[len] = '$'; | |
897 len++; | |
898 } | |
899 | |
900 cmd[len] = NUL; | |
901 } | |
902 | |
903 if ((dict = dict_alloc()) == NULL) | |
904 continue; | |
905 if (list_append_dict(list, dict) == FAIL) | |
906 { | |
907 vim_free(dict); | |
908 continue; | |
909 } | |
910 | |
911 dict_add_string(dict, "text", tag_name); | |
912 dict_add_string(dict, "filename", fname); | |
913 dict_add_number(dict, "lnum", lnum); | |
914 if (lnum == 0) | |
915 dict_add_string(dict, "pattern", cmd); | |
916 } | |
917 | |
918 vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); | |
919 set_errorlist(curwin, list, ' ', IObuff, NULL); | |
920 | |
921 list_free(list); | |
922 vim_free(fname); | |
923 vim_free(cmd); | |
924 | |
925 cur_match = 0; /* Jump to the first tag */ | 605 cur_match = 0; /* Jump to the first tag */ |
926 } | 606 } |
927 #endif | 607 #endif |
928 | 608 |
929 if (ask_for_selection == TRUE) | 609 if (ask_for_selection == TRUE) |
1085 return jumped_to_tag; | 765 return jumped_to_tag; |
1086 #else | 766 #else |
1087 return FALSE; | 767 return FALSE; |
1088 #endif | 768 #endif |
1089 } | 769 } |
770 | |
771 /* | |
772 * List all the matching tags. | |
773 */ | |
774 static void | |
775 print_tag_list( | |
776 int new_tag, | |
777 int use_tagstack, | |
778 int num_matches, | |
779 char_u **matches) | |
780 { | |
781 taggy_T *tagstack = curwin->w_tagstack; | |
782 int tagstackidx = curwin->w_tagstackidx; | |
783 int i; | |
784 char_u *p; | |
785 char_u *command_end; | |
786 tagptrs_T tagp; | |
787 int taglen; | |
788 int attr; | |
789 | |
790 /* | |
791 * Assume that the first match indicates how long the tags can | |
792 * be, and align the file names to that. | |
793 */ | |
794 parse_match(matches[0], &tagp); | |
795 taglen = (int)(tagp.tagname_end - tagp.tagname + 2); | |
796 if (taglen < 18) | |
797 taglen = 18; | |
798 if (taglen > Columns - 25) | |
799 taglen = MAXCOL; | |
800 if (msg_col == 0) | |
801 msg_didout = FALSE; // overwrite previous message | |
802 msg_start(); | |
803 msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T)); | |
804 msg_clr_eos(); | |
805 taglen_advance(taglen); | |
806 msg_puts_attr(_("file\n"), HL_ATTR(HLF_T)); | |
807 | |
808 for (i = 0; i < num_matches && !got_int; ++i) | |
809 { | |
810 parse_match(matches[i], &tagp); | |
811 if (!new_tag && ( | |
812 #if defined(FEAT_QUICKFIX) | |
813 (g_do_tagpreview != 0 | |
814 && i == ptag_entry.cur_match) || | |
815 #endif | |
816 (use_tagstack | |
817 && i == tagstack[tagstackidx].cur_match))) | |
818 *IObuff = '>'; | |
819 else | |
820 *IObuff = ' '; | |
821 vim_snprintf((char *)IObuff + 1, IOSIZE - 1, | |
822 "%2d %s ", i + 1, | |
823 mt_names[matches[i][0] & MT_MASK]); | |
824 msg_puts((char *)IObuff); | |
825 if (tagp.tagkind != NULL) | |
826 msg_outtrans_len(tagp.tagkind, | |
827 (int)(tagp.tagkind_end - tagp.tagkind)); | |
828 msg_advance(13); | |
829 msg_outtrans_len_attr(tagp.tagname, | |
830 (int)(tagp.tagname_end - tagp.tagname), | |
831 HL_ATTR(HLF_T)); | |
832 msg_putchar(' '); | |
833 taglen_advance(taglen); | |
834 | |
835 // Find out the actual file name. If it is long, truncate | |
836 // it and put "..." in the middle | |
837 p = tag_full_fname(&tagp); | |
838 if (p != NULL) | |
839 { | |
840 msg_outtrans_long_attr(p, HL_ATTR(HLF_D)); | |
841 vim_free(p); | |
842 } | |
843 if (msg_col > 0) | |
844 msg_putchar('\n'); | |
845 if (got_int) | |
846 break; | |
847 msg_advance(15); | |
848 | |
849 // print any extra fields | |
850 command_end = tagp.command_end; | |
851 if (command_end != NULL) | |
852 { | |
853 p = command_end + 3; | |
854 while (*p && *p != '\r' && *p != '\n') | |
855 { | |
856 while (*p == TAB) | |
857 ++p; | |
858 | |
859 // skip "file:" without a value (static tag) | |
860 if (STRNCMP(p, "file:", 5) == 0 | |
861 && vim_isspace(p[5])) | |
862 { | |
863 p += 5; | |
864 continue; | |
865 } | |
866 // skip "kind:<kind>" and "<kind>" | |
867 if (p == tagp.tagkind | |
868 || (p + 5 == tagp.tagkind | |
869 && STRNCMP(p, "kind:", 5) == 0)) | |
870 { | |
871 p = tagp.tagkind_end; | |
872 continue; | |
873 } | |
874 // print all other extra fields | |
875 attr = HL_ATTR(HLF_CM); | |
876 while (*p && *p != '\r' && *p != '\n') | |
877 { | |
878 if (msg_col + ptr2cells(p) >= Columns) | |
879 { | |
880 msg_putchar('\n'); | |
881 if (got_int) | |
882 break; | |
883 msg_advance(15); | |
884 } | |
885 p = msg_outtrans_one(p, attr); | |
886 if (*p == TAB) | |
887 { | |
888 msg_puts_attr(" ", attr); | |
889 break; | |
890 } | |
891 if (*p == ':') | |
892 attr = 0; | |
893 } | |
894 } | |
895 if (msg_col > 15) | |
896 { | |
897 msg_putchar('\n'); | |
898 if (got_int) | |
899 break; | |
900 msg_advance(15); | |
901 } | |
902 } | |
903 else | |
904 { | |
905 for (p = tagp.command; | |
906 *p && *p != '\r' && *p != '\n'; ++p) | |
907 ; | |
908 command_end = p; | |
909 } | |
910 | |
911 // Put the info (in several lines) at column 15. | |
912 // Don't display "/^" and "?^". | |
913 p = tagp.command; | |
914 if (*p == '/' || *p == '?') | |
915 { | |
916 ++p; | |
917 if (*p == '^') | |
918 ++p; | |
919 } | |
920 // Remove leading whitespace from pattern | |
921 while (p != command_end && vim_isspace(*p)) | |
922 ++p; | |
923 | |
924 while (p != command_end) | |
925 { | |
926 if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) | |
927 msg_putchar('\n'); | |
928 if (got_int) | |
929 break; | |
930 msg_advance(15); | |
931 | |
932 // skip backslash used for escaping a command char or | |
933 // a backslash | |
934 if (*p == '\\' && (*(p + 1) == *tagp.command | |
935 || *(p + 1) == '\\')) | |
936 ++p; | |
937 | |
938 if (*p == TAB) | |
939 { | |
940 msg_putchar(' '); | |
941 ++p; | |
942 } | |
943 else | |
944 p = msg_outtrans_one(p, 0); | |
945 | |
946 // don't display the "$/;\"" and "$?;\"" | |
947 if (p == command_end - 2 && *p == '$' | |
948 && *(p + 1) == *tagp.command) | |
949 break; | |
950 // don't display matching '/' or '?' | |
951 if (p == command_end - 1 && *p == *tagp.command | |
952 && (*p == '/' || *p == '?')) | |
953 break; | |
954 } | |
955 if (msg_col) | |
956 msg_putchar('\n'); | |
957 ui_breakcheck(); | |
958 } | |
959 if (got_int) | |
960 got_int = FALSE; // only stop the listing | |
961 } | |
962 | |
963 #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL) | |
964 /* | |
965 * Add the matching tags to the location list for the current | |
966 * window. | |
967 */ | |
968 static int | |
969 add_llist_tags( | |
970 char_u *tag, | |
971 int num_matches, | |
972 char_u **matches) | |
973 { | |
974 list_T *list; | |
975 char_u tag_name[128 + 1]; | |
976 char_u *fname; | |
977 char_u *cmd; | |
978 int i; | |
979 char_u *p; | |
980 tagptrs_T tagp; | |
981 | |
982 fname = alloc(MAXPATHL + 1); | |
983 cmd = alloc(CMDBUFFSIZE + 1); | |
984 list = list_alloc(); | |
985 if (list == NULL || fname == NULL || cmd == NULL) | |
986 { | |
987 vim_free(cmd); | |
988 vim_free(fname); | |
989 if (list != NULL) | |
990 list_free(list); | |
991 return FAIL; | |
992 } | |
993 | |
994 for (i = 0; i < num_matches; ++i) | |
995 { | |
996 int len, cmd_len; | |
997 long lnum; | |
998 dict_T *dict; | |
999 | |
1000 parse_match(matches[i], &tagp); | |
1001 | |
1002 /* Save the tag name */ | |
1003 len = (int)(tagp.tagname_end - tagp.tagname); | |
1004 if (len > 128) | |
1005 len = 128; | |
1006 vim_strncpy(tag_name, tagp.tagname, len); | |
1007 tag_name[len] = NUL; | |
1008 | |
1009 // Save the tag file name | |
1010 p = tag_full_fname(&tagp); | |
1011 if (p == NULL) | |
1012 continue; | |
1013 vim_strncpy(fname, p, MAXPATHL); | |
1014 vim_free(p); | |
1015 | |
1016 // Get the line number or the search pattern used to locate | |
1017 // the tag. | |
1018 lnum = 0; | |
1019 if (isdigit(*tagp.command)) | |
1020 // Line number is used to locate the tag | |
1021 lnum = atol((char *)tagp.command); | |
1022 else | |
1023 { | |
1024 char_u *cmd_start, *cmd_end; | |
1025 | |
1026 // Search pattern is used to locate the tag | |
1027 | |
1028 // Locate the end of the command | |
1029 cmd_start = tagp.command; | |
1030 cmd_end = tagp.command_end; | |
1031 if (cmd_end == NULL) | |
1032 { | |
1033 for (p = tagp.command; | |
1034 *p && *p != '\r' && *p != '\n'; ++p) | |
1035 ; | |
1036 cmd_end = p; | |
1037 } | |
1038 | |
1039 // Now, cmd_end points to the character after the | |
1040 // command. Adjust it to point to the last | |
1041 // character of the command. | |
1042 cmd_end--; | |
1043 | |
1044 // Skip the '/' and '?' characters at the | |
1045 // beginning and end of the search pattern. | |
1046 if (*cmd_start == '/' || *cmd_start == '?') | |
1047 cmd_start++; | |
1048 | |
1049 if (*cmd_end == '/' || *cmd_end == '?') | |
1050 cmd_end--; | |
1051 | |
1052 len = 0; | |
1053 cmd[0] = NUL; | |
1054 | |
1055 // If "^" is present in the tag search pattern, then | |
1056 // copy it first. | |
1057 if (*cmd_start == '^') | |
1058 { | |
1059 STRCPY(cmd, "^"); | |
1060 cmd_start++; | |
1061 len++; | |
1062 } | |
1063 | |
1064 // Precede the tag pattern with \V to make it very | |
1065 // nomagic. | |
1066 STRCAT(cmd, "\\V"); | |
1067 len += 2; | |
1068 | |
1069 cmd_len = (int)(cmd_end - cmd_start + 1); | |
1070 if (cmd_len > (CMDBUFFSIZE - 5)) | |
1071 cmd_len = CMDBUFFSIZE - 5; | |
1072 STRNCAT(cmd, cmd_start, cmd_len); | |
1073 len += cmd_len; | |
1074 | |
1075 if (cmd[len - 1] == '$') | |
1076 { | |
1077 // Replace '$' at the end of the search pattern | |
1078 // with '\$' | |
1079 cmd[len - 1] = '\\'; | |
1080 cmd[len] = '$'; | |
1081 len++; | |
1082 } | |
1083 | |
1084 cmd[len] = NUL; | |
1085 } | |
1086 | |
1087 if ((dict = dict_alloc()) == NULL) | |
1088 continue; | |
1089 if (list_append_dict(list, dict) == FAIL) | |
1090 { | |
1091 vim_free(dict); | |
1092 continue; | |
1093 } | |
1094 | |
1095 dict_add_string(dict, "text", tag_name); | |
1096 dict_add_string(dict, "filename", fname); | |
1097 dict_add_number(dict, "lnum", lnum); | |
1098 if (lnum == 0) | |
1099 dict_add_string(dict, "pattern", cmd); | |
1100 } | |
1101 | |
1102 vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); | |
1103 set_errorlist(curwin, list, ' ', IObuff, NULL); | |
1104 | |
1105 list_free(list); | |
1106 vim_free(fname); | |
1107 vim_free(cmd); | |
1108 | |
1109 return OK; | |
1110 } | |
1111 #endif | |
1090 | 1112 |
1091 /* | 1113 /* |
1092 * Free cached tags. | 1114 * Free cached tags. |
1093 */ | 1115 */ |
1094 void | 1116 void |
3429 | 3451 |
3430 return retval; | 3452 return retval; |
3431 } | 3453 } |
3432 | 3454 |
3433 /* | 3455 /* |
3434 * Converts a file name into a canonical form. It simplifies a file name into | |
3435 * its simplest form by stripping out unneeded components, if any. The | |
3436 * resulting file name is simplified in place and will either be the same | |
3437 * length as that supplied, or shorter. | |
3438 */ | |
3439 void | |
3440 simplify_filename(char_u *filename) | |
3441 { | |
3442 #ifndef AMIGA /* Amiga doesn't have "..", it uses "/" */ | |
3443 int components = 0; | |
3444 char_u *p, *tail, *start; | |
3445 int stripping_disabled = FALSE; | |
3446 int relative = TRUE; | |
3447 | |
3448 p = filename; | |
3449 #ifdef BACKSLASH_IN_FILENAME | |
3450 if (p[1] == ':') /* skip "x:" */ | |
3451 p += 2; | |
3452 #endif | |
3453 | |
3454 if (vim_ispathsep(*p)) | |
3455 { | |
3456 relative = FALSE; | |
3457 do | |
3458 ++p; | |
3459 while (vim_ispathsep(*p)); | |
3460 } | |
3461 start = p; /* remember start after "c:/" or "/" or "///" */ | |
3462 | |
3463 do | |
3464 { | |
3465 /* At this point "p" is pointing to the char following a single "/" | |
3466 * or "p" is at the "start" of the (absolute or relative) path name. */ | |
3467 #ifdef VMS | |
3468 /* VMS allows device:[path] - don't strip the [ in directory */ | |
3469 if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':') | |
3470 { | |
3471 /* :[ or :< composition: vms directory component */ | |
3472 ++components; | |
3473 p = getnextcomp(p + 1); | |
3474 } | |
3475 /* allow remote calls as host"user passwd"::device:[path] */ | |
3476 else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' ) | |
3477 { | |
3478 /* ":: composition: vms host/passwd component */ | |
3479 ++components; | |
3480 p = getnextcomp(p + 2); | |
3481 } | |
3482 else | |
3483 #endif | |
3484 if (vim_ispathsep(*p)) | |
3485 STRMOVE(p, p + 1); /* remove duplicate "/" */ | |
3486 else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL)) | |
3487 { | |
3488 if (p == start && relative) | |
3489 p += 1 + (p[1] != NUL); /* keep single "." or leading "./" */ | |
3490 else | |
3491 { | |
3492 /* Strip "./" or ".///". If we are at the end of the file name | |
3493 * and there is no trailing path separator, either strip "/." if | |
3494 * we are after "start", or strip "." if we are at the beginning | |
3495 * of an absolute path name . */ | |
3496 tail = p + 1; | |
3497 if (p[1] != NUL) | |
3498 while (vim_ispathsep(*tail)) | |
3499 MB_PTR_ADV(tail); | |
3500 else if (p > start) | |
3501 --p; /* strip preceding path separator */ | |
3502 STRMOVE(p, tail); | |
3503 } | |
3504 } | |
3505 else if (p[0] == '.' && p[1] == '.' && | |
3506 (vim_ispathsep(p[2]) || p[2] == NUL)) | |
3507 { | |
3508 /* Skip to after ".." or "../" or "..///". */ | |
3509 tail = p + 2; | |
3510 while (vim_ispathsep(*tail)) | |
3511 MB_PTR_ADV(tail); | |
3512 | |
3513 if (components > 0) /* strip one preceding component */ | |
3514 { | |
3515 int do_strip = FALSE; | |
3516 char_u saved_char; | |
3517 stat_T st; | |
3518 | |
3519 /* Don't strip for an erroneous file name. */ | |
3520 if (!stripping_disabled) | |
3521 { | |
3522 /* If the preceding component does not exist in the file | |
3523 * system, we strip it. On Unix, we don't accept a symbolic | |
3524 * link that refers to a non-existent file. */ | |
3525 saved_char = p[-1]; | |
3526 p[-1] = NUL; | |
3527 #ifdef UNIX | |
3528 if (mch_lstat((char *)filename, &st) < 0) | |
3529 #else | |
3530 if (mch_stat((char *)filename, &st) < 0) | |
3531 #endif | |
3532 do_strip = TRUE; | |
3533 p[-1] = saved_char; | |
3534 | |
3535 --p; | |
3536 /* Skip back to after previous '/'. */ | |
3537 while (p > start && !after_pathsep(start, p)) | |
3538 MB_PTR_BACK(start, p); | |
3539 | |
3540 if (!do_strip) | |
3541 { | |
3542 /* If the component exists in the file system, check | |
3543 * that stripping it won't change the meaning of the | |
3544 * file name. First get information about the | |
3545 * unstripped file name. This may fail if the component | |
3546 * to strip is not a searchable directory (but a regular | |
3547 * file, for instance), since the trailing "/.." cannot | |
3548 * be applied then. We don't strip it then since we | |
3549 * don't want to replace an erroneous file name by | |
3550 * a valid one, and we disable stripping of later | |
3551 * components. */ | |
3552 saved_char = *tail; | |
3553 *tail = NUL; | |
3554 if (mch_stat((char *)filename, &st) >= 0) | |
3555 do_strip = TRUE; | |
3556 else | |
3557 stripping_disabled = TRUE; | |
3558 *tail = saved_char; | |
3559 #ifdef UNIX | |
3560 if (do_strip) | |
3561 { | |
3562 stat_T new_st; | |
3563 | |
3564 /* On Unix, the check for the unstripped file name | |
3565 * above works also for a symbolic link pointing to | |
3566 * a searchable directory. But then the parent of | |
3567 * the directory pointed to by the link must be the | |
3568 * same as the stripped file name. (The latter | |
3569 * exists in the file system since it is the | |
3570 * component's parent directory.) */ | |
3571 if (p == start && relative) | |
3572 (void)mch_stat(".", &new_st); | |
3573 else | |
3574 { | |
3575 saved_char = *p; | |
3576 *p = NUL; | |
3577 (void)mch_stat((char *)filename, &new_st); | |
3578 *p = saved_char; | |
3579 } | |
3580 | |
3581 if (new_st.st_ino != st.st_ino || | |
3582 new_st.st_dev != st.st_dev) | |
3583 { | |
3584 do_strip = FALSE; | |
3585 /* We don't disable stripping of later | |
3586 * components since the unstripped path name is | |
3587 * still valid. */ | |
3588 } | |
3589 } | |
3590 #endif | |
3591 } | |
3592 } | |
3593 | |
3594 if (!do_strip) | |
3595 { | |
3596 /* Skip the ".." or "../" and reset the counter for the | |
3597 * components that might be stripped later on. */ | |
3598 p = tail; | |
3599 components = 0; | |
3600 } | |
3601 else | |
3602 { | |
3603 /* Strip previous component. If the result would get empty | |
3604 * and there is no trailing path separator, leave a single | |
3605 * "." instead. If we are at the end of the file name and | |
3606 * there is no trailing path separator and a preceding | |
3607 * component is left after stripping, strip its trailing | |
3608 * path separator as well. */ | |
3609 if (p == start && relative && tail[-1] == '.') | |
3610 { | |
3611 *p++ = '.'; | |
3612 *p = NUL; | |
3613 } | |
3614 else | |
3615 { | |
3616 if (p > start && tail[-1] == '.') | |
3617 --p; | |
3618 STRMOVE(p, tail); /* strip previous component */ | |
3619 } | |
3620 | |
3621 --components; | |
3622 } | |
3623 } | |
3624 else if (p == start && !relative) /* leading "/.." or "/../" */ | |
3625 STRMOVE(p, tail); /* strip ".." or "../" */ | |
3626 else | |
3627 { | |
3628 if (p == start + 2 && p[-2] == '.') /* leading "./../" */ | |
3629 { | |
3630 STRMOVE(p - 2, p); /* strip leading "./" */ | |
3631 tail -= 2; | |
3632 } | |
3633 p = tail; /* skip to char after ".." or "../" */ | |
3634 } | |
3635 } | |
3636 else | |
3637 { | |
3638 ++components; /* simple path component */ | |
3639 p = getnextcomp(p); | |
3640 } | |
3641 } while (*p != NUL); | |
3642 #endif /* !AMIGA */ | |
3643 } | |
3644 | |
3645 /* | |
3646 * Check if we have a tag for the buffer with name "buf_ffname". | 3456 * Check if we have a tag for the buffer with name "buf_ffname". |
3647 * This is a bit slow, because of the full path compare in fullpathcmp(). | 3457 * This is a bit slow, because of the full path compare in fullpathcmp(). |
3648 * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current | 3458 * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current |
3649 * file. | 3459 * file. |
3650 */ | 3460 */ |