> ## Documentation Index
> Fetch the complete documentation index at: https://openidea.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Indexing

> Publish cross-file associations that the IDE uses for navigation, gutter markers, Find Usages and Rename, and share a project's HTTP endpoints.

Editor providers answer questions about **one** document while the user types. Some
features need the whole project: a MyBatis mapper method and its XML statement, a route
and its controller. For those, a plugin can publish data once per document and let the
host answer from memory:

* **Document indexers** (`indexing.wit`, API 1.2): definitions, references, aliases and
  gutter markers keyed by strings the plugin chooses. The host resolves Go to Declaration,
  gutter markers and, since API 1.3, Find Usages and Rename from them, without calling the
  plugin again.
* **Endpoint providers** (`endpoints.wit`, API 1.3): a project's HTTP routes for Search
  Everywhere and the HTTP client.

For a quick lookup in the host's existing symbol index, `Project::symbols(query, limit)`
(API 1.3) is enough; see [File actions](https://openidea.sh/en/plugins/guides/file-actions.md#project-files).

## Declare a document indexer

```toml theme={null}
engines.openidea = "^1.3"
wasm = "dist/plugin.wasm"

[capabilities]
required = ["project.read"]

[[contributes.documentIndexers]]
extensions = ["java", "xml"]
```

Implement `openidea_plugin::indexing::DocumentIndexer` and register `indexing`
(`register_plugin!(MyPlugin, providers, indexing)` when the plugin also has interactive
providers). Components built with other tools target the `plugin-indexing` world.
Indexers need `project.read` and run only in trusted projects.

## Publish edges

`index(doc)` receives the document's text (the editor's while it is open) and the symbols
the host's syntax support found in it, and returns its `Edges`:

| Field | Meaning |
| - | - |
| `definitions` | Where keys are declared. |
| `references` | Ranges that go to the definitions of their key on Go to Declaration (`⌘B` / `Ctrl+B`) and Ctrl/⌘-click. Elsewhere the language server answers as usual. |
| `aliases` | `Alias { prefix, target }`: keys starting with `prefix` also resolve as if they started with `target` (both end with `#`), for inherited members. |
| `markers` | Gutter icons on the anchor's first line that go to the definitions of their key. |

A key is any string the plugin chooses (`com.example.UserMapper#findById`); keys are
compared as is. Ranges are UTF-16. The edges of a document replace its previous ones.

From the [indexer fixture](https://github.com/l0ng-ai/openidea/tree/main/crates/ide-plugin/tests/fixtures/indexer),
a line-oriented test language where `def key` defines, `ref key` refers and
`alias prefix# target#` aliases:

```rust theme={null}
use openidea_plugin::indexing::{Alias, Anchor, Document, DocumentIndexer, Edges};
use openidea_plugin::{Position, Range, register_plugin};

impl DocumentIndexer for Indexer {
    fn index(&mut self, doc: &Document) -> Edges {
        let mut out = Edges::default();
        for (line, text) in doc.text.lines().enumerate() {
            let Some((kind, value)) = text.split_once(' ') else { continue };
            let range = Range {
                start: Position { line: line as u32, character: kind.len() as u32 + 1 },
                end: Position { line: line as u32, character: text.encode_utf16().count() as u32 },
            };
            let anchor = Anchor { key: value.into(), range };
            match kind {
                "def" => out.definitions.push(anchor),
                "ref" => {
                    out.references.push(anchor.clone());
                    out.markers.push(anchor);
                }
                "alias" => {
                    if let Some((prefix, target)) = value.split_once(' ') {
                        out.aliases.push(Alias { prefix: prefix.into(), target: target.into() });
                    }
                }
                _ => {}
            }
        }
        out
    }
}

register_plugin!(Indexer, indexing);
```

`doc.symbols` lists the host's symbols (`name`, `kind`, `container`, `range`), so a plugin
can anchor keys to declarations without parsing the language itself.

### Resolution rules

* When a key has definitions in the same detected module, other modules' definitions are
  left out. Several targets open a chooser.
* Alias chains are followed with bounded depth and candidates.
* A marker's tooltip lists its targets.

## Branded markers (API 1.3)

A `[[contributes.markerIcons]]` entry with `markers` gives markers whose key starts with
one of its prefixes a package icon and an optional tooltip; the longest matching prefix
wins, then declaration order. Other markers keep the navigation glyph.

```toml theme={null}
[[contributes.markerIcons]]
id = "usages"
icon = "icons/usages.svg"
iconDark = "icons/usages_dark.svg"
markers = ["usages#"]                # up to 16 prefixes of 1–256 bytes
tooltip = "Usages of this class"     # 1–256 bytes, shown above the targets
```

A marker published as `Anchor { key: "usages#User".into(), range }` then shows that icon.
IDEs older than API 1.3 refuse a manifest with `markers` or `tooltip`.

## Find Usages and Rename (API 1.3)

With no extra export, the graph also answers:

* **Find Usages** on a published definition or reference lists every definition and
  reference of its key alongside the language server's results.
* **Rename** replaces every such anchor with the new name, merged with the language
  server's edits into one workspace edit. It always shows a preview, even for one file;
  cancelling changes nothing, and applying it is one undoable change.

For this to work, anchors must cover **exactly the name**: no quotes, delimiters or
surrounding syntax, in UTF-16 columns (an astral character is two columns). Markers are
excluded, since their ranges may cover an annotation or a whole line.

* Alias-linked keys follow the same targets and module preference as navigation.
* Queries flush queued indexing work but don't rescan the project; external changes must
  reach the file watcher and be indexed first. If a document changed, wait for indexing and
  rename again.
* When no language server can rename at the position, an anchor on a code identifier (per
  the file's syntax tree) is not renamed, because the declaration, imports and file name
  would be missed. Anchors in strings, comments, attribute values and files without a
  grammar are still renamed from the index.

## How indexing runs

* Initial discovery reuses the host's project index. The editor schedules cancellable
  batches of at most 16 files; edits and file changes invalidate the affected entries
  before they are indexed again.
* Reloading, disabling or a failing plugin drops its graph. Nothing is answered from a
  document version older than the editor's.

Limits: 2 MiB per document, 20,000 files per graph, 4,096 definitions and references and
4,096 markers per document, 256 aliases, 200,000 entries per graph, 16 project/window
graphs and 256 candidates per lookup. A document over a limit is not indexed; editing is
never blocked.

To try the fixture, build it with `plugins/build.sh crates/ide-plugin/tests/fixtures/indexer`
and load the folder as a development plugin in a trusted project with `.idx` files; its
README lists the steps. `cargo test -p ide-host --test plugin_indexing` runs the real
component contract checks.

## Project HTTP endpoints (API 1.3)

A plugin that knows a framework's routes can share them with the IDE: Search Everywhere's
**Endpoints** section, URL completion in `.http` files, and an **Open in HTTP Client** gutter
action on each declaration.

```toml theme={null}
engines.openidea = "^1.3"

[capabilities]
required = ["project.read"]

[[contributes.endpoints]]
files = ["config/routes.rb", "routes/**/*.php"]
```

```rust theme={null}
use openidea_plugin::endpoints::{Endpoint, EndpointsProvider};
use openidea_plugin::{Error, Invocation, Location, Position, Range, register_plugin};

impl EndpointsProvider for Routes {
    fn endpoints(&mut self, ctx: &Invocation) -> Result<Vec<Endpoint>, Error> {
        let project = ctx.project().ok_or("no project")?;
        let root = project.root()?;
        let text = project.read_file("config/routes.rb")?;
        Ok(parse_routes(&text)
            .into_iter()
            .map(|(line, method, path, handler)| Endpoint {
                method,                         // None accepts any method
                path,                           // path parameters in braces: /photos/{id}
                framework: "Rails".into(),
                handler: Some(handler),         // display name, e.g. PhotosController#show
                location: Location {
                    path: format!("{root}/config/routes.rb"),
                    range: Range::new(Position::new(line, 0), Position::new(line, 0)),
                },
            })
            .collect())
    }
}

register_plugin!(Routes, endpoints);
```

* `endpoints(ctx)` runs the first time the IDE needs the project's routes, and again after a
  file matching `files` is created, changed or removed; in between the host answers from its
  cache. Return the complete list (an empty list clears it).
* It runs in the background and can't prompt. Each endpoint's location must be inside the
  project.
* **Open in HTTP Client** opens the generated-requests scratch file; requests start at
  `http://localhost:8080`, path parameters become HTTP client variables, and any-method
  routes use GET.
* The host validates the whole batch before replacing the last valid result: up to 10,000
  endpoints, 4 MiB of route metadata, 16 MiB of declaration documents.
* Disabling, uninstalling, reloading or crashing withdraws the routes; idle reclaim keeps
  them cached. This API adds no Endpoints tool window.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.