ctags-lang-elm(7)
π ctags-lang-elm β random notes about tagging Elm with Universal Ctags π
okay bestie, so this is ctags-lang-elm(7) from the Universal Ctags manual, and it's literally just the tea on how ctags handles your Elm source code. no cap. let's get into it π«Ά
β¨ SYNOPSIS β¨
ctags ... --languages=+Elm ...
ctags ... --language-force=Elm ...
ctags ... --map-Elm=+.elm ...
πΈ DESCRIPTION πΈ
so the Elm parser is a PEG parser built with PackCC, which is part of the ctags infrastructure itself. it's giving very much "we built our own tooling" energy and honestly? iconic.
it should correctly process all top level statements π but there's a lil limitation with functions embedded in let/in blocks. they'll mostly be fine, but sometimes a function inside a let/in block will just... not show up. π like it ghosted the tags file.
π¦ EXAMPLES π¦
π Imports π
imported modules get tagged, and their role is "imported", not "def". types, functions, etc. that get exposed via an imported module have their role as "exposed".
and listen β exposed items are marked as being in the scope of their own module, not the module that's doing the importing. main character energy, they belong to themselves π
"input.elm"
module SomeMod exposing (..)
import MyMod exposing
( map
, Maybe
, Result(..)
, MyList(Empty)
)
"output.tags" with --options=NONE -o - --sort=no --extras=+r --fields=+r input.elm
SomeMod input.elm /^module SomeMod exposing (..)$/;" m roles:def
MyMod input.elm /^import MyMod exposing$/;" m roles:imported
map input.elm /^ ( map$/;" f module:MyMod roles:exposed
Maybe input.elm /^ , Maybe$/;" t module:MyMod roles:exposed
Result input.elm /^ , Result(..)$/;" t module:MyMod roles:exposed
MyList input.elm /^ , MyList(Empty)$/;" t module:MyMod roles:exposed
Empty input.elm /^ , MyList(Empty)$/;" c type:MyMod.MyList roles:exposed
π Namespaces π
namespaces get tagged too, and their role is "def". slay.
"input.elm"
module AMod exposing (..)
import MyImport as NSpace exposing (impFunc)
"output.tags" with --options=NONE -o - --sort=no --extras=+r --fields=+r input.elm
AMod input.elm /^module AMod exposing (..)$/;" m roles:def
NSpace input.elm /^import MyImport as NSpace exposing (impFunc)$/;" n module:AMod roles:def moduleName:MyImport
MyImport input.elm /^import MyImport as NSpace exposing (impFunc)$/;" m roles:imported
impFunc input.elm /^import MyImport as NSpace exposing (impFunc)$/;" f module:MyImport roles:exposed
π Type names π
constructors and top level functions will have type names. yes, that's the whole vibe.
"input.elm"
funcA : Int -> Int
funcA a = a + 1
type B
= B1Cons
{ x : Float
, y : Float
}
| B2Cons String Integer
| B3Cons
"output.tags" with --options=NONE -o - --sort=no --extras=+r --fields=+r input.elm
funcA input.elm /^funcA a = a + 1$/;" f typeref:typename:Int -> Int roles:def
B input.elm /^type B$/;" t roles:def
B1Cons input.elm /^ = B1Cons$/;" c type:B typeref:typename:{ x : Float , y : Float } -> B roles:def
B2Cons input.elm /^ | B2Cons String Integer$/;" c type:B typeref:typename:String -> Integer -> B roles:def
B3Cons input.elm /^ | B3Cons$/;" c type:B typeref:typename:B roles:def
π§ Function parameter lists π§
function parameter lists can be extracted into the tags file signature field. ngl they're not really function signatures, but it's the closest concept available in ctags, so we work with what we've got π
use --fields=+S.
funcA a1 a2 =
a1 + a2
"output.tags" with --sort=no --extras=+r --fields=+rS
funcA input.elm /^funcA a1 a2 =$/;" f signature:a1 a2 roles:def
π KNOWN LIMITATIONS π
the ctags signature field is used for function parameter lists, even though it's not an ideal field. see above. it is what it is π
Elm requires all statements at the same logical level to have the same indentation. if there's additional indentation, that line is part of the previous one. so, without over-complicating the PEG parser, we have the following limitations... (brace yourself bestie)
- Sometimes functions in
let/inblocks will be omitted. - Functions in
let/inblocks will be marked as being in the scope of their outer function, regardless of how deeply nested thelet/inblock is. - Functions in
let/inblocks won't have type names.
real talk: if your Elm code leans hard on deeply nested let/in blocks, those three bullets are gonna matter. not a vibe check win, but at least you know fr fr. π«
π SEE ALSO π
and that's the whole page, bestie!! this was ctags-lang-elm(7) from Universal Ctags, version 6.2.1 β go forth and tag your Elm, you absolute icon πβ¨ππ¦πΈπ
π¦ where this page comes from
- ubuntu resolute: universal-ctags 6.2.1-1 Β· GPL-2.0-or-later
- ubuntu stonking: universal-ctags 6.2.1-1 Β· GPL-2.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms