#
#  cmake.ctags --- multitable regex parser for CMake's files
#
#  Copyright (c) 2018, 128 Technology, Inc.
#
#  Author: Hadriel Kaplan (hadrielk@yahoo.com)
#
#  This source code is released for free distribution under the terms of the
#  GNU General Public License version 2 or (at your option) any later version.
#
#
# Overview:
#
#  This universal-ctags optlib option file defines the parser for tagging
#  CMake files. It supports tagging the following:
#
#    - cmake function and macro names
#    - build target, executable, and library target names
#    - cmake variables and options
#    - cmake project names
#
# Caveats:
#
#  Names that are ${} references to variables are not tagged.
#
#  For example, given the following:
#
#    set(PROJECT_NAME_STR ${PROJECT_NAME})
#    add_executable( ${PROJECT_NAME_STR} ... )
#    add_custom_target( ${PROJECT_NAME_STR}_tests ... )
#    add_library( sharedlib ... )
#
#  the variable 'PROJECT_NAME_STR' and target 'sharedlib' will both be tagged,
#  but the other targets will not be.
#
#
# References:
#
# - https://cmake.org/cmake/help/latest/manual/cmake-language.7.html
#

--langdef=CMake
--map-CMake=+.cmake
--map-CMake=+(CMakeLists.txt)

#
# Kinds
#
--kinddef-CMake=f,function,functions
--kinddef-CMake=m,macro,macros
--kinddef-CMake=t,target,targets
--kinddef-CMake=v,variable,variable definitions
--kinddef-CMake=D,option,options specified with -D
--kinddef-CMake=p,project,projects

#
# Tables
#
--_tabledef-CMake=main
--_tabledef-CMake=variable
--_tabledef-CMake=variableScoped
--_tabledef-CMake=function
--_tabledef-CMake=macro
--_tabledef-CMake=target
--_tabledef-CMake=option
--_tabledef-CMake=project

#
# comment
#
--_tabledef-CMake=commentBegin
--_tabledef-CMake=commentMultiline

--_mtable-regex-CMake=commentBegin/\[\[//{tjump=commentMultiline}
--_mtable-regex-CMake=commentBegin/[^\n]*[ \t\n]*//{tleave}

--_mtable-regex-CMake=commentMultiline/\]\][ \t\n]*//{tleave}
--_mtable-regex-CMake=commentMultiline/.[^]]*//

--_tabledef-CMake=skipComment
--_mtable-regex-CMake=skipComment/#//{tenter=commentBegin}

#
# Utilities
#
--_tabledef-CMake=skipWhiteSpace
--_tabledef-CMake=skipToName
--_tabledef-CMake=nextToken

--_mtable-regex-CMake=skipWhiteSpace/[ \t\n]+//

--_mtable-extend-CMake=skipToName+skipWhiteSpace
--_mtable-extend-CMake=skipToName+skipComment

--_mtable-regex-CMake=nextToken/[^ \t\n]+[ \t\n]*//

#
# main
#
# This first regex entry may seem odd - it's purely for improving performance, by
# matching tokens with leading characters that could not possibly match a later regex,
# and just skipping the whole token (and trailing whitespace). This one regex line
# improved performance by an order of magnitude.
--_mtable-regex-CMake=main/[^sSfFmMaAoOpP# \t\n][^ #\t\n]*[ \t\n]+//
--_mtable-extend-CMake=main+skipComment
--_mtable-regex-CMake=main/set[ \t]*\(//{icase}{tenter=variable}
--_mtable-regex-CMake=main/function[ \t]*\(//{icase}{tenter=function}
--_mtable-regex-CMake=main/macro[ \t]*\(//{icase}{tenter=macro}
--_mtable-regex-CMake=main/add_(custom_target|executable|library)[ \t]*\(//{icase}{tenter=target}
--_mtable-regex-CMake=main/option[ \t]*\(//{icase}{tenter=option}
--_mtable-regex-CMake=main/project[ \t]*\(//{icase}{tenter=project}
--_mtable-extend-CMake=main+nextToken
--_mtable-extend-CMake=main+skipWhiteSpace

#
# For performance reasons, this is a separate table from 'main', mostly to avoid
# matching tokens starting with 'e' in the main table - such tokens are very
# common in CMake, due to 'endif()' and such
#
--_tabledef-CMake=inFunction
--_mtable-regex-CMake=inFunction/([^eEsSfFmMaAoO# \t\n]|[eE][^nN]|[eE][nN][^dD]|[eE][nN][dD][^fF#])[^ #\t\n]*[ \t\n]+//
--_mtable-extend-CMake=inFunction+skipComment
--_mtable-regex-CMake=inFunction/set[ \t]*\(//{icase}{tenter=variableScoped}
--_mtable-regex-CMake=inFunction/function[ \t]*\(//{icase}{tenter=function}
--_mtable-regex-CMake=inFunction/macro[ \t]*\(//{icase}{tenter=macro}
--_mtable-regex-CMake=inFunction/endfunction[ \t]*\([^)]*\)//{icase}{tleave}{scope=pop}
--_mtable-regex-CMake=inFunction/add_(custom_target|executable|library)[ \t]*\(//{icase}{tenter=target}
--_mtable-regex-CMake=inFunction/option[ \t]*\(//{icase}{tenter=option}
--_mtable-extend-CMake=inFunction+nextToken
--_mtable-extend-CMake=inFunction+skipWhiteSpace

#
# Each of the following basically work the same way, and only differ in the
# exact pattern allowed to be their name, and the Kind they add. Note that they
# capture a required trailing '[ \t\n\)]' or '#', to verify the full name token
# matched the name's pattern, but then we advanceTo=2start for the next round,
# so that we don't go past a potential '#' comment token but instead match it
# again in the main table as a comment. The odds of a comment '#' immediately
# following the name is very low, so we split it into its own check and do it
# last in each table - this improves real-world performance ~10%, because in
# the common case we can capture the whitespace at the same time as the name,
# and not have to skip it again in the 'main' table.
#

#
# variable
#
--_mtable-regex-CMake=variable/([A-Za-z0-9_.-]+)[ \t\n\)]+/\1/v/{tleave}
--_mtable-extend-CMake=variable+skipToName
--_mtable-regex-CMake=variable/([A-Za-z0-9_.-]+)(#)/\1/v/{tleave}{_advanceTo=2start}

# when a variable is defined inside a function, we reference its scope unless it
# has a PARENT_SCOPE argument; unfortunately the regex for this can backtrack a
# lot and the current ctags regex engine doesn't do lazy or atomic/possessive
# captures to avoid it, but in practice this should be ok
--_tabledef-CMake=inVariable
--_mtable-regex-CMake=inVariable/[^")]+(("(\\"|[^"])*")([^")]+("(\\"|[^"])*"))*)[ \t\n]PARENT_SCOPE[# \t\n)]//{tjump=variable}{_advanceTo=0start}
--_mtable-regex-CMake=inVariable/([A-Za-z0-9_.-]+)[ \t\n\)]+/\1/v/{tleave}{scope=ref}
--_mtable-regex-CMake=inVariable/([A-Za-z0-9_.-]+)(#)/\1/v/{tleave}{scope=ref}{_advanceTo=2start}

--_mtable-regex-CMake=variableScoped/[A-Za-z0-9_.-]+[# \t\n\)]//{tjump=inVariable}{_advanceTo=0start}
--_mtable-extend-CMake=variableScoped+skipToName


#
# function
#
--_mtable-regex-CMake=function/([A-Za-z_][A-Za-z0-9_]*)([# \t\n\)])/\1/f/{_advanceTo=2start}{tjump=inFunction}{scope=push}
--_mtable-extend-CMake=function+skipToName

#
# macro: unlike functions, technically macros don't create scopes for any
# variables set in them - the scope is whatever it is when the macro is invoked.
# So we're not going to push a scope nor go into the 'inFunction' table.
#
--_mtable-regex-CMake=macro/([A-Za-z_][A-Za-z0-9_]*)[ \t\n\)]+/\1/m/{tleave}
--_mtable-extend-CMake=macro+skipToName
--_mtable-regex-CMake=macro/([A-Za-z_][A-Za-z0-9_]*)(#)/\1/m/{tleave}{_advanceTo=2start}

#
# target
#
--_mtable-regex-CMake=target/([A-Za-z0-9_.-]+)[ \t\n\)]+/\1/t/{tleave}
--_mtable-extend-CMake=target+skipToName
--_mtable-regex-CMake=target/([A-Za-z0-9_.-]+)(#)/\1/t/{tleave}{_advanceTo=2start}

#
# option
#
--_mtable-regex-CMake=option/([A-Za-z0-9_.-]+)[ \t\n\)]+/\1/D/{tleave}
--_mtable-extend-CMake=option+skipToName
--_mtable-regex-CMake=option/([A-Za-z0-9_.-]+)(#)/\1/D/{tleave}{_advanceTo=2start}

#
# project
#
--_mtable-regex-CMake=project/([A-Za-z0-9_.-]+)([# \t\n\)])/\1/p/{tleave}{_advanceTo=2start}
--_mtable-extend-CMake=project+skipToName
