Skip to main content

poly-context

Implement PolyContext feature gating, environment detection, or language substitution.

Source facts

Repository
JetBrains/intellij-community
Last source activity
August 5, 2026 at 18:20
Detected SKILL.md language
English
Stars
20,445
Forks
6,009

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
poly-context
description
Implement PolyContext feature gating, environment detection, or language substitution.
# PolyContext `PolyContext` answers "what coding environment am I in right now." It is a **standalone platform mechanism** — [poly-symbols](../poly-symbols/SKILL.md) is its biggest consumer (gating which `PolySymbolQueryScopeContributor`/`QueryConfigurator` registrations apply), but `PolyContext` itself has no dependency on PolySymbols or on web frameworks being involved at all. Its defining advantage is that it is **heavily optimized for performance** (cached, cheap, `RequiresReadLock`-only), which makes it the right tool for *any* "is feature X relevant here" check that would otherwise get reinvented per-feature as an ad-hoc cached provider — not just "which JS framework is active." Confirmed non-web examples already in this repo: `PolyContext.PKG_MANAGER_RUBY_GEMS`/ `PKG_MANAGER_SYMFONY_BUNDLES` constants live right next to `PKG_MANAGER_NODE_PACKAGES` in `PolyContext.kt`, and `ruby/tests/.../gem/GemContextTest.kt` queries a `"timecop-context"` kind (is the `timecop` Ruby gem present?) with no PolySymbols query executor or web framework in sight. For any given context **`kind`** (e.g. `"framework"`), at most one **`name`** is active at a location, or `null` if none applies. ```kotlin PolyContext.get("framework", psiElement) // "vue" | "angular" | "astro" | null ``` ## Two location overloads ```kotlin PolyContext.get(kind: PolyContextKind, location: PsiElement): PolyContextName? PolyContext.get(kind: PolyContextKind, location: VirtualFile, project: Project): PolyContextName? ``` | Location | Cost | Capability | |---|---|---| | `PsiElement` | normal | can inspect the PSI tree (e.g. check imports) — use for most coding-assistance logic | | `VirtualFile` + `Project` | cheap, indexing-safe | no PSI access; results may legitimately differ from the `PsiElement` answer for the same file once parsed | **The `VirtualFile`+`Project` overload is what makes `PolyContext` usable during language substitution** — i.e. *before* any PSI exists for the file, when the platform is still deciding which `Language` to parse it as. Concrete chain in this repo: `WebFrameworkHtmlLanguageSubstitutor.getLanguage(file: VirtualFile, project: Project)` (a `LanguageSubstitutor`, `plugins/JavaScriptLanguage/web-platform/.../WebFrameworkHtmlLanguageSubstitutor.kt:15`) → `WebFramework.forContext(file, project)` → `PolySymbolFramework.inLocation(file, project)` (`community/platform/polySymbols/src/com/intellij/polySymbols/framework/PolySymbolFramework.kt:52-53`) → `PolyContext.get(KIND_FRAMEWORK, file, project)`. The result picks which HTML dialect (Vue/Angular/plain) the file's `Language` is substituted to, entirely before a PSI tree is built. Canonical PolySymbols use case: web framework detection. Only one of Vue/Angular/React/Astro can be the active `framework` at a location, and each plugin's `PolySymbolQueryScopeContributor`/ `QueryConfigurator` gates its registrations with `.inContext { it.framework == MyFramework.ID }` (see [poly-symbols/references/query-model.md](../poly-symbols/references/query-model.md)) — but this is one consumer among many, not the reason `PolyContext` exists. ## `PolyContextProvider` — direct/dynamic detection Implement `isEnabled()` (or `isForbidden()`) and register at EP `com.intellij.polySymbols.context`. Results are called very often — **cache them** (`CachedValuesManager`). ```kotlin PolyContext.get("stimulus-context", psiElement) == "true" ``` **Blocking**: `isForbidden()` with name `"any"` suppresses an entire context kind — e.g. a Python templating (Blade-style) blocker disables the whole `framework` kind so it never conflicts with Vue/Angular detection. Real examples in this repo/docs: `StimulusContextProvider` (checks for a JS import, caches the result), `PyTemplatesWebContextBlocker` (forbids `framework` when Python templating is detected). For a full custom-detector example, see `VueFileContextProvider` (`contrib/vuejs/vuejs-backend/src/org/jetbrains/vuejs/context/VueFileContextProvider.kt`) — returns true unconditionally for `.vue` files, and for any HTML-compatible file whose `<script src="...">` matches a known Vue CDN/filename pattern (a cached-value scan, not a per-call check). Angular's equivalent is `AngularCliContextProvider` (`contrib/Angular/.../org/angular2/cli/`), registered at `polySymbols.context kind="framework" name="angular"`. ## Context rules — the declarative, faster alternative Many providers redundantly re-check the same thing (is package X a dependency?). Context **rules** are declarative and let the platform compute proximity once instead of running N ad-hoc providers. ### Via Web Types Under a top-level `contexts-config` property in a Web Types JSON file (see [poly-symbols/references/web-types.md](../poly-symbols/references/web-types.md)): `kind` → `name` → `enable-when`/`disable-when`. (Pre-2024.2 files use a deprecated layout where `name` is top-level and `kind` a sibling property — don't use that shape for new files.) `enable-when` rule kinds: `file-extensions`, `file-name-patterns`, `ide-libraries`, `project-tool-executables`, `node-packages`, `ruby-gems`. `disable-when` is more limited: `file-extensions`, `file-name-patterns` only. **Proximity scoring** (lower = closer/stronger match; the winning `name` for a `kind` is whichever rule has the lowest total): - `file-extensions` / `file-name-patterns` → `0.0` (perfect match) - `ide-libraries` / `ruby-gems` → `Double.MAX_VALUE` (project/module-level match) - `node-packages` → computed from `package.json` location + dependency type: same directory as base `0.0`, `+1.0` per parent directory walked up, plus importance modifiers — `peerDependencies +0.1`, `bundledDependencies +0.2`, `dependencies +0.3`, `optionalDependencies +0.4`, `devDependencies +0.5`, indirect (`node_modules`) `+0.6`. Web Types shipping a context rule are registered at EP `com.intellij.polySymbols.webTypes` (currently Node Package Manager only, requires the JS plugin) with two naming strategies: name the file after the real npm package if the rule should trigger on that package's presence, or use an arbitrary name + `enableByDefault="true"` if it should always apply. ### Via code — `PolyContextRulesProvider` For rules that can't be expressed as static file-based data, implement a `PolySymbolQueryConfigurator` and override `getContextRulesProviders(project, dir): List<PolyContextRulesProvider>` at EP `com.intellij.polySymbols.queryConfigurator`. **Rule output must be stable** — a change triggers project rescanning and cache invalidation. ## `.ws-context` — user override file Since 2024.1.2, users can force context values with a `.ws-context` JSON file: - `<context kind>` → `<context name>` — direct top-level assignment (implicitly applies to `/**`). - `<GLOB path>` → nested context-details object — GLOB supports only `*` in the final segment; `**` matches nested directories. When multiple patterns match a file, resolution priority is: (1) most path segments excluding `**`, (2) prefer patterns with an actual file-name match (not ending in `**`/`/`), (3) first-defined wins. ## `PolyContextSourceProximityProvider` For integrating a new package manager or a language with a global-library concept, register at EP `com.intellij.polySymbols.contextSourceProximityProvider` and implement `calculateProximity()`, computing proximity per `sourceNames` entry matching a `sourceKind` (e.g. a dependency type). Return a `Result` with `modificationTrackers` — keep the tracker count minimal since it feeds **every** `PolyContext.get()` call. ## Related - **[poly-symbols](../poly-symbols/SKILL.md)** — PolyContext's biggest consumer, not something it depends on. - **[poly-symbols/references/web-types.md](../poly-symbols/references/web-types.md)** — `contexts-config` file format. - Official docs: [Poly Symbols Context](https://plugins.jetbrains.com/docs/intellij/polysymbols-context.html) (the only official doc page for this API — despite the URL/page name, treat it as documenting a general-purpose context mechanism, not something PolySymbols-exclusive).
View on GitHub