Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Tmain/list-map-rexprs.d/exit-expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
38 changes: 38 additions & 0 deletions Tmain/list-map-rexprs.d/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright: 2025 Masatake YAMATO
# License: GPL-2

CTAGS=$1

$CTAGS --quiet --options=NONE \
--langdef=Something \
--map-Something='%\%ESCAPING\%%' \
--map-Something=+'%ICASE%i' \
--map-Something=+'%TEMP%' \
--map-Something=-'%TEMP%' \
--map-Something=+'%TEMPi%i' \
--map-Something=-'%TEMPi%i' \
--list-map-rexprs=Something

$CTAGS --quiet --options=NONE \
--langdef=Something \
--map-Something='%aExpr%' \
--map-Something=+'%\%ESCAPING\%%' \
--map-Something=+'%ICASE%i' \
--map-Something=+'%TEMP%' \
--map-Something=-'%TEMP%' \
--map-Something=+'%TEMPi%i' \
--map-Something=-'%TEMPi%i' \
--list-maps=Something

$CTAGS --quiet --options=NONE \
--langdef=Something \
--kinddef-Something=t,type,types \
--fields=+'{language}' \
--regex-Something='/^([a-z]+)[ \t]+tdef;$/\1/t/' \
\
--map-Something='%something/.*\.c%' \
--map-Something=+'%something/.*\.cpp%i' \
--map-Something=+'%something/.*\.h%{icase}' \
\
-x --_xformat='%10N %{language}' \
-R something
1 change: 1 addition & 0 deletions Tmain/list-map-rexprs.d/something/input.CPP
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
float tdef;
1 change: 1 addition & 0 deletions Tmain/list-map-rexprs.d/something/input.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int tdef;
1 change: 1 addition & 0 deletions Tmain/list-map-rexprs.d/something/input.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
double tdef;
Empty file.
7 changes: 7 additions & 0 deletions Tmain/list-map-rexprs.d/stdout-expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#EXPRESSION CASE
%ESCAPING% sensitive
ICASE insensitive
Something %aExpr% %\%ESCAPING\%% %ICASE%i
int Something
float Something
double Something
1 change: 1 addition & 0 deletions Tmain/map-rexpr.d/macros.d/macros.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
%vimfiles_root %{_datadir}/vim/vimfiles
17 changes: 17 additions & 0 deletions Tmain/map-rexpr.d/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright: 2025 Masatake YAMATO
# License: GPL-2

CTAGS=$1

. ../utils.sh

echo "# < macros.d/macros.vm"
${CTAGS} --quiet --options=NONE --print-language macros.d/macros.vim
${CTAGS} --quiet --options=NONE -o - macros.d/macros.vim

echo "# cd macros.d; < macros.vim"
(
cd macros.d;
${CTAGS} --quiet --options=NONE --print-language macros.vim;
${CTAGS} --quiet --options=NONE -o - macros.vim
)
Empty file.
5 changes: 5 additions & 0 deletions Tmain/map-rexpr.d/stdout-expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# < macros.d/macros.vm
macros.d/macros.vim: RpmMacros
vimfiles_root macros.d/macros.vim /^%vimfiles_root %{_datadir}\/vim\/vimfiles$/;" m
# cd macros.d; < macros.vim
macros.vim: Vim
4 changes: 4 additions & 0 deletions Tmain/versioning.d/stdout-expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ About TEST language
enabled: yes
version: 10.9

Mappings/rexprs
-------------------------------------------------------


Mappings/patterns
-------------------------------------------------------
MYTEST
Expand Down
4 changes: 2 additions & 2 deletions docs/man/ctags-optlib.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ readers should read :ref:`ctags(1) <ctags(1)>` of Universal Ctags first.
Following options are for defining (or customizing) a parser:

* ``--langdef=<name>``
* ``--map-<LANG>=[+|-]<extension>|<pattern>``
* ``--map-<LANG>=[+|-]<extension>|<pattern>|<rexpr>``
* ``--kinddef-<LANG>=<letter>,<name>,<description>``
* ``--regex-<LANG>=/<line_pattern>/<name_pattern>/<kind-spec>/[<flags>]``
* ``--mline-regex-<LANG>=/<line_pattern>/<name_pattern>/<kind-spec>/{mgroup=<N>}[<flags>]``
Expand Down Expand Up @@ -103,7 +103,7 @@ Overview for defining a parser

3. Give a file pattern or file extension for activating the parser

Use ``--map-<LANG>=[+|-]<extension>|<pattern>``.
Use ``--map-<LANG>=[+|-]<extension>|<pattern>|<rexpr>``.

4. Define kinds

Expand Down
96 changes: 77 additions & 19 deletions docs/man/ctags.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -499,26 +499,71 @@ Language Selection and Mapping Options
Exuberant Ctags. See :ref:`ctags-incompatibilities(7) <ctags-incompatibilities(7)>` for the background of
this incompatible change.

``--map-<LANG>=[+|-]<extension>|<pattern>``
Unlike ``--map-<LANG>`` option, you cannot specify relative-path regular
expressions to ``--langmap`` option.

``--map-<LANG>=[+|-]<extension>|<pattern>|<rexpr>``
This option provides the way to control mapping(s) of file names to
languages in a more fine-grained way than ``--langmap`` option.

In ctags, more than one language can map to a
file name *<pattern>* or file *<extension>* (*N:1 map*). Alternatively,
``--langmap`` option handle only *1:1 map*, only one language
mapping to one file name *<pattern>* or file *<extension>*. A typical N:1
map is seen in C++ and ObjectiveC language; both languages have
a map to ``.h`` as a file extension.

A file extension is specified by preceding the extension with a period (e.g. ``.c``).
A file name pattern is specified by enclosing the pattern in parentheses (e.g.
``([Mm]akefile)``). A prefixed plus ('``+``') sign is for adding, and
relative-path regular expression (*<rexpr>*), file name *<pattern>*, or
file *<extension>* (*N:1 map*). Alternatively, ``--langmap``
option handle only *1:1 map*, only one language mapping to one
file name *<pattern>* or file *<extension>*. A typical N:1 map is
seen in C++ and ObjectiveC language; both languages have a map to
``.h`` as a file extension.

A file extension is specified by preceding the extension with a period
(e.g. ``.c``). A file name pattern is specified by enclosing the pattern in
parentheses (e.g. ``([Mm]akefile)``). A relative-path regular expression is
specified by enclosing the expressions in percent signs '``%``'
(e.g. ``%include/.*\.h%``). To include a literal percent sign
inside the regular expression, escape it as ``\%``.

A prefixed plus ('``+``') sign is for adding, and
minus ('``-``') is for removing. No prefix means replacing the map of *<LANG>*.

Unlike ``--langmap``, *<extension>* (or *<pattern>*) is not a list.
``--map-<LANG>`` takes one extension (or pattern). However,
the option can be specified with different arguments multiple times
in a command line.
Unlike ``--langmap``, ``--map-<LANG>`` does not take a list; ``--map-<LANG>``
takes one extension, one pattern, or one regular expression. However, the
option can be specified with different arguments multiple times in a command
line.

For file extensions and file name patterns, the match is performed
with a base file name, a file without any directory components.
For relative-path regular expressions, the match is performed with
a relative-path incorporating the directory components. A
relative-path is relative to the directory where ctags launches.

Assume your shell is in ``/project/x`` directory and you have the following
source tree under the directory.

.. code-block::

src
└── lib
├── data.c
└── logic.c

If you run ctags with ``ctags -R src``,
the match is performed with ``src/lib/data.c`` and ``src/lib/logic.c`` If you
give ``--map-YourParser='%src/lib/.*\.c%'``, ctags
chooses ``YourParser`` parser for processing ``data.c`` and ``logic.c`` in the
tree.

If your shell is in ``/project/x/src`` and you run
``ctags -R lib``, ctags may not choose
``YourParser`` because the match is performed with ``lib/data.c`` and
``lib/logic.c``.

A relative-path regular expression can take a flag controlling its testing.
The flag comes after the last percent sign. Currently only one available flag:

``{icase}`` (one-letter form '``i``')
The regular expression is to be applied in a case-insensitive
manner. (e.g. ``%include/.*\.h%i`` or ``%include/.*\.h%{icase}``

The relative-path regular expression is available since version 6.3.0.

.. _option_tags_file_contents:

Expand Down Expand Up @@ -1243,14 +1288,24 @@ Listing Options
languages, and then exits.
``all`` is used as default value if the option argument is omitted.

``--list-maps[=(<language>|all)]``
Lists file name patterns and the file extensions which associate a file
``--list-map-rexprs[=(<language>|all)]``
Lists the relative-path regular expressions which associate a file
name with a language for either the specified *<language>* or ``all``
languages, and then exits.
``all`` is used as default value if the option argument is omitted.

To list the file extensions or file name patterns individually, use
``--list-map-extensions`` or ``--list-map-patterns`` option.
(since version 6.3.0)

``--list-maps[=(<language>|all)]``
Lists the file name patterns, the file extensions, and the relative-path
regular expressions which associate a file name with a language for either
the specified *<language>* or ``all`` languages, and then exits.
``all`` is used as default value if the option argument is omitted.

To list the file extensions, file name patterns, or relative-path regular
expressions individually, use ``--list-map-extensions``,
``--list-map-patterns``, or ``--list-map-rexprs`` option.

See the ``--langmap`` option, and "`Determining file language`_", above.

This option does not work with ``--machinable`` nor
Expand Down Expand Up @@ -1507,10 +1562,13 @@ are mapped to C++, C and ObjectiveC. These mappings can cause
issues. ctags tries to select the proper parser
for the source file by applying heuristics to its content, however
it is not perfect. In case of issues one can use ``--language-force=<language>``,
``--langmap=<map>[,<map>[...]]``, or the ``--map-<LANG>=[+|-]<extension>|<pattern>``
``--langmap=<map>[,<map>[...]]``, or the ``--map-<LANG>=[+|-]<extension>|<pattern>|<rexpr>``
options. (Some of the heuristics are applied whether ``--guess-language-eagerly``
is given or not.)

The order of testing is relative-path regular expressions (specified with
``--map-<LANG>=<rexpr>``), file name patterns, then file extensions.

.. TODO: all heuristics??? To be confirmed.

Heuristically guessing
Expand Down
9 changes: 9 additions & 0 deletions docs/news/HEAD.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ New column, VER in the output of ``--list-{kinds-full,roles,fields,extras,pseudo
Indicates the versions of ctags output (or the parser) introducing the
item in the list.

Extend ``--map-<LANG>=`` and ``--langmap=`` options to choose a parser using regular expressions

``--map-<LANG>=[+]%REXPR%`` (or ``--langmap=<LANG>:[+]%REXPR%``) maps relative-paths
that match the regular expression to the given language.

The new ``--list-map-rexprs`` lists all regular-expression-based mappings.

``--list-maps`` has also been extended to include regular-expression-based mappings.

Incompatible changes
---------------------------------------------------------------------

Expand Down
16 changes: 8 additions & 8 deletions extra-cmds/utiltest.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,27 +278,27 @@ static void test_routines_strrstr(void)
TEST_CHECK(strcmp(strrstr("abcdcdb", "cd"), "cdb") == 0);
}

static void test_routines_baseFilenameSansExtensionNew(void)
static void test_routines_filenameSansExtensionNew(void)
{
char *bs;

TEST_CHECK ((bs = baseFilenameSansExtensionNew ("a.in", ".in"))
TEST_CHECK ((bs = filenameSansExtensionNew ("a.in", ".in"))
&& strcmp(bs, "a") == 0);
if (bs)
eFree (bs);

TEST_CHECK ((bs = baseFilenameSansExtensionNew ("x/b.in", ".in"))
&& strcmp(bs, "b") == 0);
TEST_CHECK ((bs = filenameSansExtensionNew ("x/b.in", ".in"))
&& strcmp(bs, "x/b") == 0);
if (bs)
eFree (bs);

TEST_CHECK ((bs = baseFilenameSansExtensionNew ("c.in.in", ".in.in"))
TEST_CHECK ((bs = filenameSansExtensionNew ("c.in.in", ".in.in"))
&& strcmp(bs, "c") == 0);
if (bs)
eFree (bs);

TEST_CHECK ((bs = baseFilenameSansExtensionNew ("/y/d.in.in", ".in.in"))
&& strcmp(bs, "d") == 0);
TEST_CHECK ((bs = filenameSansExtensionNew ("/y/d.in.in", ".in.in"))
&& strcmp(bs, "/y/d") == 0);
if (bs)
eFree (bs);
}
Expand Down Expand Up @@ -377,7 +377,7 @@ TEST_LIST = {
{ "intern", test_intern },
{ "numarray", test_numarray },
{ "routines/strrstr", test_routines_strrstr },
{ "routines/baseFilenameSansExtensionNew", test_routines_baseFilenameSansExtensionNew },
{ "routines/filenameSansExtensionNew", test_routines_filenameSansExtensionNew },
{ "vstring/ncats", test_vstring_ncats },
{ "vstring/truncate_leading", test_vstring_truncate_leading },
{ "vstring/EqC", test_vstring_eqc },
Expand Down
Loading
Loading