diff options
| author | David Fussner | 2024-06-10 14:16:04 +0100 |
|---|---|---|
| committer | Stefan Kangas | 2024-09-14 17:05:33 +0200 |
| commit | b44c00669ace7b9e6a90aecb5f4e9f4edf6ed25a (patch) | |
| tree | 7119d2ff1695271877a27a1a0cf0b0d648983d1c /lib-src | |
| parent | 98e582e74a2bbc2c7fdef02b8cd90036fa217712 (diff) | |
| download | emacs-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.c | 186 |
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."; | |||
| 793 | static const char *TeX_suffixes [] = | 793 | static const char *TeX_suffixes [] = |
| 794 | { "bib", "clo", "cls", "ltx", "sty", "TeX", "tex", NULL }; | 794 | { "bib", "clo", "cls", "ltx", "sty", "TeX", "tex", NULL }; |
| 795 | static const char TeX_help [] = | 795 | static 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\ | ||
| 816 | variant of these commands.\n\ | ||
| 801 | \n\ | 817 | \n\ |
| 802 | Other commands can be specified by setting the environment variable\n\ | 818 | Other 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. */ |
| 5748 | static const char *TEX_defenv = "\ | 5764 | static 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 | ||
| 5753 | static void TEX_decode_env (const char *, const char *); | 5780 | static 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: |