packages feed

ghc-tags-plugin-1.0.0.0: README.md

Ghc Tags Compiler Plugin
========================
[![Haskell](https://img.shields.io/badge/language-Haskell-8D82AC.svg?style=for-the-badge)](https://haskell.org)
[![MPL-2.0 License](http://img.shields.io/badge/license-MPL20-brightgreen.svg?style=for-the-badge)](https://github.com/coot/ghc-tags-plugin/blob/master/ghc-tags-core/LICENSE)
[![Haskell CI](https://img.shields.io/github/actions/workflow/status/coot/ghc-tags-plugin/ci.yml?branch=master&label=Build&style=for-the-badge)](https://github.com/coot/ghc-tags-plugin/actions/workflows/ci.yml)

A library and a [GHC compiler
plugin](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/extending_ghc.html#compiler-plugins)
which generates tags for each compiled module or component.


● Requirements
--------------

The plugin requires at least: `ghc >= 9.6`.

● Plugin options
----------------

```
Usage: <program> [-e|--etags] [--stream] [--debug] [--qualified] [-F|--no-filter] [file_path]
  write tags from ghc abstract syntax tree

Available options:
  -e,--etags               produce emacs etags file
  --stream                 stream existing tags (ctags only)
  file_path                tags file relative to cabal.project or stack.yaml:
                           default tags or TAGS (with --etags)
  --debug                  tag counts, file sizes, and update timings
  -q,--qualified           also generate names qualified by the defining module
  -F,--no-filter           retain both signatures and definitions
```

The tags path can be absolute or relative to the nearest directory containing
`cabal.project`, `cabal.project.local`, or `stack.yaml`, searching upward from
the compiled package. Packages using the same project root can share one tags
file:
```
-fplugin-opt=Plugin.GhcTags:tags
```
A `cabal.project.local` file is a project-root marker even without a
`cabal.project` beside it. A nearer marker takes precedence over configurations
in parent directories. For example, with `/repo/cabal.project` and
`/repo/package/cabal.project.local`, compiling that package writes to
`/repo/package/tags`.

If `STACK_YAML` points to an existing configuration file, its directory is used
instead. If no project configuration is found, the plugin prints a warning and
resolves the path relative to the nearest `*.cabal` package file. When no package
file is found either, it uses the compiler's working directory. Absolute output
paths are used unchanged.

## ● Qualified names

To generate module-qualified names alongside ordinary names, enable:

```
-fplugin-opt=Plugin.GhcTags:--qualified
```

For example, a declaration `lookup` in `Data.Map` produces both `lookup` and
`Data.Map.lookup`, pointing to the same declaration. This works with ctags,
streaming ctags, etags, and declarations generated by Template Haskell. Module
tags themselves are kept unchanged. Modules without a module header use `Main`.

The prefix identifies the module containing the declaration. It does not resolve
import aliases or re-exports: `import qualified Data.Map as M` does not add
`M.lookup`. Local bindings also receive module-prefixed names; those names
identify their containing module, rather than a valid qualified import.

Only recompiled modules are updated. Rebuild all modules after enabling or
disabling this option to update existing tags consistently.

## ● Signature and definition filtering

By default, a type signature replaces the corresponding function or pattern
synonym definition in the tags file. A data constructor also takes precedence
over a type constructor with the same name. To retain both tags, use:

```
-fplugin-opt=Plugin.GhcTags:--no-filter
```

The short form is `-fplugin-opt=Plugin.GhcTags:-F`. This applies to ctags,
streaming ctags, etags, and Template Haskell signature/definition pairs. Tags
remain sorted. Recompile all modules after changing this option to update
existing tags consistently.

## ● Update timings

Enable timing diagnostics with:

```
-fplugin-opt=Plugin.GhcTags:--debug
```

Each completed update reports monotonic wall-clock durations in milliseconds:

- `lock-wait`: opening and acquiring the tags-file lock.
- `read`: reading the existing tags file.
- `parse`: parsing its records.
- `generate`: extracting and preparing tags for the current module or splice.
- `merge`: combining old and new tags, including sorting where required.
- `write`: formatting, writing, and flushing the replacement's contents.
- `total`: elapsed time from before lock acquisition until after release,
  including temporary-file close/rename, bookkeeping, and other debug output.

Reports include the module, tags path, output mode, and whether the update came
from parsing or a Template Haskell declaration splice. Streaming ctags measures
each stage as records flow through it, excluding upstream work from downstream
phase times. It does not load the existing file into memory for timing.

Debug mode forces lazy results within their measured phase and adds measurement
overhead, especially when streaming. The phase times need not sum to `total`.
With debug disabled, timing does not read the clock or force additional results.

## ● Emacs support

To produce `etags` file you will need to pass the following option
```
-fplugin-opt=Plugin.GhcTags:--etags
```

## ● Editor configuration

By default the generated `tags` file is put in the nearest directory containing
`cabal.project`, `cabal.project.local`, or `stack.yaml`. To let Vim or Neovim find
it while editing files in package subdirectories, enable searching parent
directories:
```
:set tags+=./tags;
```
You can also pass an option to change where tags are written, see below.

● Configuration: Ghc / Cabal / Stack
------------------------------------

## ● Cabal: quick start with GHCi

To generate tags in a REPL session without installing the plugin separately or
editing any `.cabal` files, run:

```sh
cabal repl TARGET \
  --build-depends=ghc-tags-plugin \
  --repl-options=-fplugin=Plugin.GhcTags \
  --repl-options=-fplugin-opt=Plugin.GhcTags:tags
```

Replace `TARGET` with a component such as `lib:my-package` or `exe:my-program`.
Cabal builds the plugin and makes it available to that REPL session. This
command was tested with Cabal 3.16.1.0 and GHC 9.10.3 using a local plugin
checkout and this repository's project configuration. See Cabal's
[`--build-depends` documentation](https://cabal.readthedocs.io/en/stable/cabal-commands.html#cmdoption-repl-build-depends).

The plugin's dependencies are solved together with the target project's
dependencies, using that project's `index-state`, constraints, and
`allow-newer` settings.  You might need to modify `allow-newer` via
[`--allow-newer` documentation](https://cabal.readthedocs.io/en/stable/cabal-commands.html#cmdoption-allow-newer).


This configures the REPL session only. For normal builds and HLS, use the
persistent configuration below.

## ● Cabal: persistent configuration

This setup enables the plugin through `cabal.project.local`, without changing
any `.cabal` files.

First, install the plugin into Cabal's store:

```sh
cabal install --lib ghc-tags-plugin --package-env=ghc-tags
```

If installation encounters the GHC 9.14 dependency bounds described above,
add `--allow-newer=pipes-safe:base,ghc-tags-pipes:base` to this command too.

The named `ghc-tags` environment keeps this installation separate from the
default GHC package environment. The configuration below loads the plugin
through its package database, so it does not require selecting that environment.
See Cabal's [package environment documentation](https://cabal.readthedocs.io/en/stable/cabal-commands.html#adding-libraries-to-ghc-package-environments).

Use the same GHC to install the plugin and build the target project. If the
project selects a particular compiler, pass `-w /absolute/path/to/ghc` to the
installation command as well. Reinstall the plugin when changing compilers.

With Cabal 3.16.1.0 or newer, run this in the target project to find its compiler
and the corresponding store directory:

```sh
cabal path --compiler-info
```

Append `/package.db` to the reported `compiler-store-path` to obtain
`PACKAGE_DB`. For example, the resulting path might be:

```text
/home/alice/.cabal/store/ghc-9.10.3-1be3/package.db
```

Use the reported path rather than constructing it from the GHC version:
compiler ABI suffixes and custom store locations can change the directory name.
With older Cabal versions, locate the compiler's `package.db` in the Cabal store
(the store location can be queried with `cabal path --store-dir` where available).

Add the following [`program-options`] stanza to `cabal.project.local`:

```cabal
program-options
    ghc-options: -package-db=PACKAGE_DB
                 -plugin-package=ghc-tags-plugin
                 -fplugin=Plugin.GhcTags
                 -fplugin-opt=Plugin.GhcTags:tags
```

Replace `PACKAGE_DB` with the absolute path found above. Environment variables
such as `$HOME` are not expanded in this file. If the path contains spaces,
quote the entire argument, for example `"-package-db=C:/Users/My Name/cabal/store/ghc-VERSION/package.db"`.

The stanza applies to all local packages. You can now run `cabal build` or
`cabal repl` normally. When HLS uses a Cabal cradle, restart it after changing
the configuration so it reloads the compiler options.

The default relative path shares a tags file across packages that resolve to the
same project root. To choose an absolute destination, use:

```text
-fplugin-opt=Plugin.GhcTags:/absolute/path/to/tags
```

Relative paths in the examples above are resolved from the project configuration
directory. For example, `../tags` writes one directory above the selected
`cabal.project`, `cabal.project.local`, or `stack.yaml`.

To apply the plugin to dependencies as well, use a `package *` stanza instead
of `program-options`. This can rebuild dependencies and applies the options to
the plugin's own dependencies too; install the plugin before enabling it.

## ● GHC directly

After installing the plugin, pass its package database and enable it:

```sh
ghc -package-db=PACKAGE_DB \
    -plugin-package=ghc-tags-plugin \
    -fplugin=Plugin.GhcTags Main.hs
```

Replace `PACKAGE_DB` as described above. If multiple plugin versions are
installed, select one with `-plugin-package=ghc-tags-plugin-VERSION`, or use
`-plugin-package-id=UNIT_ID` to select a particular installed instance. Inspect
the database with:

```sh
ghc-pkg --package-db=PACKAGE_DB list ghc-tags-plugin
```

See GHC's [package documentation](https://downloads.haskell.org/ghc/latest/docs/users_guide/packages.html)
and [compiler plugin documentation](https://downloads.haskell.org/ghc/latest/docs/users_guide/extending_ghc.html#using-compiler-plugins).

## ● Stack

Add `ghc-tags-plugin` to `build-depends` in the relevant components of your
`.cabal` files, optionally behind a development flag. If the plugin is not in
your snapshot, add a release compatible with the snapshot's GHC to `extra-deps`
in `stack.yaml`.

Configure the target package in `stack.yaml` under `ghc-options`:

```yaml
ghc-options:
  my-package: -plugin-package=ghc-tags-plugin
              -fplugin=Plugin.GhcTags
              -fplugin-opt=Plugin.GhcTags:tags
```

Replace `my-package` with your package name. This Stack setup uses a declared
build dependency; the Cabal setups above avoid changing `.cabal` files.

## ● Ghcid

For automatic tag updates while editing, use the REPL quick start with ghcid:

```sh
ghcid --command="cabal repl TARGET --build-depends=ghc-tags-plugin --repl-options=-fplugin=Plugin.GhcTags --repl-options=-fplugin-opt=Plugin.GhcTags:tags"
```

If you have already enabled the plugin in `cabal.project.local`, use:

```sh
ghcid --command="cabal repl TARGET"
```

Replace `TARGET` with the component you want ghcid to load.


## ● Makefile

The [Makefile](https://github.com/coot/ghc-tags-plugin/blob/master/Makefile)
contains some useful commands, e.g. `install`,  `uninstall` or `reinstall` the
package in a `package.db` (by default into `cabal` store).  This is mostly for
development, but it could be useful in other scenarios as well.

## ● Checking and comparing tags files

The optional `gtp-check` development tool reports tag counts, ctags metadata,
parser diagnostics, sorting problems, repeated declaration locations, missing
or unreadable source files, and line numbers outside their source files:

```sh
cabal run -fgtp-check gtp-check -- tags
cabal run -fgtp-check gtp-check -- --etags TAGS
```

Source paths are resolved relative to the directory containing each tags file,
so the command can be run from another directory. Duplicate locations mean
entries with the same source file, tag name and line number; kinds and columns
are not used to distinguish them.

For a tags file copied elsewhere, pass `--source-root /path/to/original/root`
to resolve source paths from its original directory. This override applies to
both files when comparing snapshots.

To compare the declarations in two files:

```sh
cabal run -fgtp-check gtp-check -- tags --compare TAGS
cabal run -fgtp-check gtp-check -- --etags TAGS --compare tags
```

The format of the comparison file is detected automatically. Both files can
also use the same format. Comparison uses canonical source paths, names and
line numbers, and preserves duplicate counts. Differences are reported with
concrete declaration locations. Tags without numeric line addresses are
reported and excluded from line checks and comparison; a ctags `line:` field
can supply the line number for an entry addressed by a search pattern.

Each diagnostic category shows up to ten examples by default. Use
`--max-examples N` to change the limit, or `--max-examples 0` to show all.
Tags-file read or parse errors and unequal comparisons return a nonzero exit
status. Other findings are warnings.

● Exceptions
------------

If a `GHC` plugin throws an exception, `GHC` stops.  This plugin wraps
`IOException`s, to make it obvious that it filed rather than `GHC`.  This
might mean you misconfigured the plugin (by passing wrong options).  The
result might look like this:

```
ghc: panic! (the 'impossible' happened)
  (GHC version 8.6.5 for x86_64-unknown-linux):
        GhcTagsPluginIOException ../: openFile: inappropriate type (Is a directory)

```

● Tips
------

- If GHC cannot find the installed plugin, check that installation and
  compilation use the same compiler and package database:

  ```sh
  ghc-pkg --package-db=PACKAGE_DB list ghc-tags-plugin
  ```

  After changing GHC versions, reinstall the plugin with the new compiler and
  update `PACKAGE_DB` in `cabal.project.local`.

- The plugin is safe for concurrent compilation, i.e. setting `jobs: $ncpus` is
  safe.  The plugin holds an exclusive (advisory) lock on a lock file.  This
  will create synchronisation between threads / process which are using
  the same `tags` file.

- Tag updates are written to a temporary file and renamed over the destination
  only after a successful write. Interrupted compilation (including editor
  cancellation) leaves the previous tags file intact. If tags were lost with an
  older plugin version, rebuild the affected modules with `-fforce-recomp` to
  regenerate them; an incremental build may skip those modules.

- If you are working on a larger project, it might be better to not collect all
  tags in a single `tags` file, since at every compilation step one will need
  to parse a large `tags` file.  Working with tag files of size 10000 tags (or
  ~1.5MB) is ok - though this will depend on the hardware.

- If you're working on a project that is using `safe-haskell`, you will likely
  need to pass
  [-fplugin-trustworthy](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/extending_ghc.html?highlight=plugin#ghc-flag--fplugin-trustworthy)
  `ghc` flag.


● Security implications of compiler plugins
-------------------------------------------

Such plugins can:

* run arbitrary `IO`;
* modify abstract syntax tree in some way;  a malicious plugin could change
  some security parameter in your code exposing a security hole.

This plugin only reads & writes to `tags` file (and updates a shared mutable
state) as of `IO`, and does not
[modify/](https://github.com/coot/ghc-tags-plugin/blob/master/src/Plugin/GhcTags.hs#L95)
the syntax tree.

● ghc-tags - standalone program
-------------------------------

[`ghc-tags-fork`] is a fork of [`ghc-tags-hackage`]. Both provide a standalone
`ghc-tags` command.  Unlike the `hackage` version [ghc-tags-fork] is using the
latest [`ghc-tags-core`] version.

[ghc-issue-20417]: https://gitlab.haskell.org/ghc/ghc/-/issues/20417
[`ghc-tags-fork`]: https://github.com/coot/ghc-tags
[`ghc-tags-hackage`]: https://hackage.haskell.org/package/ghc-tags
[`ghc-tags-core`]: https//hackage.haskell.org/package/ghc-tags-core
[`program-options`]: https://cabal.readthedocs.io/en/stable/cabal-project-description-file.html#program-options