ctags-lang-elm(7)

πŸ“¦ version 6.2.1, in ubuntu resolute, ubuntu stonking

πŸ’– 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/in blocks will be omitted.
  • Functions in let/in blocks will be marked as being in the scope of their outer function, regardless of how deeply nested the let/in block is.
  • Functions in let/in blocks 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