aboutsummaryrefslogtreecommitdiffstats
path: root/lib-src
diff options
context:
space:
mode:
authorDavid Fussner2024-06-10 14:16:04 +0100
committerStefan Kangas2024-09-14 17:05:33 +0200
commitb44c00669ace7b9e6a90aecb5f4e9f4edf6ed25a (patch)
tree7119d2ff1695271877a27a1a0cf0b0d648983d1c /lib-src
parent98e582e74a2bbc2c7fdef02b8cd90036fa217712 (diff)
downloademacs-b44c00669ace7b9e6a90aecb5f4e9f4edf6ed25a.tar.gz
emacs-b44c00669ace7b9e6a90aecb5f4e9f4edf6ed25a.zip
Provide a modified xref backend for TeX buffers
In addition to providing a new `xref' backend, the patch also improves the general handling of expl3 syntax. Expl3 is the next-generation LaTeX specification, and has for some time been available by default in the LaTeX kernel. The new syntax co-exists in many files with the standard LaTeX2e syntax, so we try at least minimally to separate the way modes handle the two specifications, both to reduce visually-disturbing interference between them and also to improve the `xref' backend. (Bug#53749) * lib-src/etags.c (TeX_commands): Improve parsing of commands in TeX buffers. (TEX_defenv): Expand list of commands to tag by default in TeX buffers. (TeX_help): * doc/emacs/maintaining.texi (Tag Syntax): Document new tagged commands. (Identifier Search): Add note about semantic-symref-filepattern-alist, auto-mode-alist, and xref-find-references. * lisp/textmodes/tex-mode.el (tex-font-lock-suscript): Test for underscore in expl3 files and regions, disable subscript face there. (tex-common-initialization): Set up xref backend for in-tree TeX modes. Detect expl3 files, and in others set up a list of expl3 regions. (tex-expl-buffer-parse): New function called in previous. (tex-expl-buffer-p): New variable to hold the result of previous. (tex-expl-region-set): New function added to 'syntax-propertize-extend-region-functions' hook. (tex-expl-region-list): New variable to hold the result of previous. (tex--xref-backend): New function to identify the xref backend. (tex--thing-at-point, tex-thingatpt--beginning-of-symbol) (tex-thingatpt--end-of-symbol, tex--bounds-of-symbol-at-point): New functions to return 'thing-at-point' for xref backend. (tex-thingatpt-exclude-chars): New variable to do the same. (xref-backend-identifier-at-point): New TeX backend method to provide symbols for processing by xref. (xref-backend-identifier-completion-table) (xref-backend-identifier-completion-ignore-case) (xref-backend-definitions, xref-backend-apropos): Placeholders to call the standard 'etags' xref backend methods. (xref-backend-references): Wrapper to call the default xref backend method, finding as many relevant files as possible and using a bespoke syntax-propertize-function when required. (tex--collect-file-extensions, tex-xref-syntax-function): Helper functions for previous. (tex-find-references-syntax-table, tex--buffers-list) (tex--xref-syntax-fun, tex--old-syntax-function): New variables for the same.
Diffstat (limited to 'lib-src')
-rw-r--r--lib-src/etags.c186
1 files changed, 171 insertions, 15 deletions
diff --git a/lib-src/etags.c b/lib-src/etags.c
index 556b7d701fc..7f652790261 100644
--- a/lib-src/etags.c
+++ b/lib-src/etags.c
@@ -793,11 +793,27 @@ variables set with 'set!' at top level in the file.";
793static const char *TeX_suffixes [] = 793static const char *TeX_suffixes [] =
794 { "bib", "clo", "cls", "ltx", "sty", "TeX", "tex", NULL }; 794 { "bib", "clo", "cls", "ltx", "sty", "TeX", "tex", NULL };
795static const char TeX_help [] = 795static const char TeX_help [] =
796"In LaTeX text, the argument of any of the commands '\\chapter',\n\ 796"In LaTeX text, the argument of the commands '\\chapter', '\\section',\n\
797'\\section', '\\subsection', '\\subsubsection', '\\eqno', '\\label',\n\ 797'\\subsection', '\\subsubsection', '\\eqno', '\\label', '\\ref',\n\
798'\\ref', '\\cite', '\\bibitem', '\\part', '\\appendix', '\\entry',\n\ 798'\\Ref', '\\footref', '\\cite', '\\bibitem', '\\part', '\\appendix',\n\
799'\\index', '\\def', '\\newcommand', '\\renewcommand',\n\ 799'\\entry', '\\index', '\\def', '\\edef', '\\gdef', '\\xdef',\n\
800'\\newenvironment' or '\\renewenvironment' is a tag.\n\ 800'\\newcommand', '\\renewcommand', '\\newrobustcmd', '\\renewrobustcmd',\n\
801'\\newenvironment', '\\renewenvironment', '\\DeclareRobustCommand',\n\
802'\\providecommand', '\\providerobustcmd', '\\NewDocumentCommand',\n\
803'\\RenewDocumentCommand', '\\ProvideDocumentCommand',\n\
804'\\DeclareDocumentCommand', '\\NewExpandableDocumentCommand',\n\
805'\\RenewExpandableDocumentCommand', '\\ProvideExpandableDocumentCommand',\n\
806'\\DeclareExpandableDocumentCommand', '\\NewDocumentEnvironment',\n\
807'\\RenewDocumentEnvironment', '\\ProvideDocumentEnvironment',\n\
808'\\DeclareDocumentEnvironment','\\csdef', '\\csedef', '\\csgdef',\n\
809'\\csxdef', '\\csletcs', '\\cslet', '\\letcs', '\\let',\n\
810'\\cs_new_protected_nopar', '\\cs_new_protected', '\\cs_new_nopar',\n\
811'\\cs_new_eq', '\\cs_new', '\\cs_set_protected_nopar',\n\
812'\\cs_set_protected', '\\cs_set_nopar', '\\cs_set_eq', '\\cs_set',\n\
813'\\cs_gset_protected_nopar', '\\cs_gset_protected', '\\cs_gset_nopar',\n\
814'\\cs_gset_eq', '\\cs_gset', '\\cs_generate_from_arg_count', or\n\
815'\\cs_generate_variant' is a tag. So is the argument of any starred\n\
816variant of these commands.\n\
801\n\ 817\n\
802Other commands can be specified by setting the environment variable\n\ 818Other commands can be specified by setting the environment variable\n\
803'TEXTAGS' to a colon-separated list like, for example,\n\ 819'TEXTAGS' to a colon-separated list like, for example,\n\
@@ -5746,9 +5762,20 @@ static linebuffer *TEX_toktab = NULL; /* Table with tag tokens */
5746/* Default set of control sequences to put into TEX_toktab. 5762/* Default set of control sequences to put into TEX_toktab.
5747 The value of environment var TEXTAGS is prepended to this. */ 5763 The value of environment var TEXTAGS is prepended to this. */
5748static const char *TEX_defenv = "\ 5764static const char *TEX_defenv = "\
5749:chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\ 5765:label:ref:Ref:footref:chapter:section:subsection:subsubsection:eqno:cite\
5750:part:appendix:entry:index:def\ 5766:bibitem:part:appendix:entry:index:def:edef:gdef:xdef:newcommand:renewcommand\
5751:newcommand:renewcommand:newenvironment:renewenvironment"; 5767:newenvironment:renewenvironment:DeclareRobustCommand:renewrobustcmd\
5768:newrobustcmd:providecommand:providerobustcmd:NewDocumentCommand\
5769:RenewDocumentCommand:ProvideDocumentCommand:DeclareDocumentCommand\
5770:NewExpandableDocumentCommand:RenewExpandableDocumentCommand\
5771:ProvideExpandableDocumentCommand:DeclareExpandableDocumentCommand\
5772:NewDocumentEnvironment:RenewDocumentEnvironment\
5773:ProvideDocumentEnvironment:DeclareDocumentEnvironment:csdef\
5774:csedef:csgdef:csxdef:csletcs:cslet:letcs:let:cs_new_protected_nopar\
5775:cs_new_protected:cs_new_nopar:cs_new_eq:cs_new:cs_set_protected_nopar\
5776:cs_set_protected:cs_set_nopar:cs_set_eq:cs_set:cs_gset_protected_nopar\
5777:cs_gset_protected:cs_gset_nopar:cs_gset_eq:cs_gset\
5778:cs_generate_from_arg_count:cs_generate_variant";
5752 5779
5753static void TEX_decode_env (const char *, const char *); 5780static void TEX_decode_env (const char *, const char *);
5754 5781
@@ -5807,19 +5834,139 @@ TeX_commands (FILE *inf)
5807 { 5834 {
5808 char *p; 5835 char *p;
5809 ptrdiff_t namelen, linelen; 5836 ptrdiff_t namelen, linelen;
5810 bool opgrp = false; 5837 bool opgrp = false, one_esc = false, is_explthree = false;
5811 5838
5812 cp = skip_spaces (cp + key->len); 5839 cp = skip_spaces (cp + key->len);
5840
5841 /* 1. The canonical expl3 syntax looks something like this:
5842 \cs_new:Npn \__hook_tl_gput:Nn { \ERROR }. First, if we
5843 want to tag any such commands, we include only the part
5844 before the colon (cs_new) in TEX_defenv or TEXTAGS. Second,
5845 etags skips the argument specifier (including the colon)
5846 after the tag token, so that it doesn't become the tag name.
5847 Third, we set the boolean 'is_explthree' to true so that we
5848 can remove the argument specifier from the actual tag name
5849 (__hook_tl_gput). This all allows us to include expl3
5850 constructs in TEX_defenv or in the environment variable
5851 TEXTAGS without requiring a change of separator, and it also
5852 allows us to find the definition of variant commands (with
5853 different argument specifiers) defined using, for example,
5854 \cs_generate_variant:Nn. Please note that the expl3 spec
5855 requires etags to pay more attention to whitespace in the
5856 code.
5857
5858 2. We also automatically remove the asterisk from starred
5859 variants of all commands, without the need to include the
5860 starred commands explicitly in TEX_defenv or TEXTAGS. */
5861 if (*cp == ':')
5862 {
5863 while (!c_isspace (*cp) && *cp != TEX_opgrp)
5864 cp++;
5865 cp = skip_spaces (cp);
5866 is_explthree = true;
5867 }
5868 else if (*cp == '*')
5869 cp++;
5870
5871 /* Skip the optional arguments to commands in the tags list so
5872 that these arguments don't end up as the name of the tag.
5873 The name will instead come from the argument in curly braces
5874 that follows the optional ones. The '\let' command gets
5875 special treatment. */
5876 while (*cp != '\0' && *cp != '%'
5877 && !streq (key->buffer, "let"))
5878 {
5879 if (*cp == '[')
5880 {
5881 while (*cp != ']' && *cp != '\0' && *cp != '%')
5882 cp++;
5883 }
5884 else if (*cp == '(')
5885 {
5886 while (*cp != ')' && *cp != '\0' && *cp != '%')
5887 cp++;
5888 }
5889 else if (*cp == ']' || *cp == ')')
5890 cp++;
5891 else
5892 break;
5893 }
5813 if (*cp == TEX_opgrp) 5894 if (*cp == TEX_opgrp)
5814 { 5895 {
5815 opgrp = true; 5896 opgrp = true;
5816 cp++; 5897 cp++;
5898 cp = skip_spaces (cp); /* For expl3 code. */
5817 } 5899 }
5900
5901 /* Removing the TeX escape character from tag names simplifies
5902 things for editors finding tagged commands in TeX buffers.
5903 This applies to Emacs but also to the tag-finding behavior
5904 of at least some of the editors that use ctags, though in
5905 the latter case this will remain suboptimal. The
5906 undocumented ctags option '--no-duplicates' may help. */
5907 if (*cp == TEX_esc)
5908 {
5909 cp++;
5910 one_esc = true;
5911 }
5912
5913 /* Testing !c_isspace && !c_ispunct is simpler, but halts
5914 processing at too many places. The list as it stands tries
5915 both to ensure that tag names will derive from macro names
5916 rather than from optional parameters to those macros, and
5917 also to return findable names while still allowing for
5918 unorthodox constructs. */
5818 for (p = cp; 5919 for (p = cp;
5819 (!c_isspace (*p) && *p != '#' && 5920 (!c_isspace (*p) && *p != '#' && *p != '=' &&
5820 *p != TEX_opgrp && *p != TEX_clgrp); 5921 *p != '[' && *p != '(' && *p != TEX_opgrp &&
5922 *p != TEX_clgrp && *p != '"' && *p != '\'' &&
5923 *p != '%' && *p != ',' && *p != '|' && *p != '$');
5821 p++) 5924 p++)
5822 continue; 5925 /* In expl3 code we remove the argument specification from
5926 the tag name. More generally we allow only one (deleted)
5927 escape char in a tag name, which (primarily) enables
5928 tagging a TeX command's different, possibly temporary,
5929 '\let' bindings. */
5930 if (is_explthree && *p == ':')
5931 break;
5932 else if (*p == TEX_esc)
5933 { /* Second part of test is for, e.g., \cslet. */
5934 if (!one_esc && !opgrp)
5935 {
5936 one_esc = true;
5937 continue;
5938 }
5939 else
5940 break;
5941 }
5942 else
5943 continue;
5944 /* For TeX files, tags without a name are basically cruft, and
5945 in some situations they can produce spurious and confusing
5946 matches. Try to catch as many cases as possible where a
5947 command name is of the form '\(', but avoid, as far as
5948 possible, the spurious matches. */
5949 if (p == cp)
5950 {
5951 switch (*p)
5952 { /* Include =? */
5953 case '(': case '[': case '"': case '\'':
5954 case '\\': case '!': case '=': case ',':
5955 case '|': case '$':
5956 p++;
5957 break;
5958 case '{': case '}': case '<': case '>':
5959 if (!opgrp)
5960 {
5961 p++;
5962 if (*p == '\0' || *p == '%')
5963 goto tex_next_line;
5964 }
5965 break;
5966 default:
5967 break;
5968 }
5969 }
5823 namelen = p - cp; 5970 namelen = p - cp;
5824 linelen = lb.len; 5971 linelen = lb.len;
5825 if (!opgrp || *p == TEX_clgrp) 5972 if (!opgrp || *p == TEX_clgrp)
@@ -5828,9 +5975,18 @@ TeX_commands (FILE *inf)
5828 p++; 5975 p++;
5829 linelen = p - lb.buffer + 1; 5976 linelen = p - lb.buffer + 1;
5830 } 5977 }
5831 make_tag (cp, namelen, true, 5978 if (namelen)
5832 lb.buffer, linelen, lineno, linecharno); 5979 make_tag (cp, namelen, true,
5833 goto tex_next_line; /* We only tag a line once */ 5980 lb.buffer, linelen, lineno, linecharno);
5981 /* Lines with more than one \def or \let are surprisingly
5982 common in TeX files, especially in the system files that
5983 form the basis of the various TeX formats. This tags them
5984 all. */
5985 /* goto tex_next_line; /\* We only tag a line once *\/ */
5986 while (*cp != '\0' && *cp != '%' && *cp != TEX_esc)
5987 cp++;
5988 if (*cp != TEX_esc)
5989 goto tex_next_line;
5834 } 5990 }
5835 } 5991 }
5836 tex_next_line: 5992 tex_next_line: