Skip to content
cccskills
FreePublished on GitHub

symbols-api

Use IntelliJ’s Symbol API to model semantic elements, declarations, and references without requiring every symbol to come from PSI. Learn when raw symbols fit, how to connect references to navigation, usage search, rename, and documentation, and when the PolySymbols framework can supply that work more directly.

OriginalShow the original description

Use IntelliJ Symbol API for declarations, references, and rename.

JetBrains/intellij-community20,664 stars(GitHub 20,664 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Modeling declarations and references for a language feature
  • Resolving placeholders inside strings to call arguments
  • Adding navigation, find-usages, or rename support for symbols

Files included (1)

  • SKILL.md10.6 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

<!-- Generated by community/.ai/render-guides.mjs; edit community/.agents/skills/symbols-api/SKILL.md -->

Symbol API

com.intellij.model.Symbol (community/platform/core-api/src/com/intellij/model/Symbol.java) is the platform's generic replacement for "resolve a PsiReference to a PsiElement". A Symbol represents a semantic element in some model — a language, a framework, a database schema — and decouples navigation/find-usages/rename/documentation from PSI. A Symbol is not required to be backed by a PsiElement, and it is incorrect to try to obtain one from a Symbol.

Canonical examples from the docs: a Java local variable (backed by PsiVariable), a compiled class from JDK stubs (not tied to any project), a Spring bean (created dynamically by framework support, project-bound but not PSI-backed), a database column (defined by a data source, not PSI-backed and not project-bound).

Before hand-wiring any of the mechanisms on this page, look at poly-symbols first. PolySymbol : Symbol, and the PolySymbols framework exists specifically to save you from writing this boilerplate by hand — its query executor, completion-provider base class, and default searchTarget/renameTarget/ getDocumentationTarget/getNavigationTargets implementations give you declarations, references, completion, search, rename, and navigation with a fraction of the code this page documents. Treat PolySymbols as the default, go-to way to implement a new language or framework's symbol support — it is not limited to cross-language microsyntax sharing, that's simply the feature that falls out for free once the query/pattern machinery exists. Reach for the raw Symbol/ PsiSymbolReference API on this page directly only when a symbol genuinely doesn't fit PolySymbols' model: a single, self-contained resolve with no query/completion surface of its own (a format-string placeholder resolved from one call expression, a markdown link label), or something not project-bound / not PSI-backed at all (a compiled-class symbol from JDK stubs, a database column) where the extra query/scope layer would add nothing.

Contract and lifecycle

public interface Symbol {
  @NotNull Pointer<? extends Symbol> createPointer();
  @Override boolean equals(Object obj);
  @Override int hashCode();
}
  • equals/hashCode must reflect semantic identity, not object identity — the platform uses Symbol as a cache key and de-duplicates concurrently-computed instances that are equal.
  • Lifecycle is a single read action. A Symbol is safe to pass between APIs within one read action, but must not be referenced across read actions. Call createPointer() while still valid, store the Pointer, and call Pointer.dereference() in the next read action to get a fresh (or the same, if still valid) instance.
  • All of this applies unchanged to PolySymbol, which extends Symbol.

Declarations

Model where a symbol is declared in a PSI tree via PsiSymbolDeclaration (community/platform/core-api/src/com/intellij/model/psi/PsiSymbolDeclaration.java): getDeclaringElement(), getRangeInDeclaringElement(), getSymbol(). A symbol can have zero declarations (files — "only references"), one (a Java local variable), or several (a C# partial class, a property key declared in multiple files).

Two ways to supply declarations:

  • Register a PsiSymbolDeclarationProvider (getDeclarations(element, offsetInElement)) at EP com.intellij.psi.declarationProvider. The platform calls it for each PsiElement from the leaf at the caret up to the containing file, with offsetInElement as a hint (-1 means "all declarations").
  • Implement PsiSymbolDeclaration directly on the PsiElement.

References — three kinds

References are modeled by PsiSymbolReference (community/platform/core-api/src/com/intellij/model/psi/PsiSymbolReference.java): getElement(), getRangeInElement(), resolveReference(): Collection<? extends Symbol> (empty collection = unresolved), and resolvesTo(target) (override to short-circuit before a full resolve, e.g. by comparing text first). For the common single-target case, extend SingleTargetReference.

KindWhenMechanism
OwnReference is intrinsic to the language itself (e.g. x in x * 2 referencing var x = 42)PsiElement.getOwnReferences()
ExternalReference is not recognized by the host language, contributed by another plugin (e.g. a filename string in new File("users.txt"))Host implements PsiExternalReferenceHost; other plugins register a PsiSymbolReferenceProvider
ImplicitReference only supports resolving to a target (navigation, hover doc) — not find-usages/rename starting from the target (e.g. var in var x = new Person())ImplicitReferenceProvider

Language support should not assume external references are absent — they may be contributed by any plugin. Own vs. external is a modeling choice about who "owns" the reference semantics, not a technical distinction in PsiSymbolReference itself.

External references — PsiSymbolReferenceProvider

Interface (community/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceProvider.java):

public interface PsiSymbolReferenceProvider {
  Collection<? extends PsiSymbolReference> getReferences(PsiExternalReferenceHost element, PsiSymbolReferenceHints hints);
  Collection<? extends SearchRequest> getSearchRequests(Project project, Symbol target);
}

Register via PsiSymbolReferenceProviderBean at EP com.intellij.psi.symbolReferenceProvider, declaring hostLanguage, hostElementClass, targetClass (common supertype of resolved symbols), referenceClass (defaults to PsiSymbolReference; narrow it so the platform can skip providers that can't possibly answer a query for a more specific reference type), and implementationClass.

Worked examples in this repo:

  • community/java/java-impl/src/com/siyeh/ig/format/StringFormatSymbolReferenceProvider.java — resolves %s/{0}-style placeholders inside String.format/printf/MessageFormat call arguments back to the corresponding call argument, complete with its own Symbol (FormatArgumentSymbol-style), NavigatableSymbol, and a SearchTarget/rename-capable design — read this file end to end as the reference implementation for "symbol lives inside a string literal, references live inside sibling call arguments."
  • community/jvm/jvm-analysis-impl/src/com/intellij/analysis/logging/resolve/LoggingArgumentSymbolReferenceProvider.kt — the same idea for JVM logging framework {}/{0} placeholders across Java/Kotlin.
  • Markdown link-label resolution (linked from the official docs as a third sample) follows the same shape one level simpler: label text in [text][label] resolves to the [label]: url definition.

Implicit references — ImplicitReferenceProvider

(community/platform/core-api/src/com/intellij/model/psi/ImplicitReferenceProvider.java, EP com.intellij.psi.implicitReferenceProvider):

public interface ImplicitReferenceProvider {
  default PsiSymbolReference getImplicitReference(PsiElement element, int offsetInElement) { ... }
  default Collection<? extends Symbol> resolveAsReference(PsiElement element) { return emptyList(); }
}

Usually you only override resolveAsReference — the default getImplicitReference wraps a non-empty result in an ImmediatePsiSymbolReference. This enables navigation and hover-link highlighting on the target Symbol, but the platform will never find this "reference" via find-usages or rename-from-target, because it was never indexed as a searchable reference. Called for every element from the caret leaf up to the file — keep it cheap.

PolySymbols' PsiPolySymbolReferenceProvider is built on top of exactly this external-reference mechanism, but replaces the manual getReferences/getSearchRequests plumbing with a single getReferencedSymbol(element): PolySymbol? you implement once — resolution, search, and rename all follow from the returned symbol. Prefer it over hand-writing PsiSymbolReferenceProvider unless you have a concrete reason not to — see the "References" section of poly-symbols/references/query-model.md.

Hooking a Symbol into platform features

A bare Symbol only gives you resolve. To light up the rest of the IDE, implement (or delegate to) these on your Symbol/PolySymbol:

FeatureInterfaceNotes
NavigationNavigatableSymbol (community/platform/core-api/src/com/intellij/navigation/NavigatableSymbol.java)getNavigationTargets(project); SymbolNavigationService helps build NavigationTargets
Find usagesSearchTarget/SearchTargetSymbol (community/platform/lang-impl/src/com/intellij/find/usages/api/SearchTarget.kt)presentation(), usageHandler, maximalSearchScope, textSearchRequests; register a factory at com.intellij.lang.symbolSearchTarget if you don't want the Symbol itself to implement it
RenameRenameableSymbol/RenameTarget (community/platform/lang-impl/src/com/intellij/refactoring/rename/symbol/RenameableSymbol.kt)or register a SymbolRenameTargetFactory at com.intellij.rename.symbolRenameTargetFactory
DocumentationDocumentationTargetvia PsiSymbolReferenceService/language-specific hookup

PolySymbol (see poly-symbols) already wires all four of these through searchTarget/renameTarget/getDocumentationTarget/getNavigationTargets properties with sensible defaults. This is the general pattern across this whole page: almost everything above is what PolySymbol implements for you. Implement PolySymbol and get all four; implementing raw Symbol means writing every row of the table above by hand for every symbol kind you add.

Related

  • poly-symbols — start here for new language/framework symbol support. It's built on Symbol and is the intended default implementation path, not a specialized add-on — reach for the mechanisms on this page directly only when a symbol falls outside its model (see the decision note above).
  • poly-context — general-purpose, performance-optimized context detection; PolySymbols is one consumer, not a dependency.
  • Official docs: Symbols, Declarations and References.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

actions

FreeSummary

Implement or update IntelliJ `AnAction` classes and their `plugin.xml` registrations. Keep labels and descriptions in message bundles, configure optional icons, and launch suspending work through the event coroutine scope so synchronous action handling does not freeze the IDE.

  • Adding a new IntelliJ action
  • Updating an action's registration or icon
  • Moving action labels into a message bundle
JetBrains/intellij-community20.7KUpdated Oct 26, 2026

Move IntelliJ JPS test modules to Bazel and diagnose failures that occur only under Bazel. The guide helps trace missing dependencies, plugins, test data, and runfiles, then verify the generated target and migrated tests.

  • Migrating a JPS test module to Bazel
  • Diagnosing a test failure that occurs only in Bazel
  • Checking Bazel dependencies and test data wiring
JetBrains/intellij-community20.7KUpdated Oct 26, 2026

code-style

FreeSummary

Write or review Kotlin and Java code in line with IntelliJ project conventions. The guidance covers language choice, formatting, sealed types, threading annotations, and safe handling of control flow exceptions so changes fit the surrounding codebase.

  • Writing a new Kotlin file that follows IntelliJ conventions
  • Reviewing Java code and checking its style guidance
  • Checking formatting, sealed-type handling, or exception handling
JetBrains/intellij-community20.7KUpdated Oct 26, 2026

commits

FreeSummary

Create, amend, or rename IntelliJ commits with messages that follow the project’s ticket and label formats. Keep each commit focused, explain the reason and decision in the body when needed, and pass the message safely through the shell or PowerShell.

  • Writing a message for a change linked to a YouTrack ticket
  • Preparing a commit message for tests, cleanup, or documentation
  • Amending or renaming an existing IntelliJ commit
JetBrains/intellij-community20.7KUpdated Oct 26, 2026

Compare how Python type checkers interpret a code snippet or file. The script runs selected tools, including ty, pyrefly, basedpyright, mypy, and zuban, then produces a Markdown report with verdicts, timings, and each checker's diagnostic output.

  • Comparing type inference for a Python snippet
  • Checking whether a Python expression should be a type error
  • Reviewing differences among Python type checkers
JetBrains/intellij-community20.7KUpdated Oct 26, 2026

Shape Compose Desktop UI code by keeping composables focused on rendering and placing application decisions in presenters or coordinators. Get guidance on state ownership, effects, reusable component APIs, lazy-list keys, animation, and performance for individual UI changes.

  • Separating screen rendering from application decisions
  • Choosing which UI state belongs in composition
  • Structuring effects and coroutine work
JetBrains/intellij-community20.7KUpdated Oct 26, 2026

See all skills by JetBrains

Report a problem with this skill