GtBridge.Xref (gt_bridge v0.17.1)

Copy Markdown View Source

I am a long-lived wrapper around OTP's :xref cross-reference server.

The previous code in GtBridge.Analysis started a fresh :xref server, added the relevant app's ebin directory, ran one query, and stopped the server — for every call to function_references/3 and module_graph/1. The add_directory step is the expensive part; xref parses every .beam file in the directory to build its edge index. Doing that per-query made interactive paths (C-n, the |> expander) perceptibly slow.

I keep one xref server running for the lifetime of the BEAM. Indexing runs in a background :low-priority Task so it yields to any normal-priority work — eager add_directory on every loaded app contends with the OTP code server enough to cascade GT's startup module_details fan-out into 5s eval timeouts at normal priority. Queries arriving before indexing completes return {:ok, []} rather than blocking. When BeamModuleRecompiled fires (from hot_reload/2 via the event bus), I call :xref.replace_module/3 on just that module — so the index stays consistent with the live BEAM without rebuilding from scratch.

Public API

  • q/1 — run an xref query string and return its :xref.q/2 result
  • replace/1 — explicitly refresh one module's edges (also called automatically on BeamModuleRecompiled)

Subscriptions

I subscribe to GtBridge.Events.AnyModuleEvent and react to :recompiled events by calling replace/1 on the affected module.

Summary

Functions

Returns a specification to start this module under a supervisor.

I am true once the initial indexing pass has completed. Until then every q/1 returns {:ok, []} per the moduledoc contract.

Block the caller until indexing finishes (or timeout_ms elapses). Useful from test setup so assertions about xref data don't race the background indexer. Returns :ok when ready, exits with :timeout like any other GenServer.call/3 if the deadline lapses.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

q(query)

@spec q(charlist()) :: {:ok, list()} | {:error, term(), term()}

ready?()

@spec ready?() :: boolean()

I am true once the initial indexing pass has completed. Until then every q/1 returns {:ok, []} per the moduledoc contract.

replace(mod)

@spec replace(module()) :: :ok

start_link(_)

@spec start_link(term()) :: GenServer.on_start()

wait_until_ready(timeout_ms \\ 30000)

@spec wait_until_ready(timeout()) :: :ok

Block the caller until indexing finishes (or timeout_ms elapses). Useful from test setup so assertions about xref data don't race the background indexer. Returns :ok when ready, exits with :timeout like any other GenServer.call/3 if the deadline lapses.

Already-ready replies immediately; otherwise the caller is parked on a deferred reply and woken from handle_cast(:indexing_done, _).