| name | intellij-plugin-dev |
| description | Develop IntelliJ Platform plugins (IDEA / PhpStorm / etc.) following the official JetBrains SDK guidelines. Use when the user asks to add an extension point, service, action, listener, inspection, index, tool window, notification, run configuration, or any other platform-level plugin feature. This is the general platform skill — for this project's specific conventions also consult /kotlin-dev. |
IntelliJ Platform Plugin Developer
Sourced from the official JetBrains SDK docs
(https://plugins.jetbrains.com/docs/intellij/). Use this skill as the
foundation; layer /kotlin-dev on top for this repo's conventions and
/kotlin-test for tests.
Decision tree — what kind of feature are you adding?
| I want to... | Open |
|---|
Set up / modify plugin.xml, module structure, resources | structure.md |
| Add or consume an Extension Point | extensions.md |
| Expose shared state / long-lived logic | services.md |
| Run on EDT / background; read/write PSI safely | threading.md |
| Walk / analyze source code | psi.md |
| Look up symbols fast across the project | indexes.md |
| Surface code warnings + quick fixes | inspections.md |
| Add menu items, tool windows, notifications, listeners | actions-ui.md |
| Change Gradle / build / runIde / verifyPlugin | gradle.md |
| Kotlin-specific pitfalls and idioms | kotlin.md |
Each file is short and self-contained — load only the one(s) you need.
Ten non-negotiable platform rules
- Never block the EDT. Long work →
Task.Backgroundable, coroutines, or
NonBlockingReadAction.
- Mutate PSI / VFS inside a
WriteAction on the EDT only. Reads need a
ReadAction from background threads.
- Get services on-demand; never cache them in fields —
service<MyService>() / project.service<MyService>().
- Register listeners declaratively in
plugin.xml
(<applicationListeners>, <projectListeners>) — they're created lazily.
- Iterate EPs lazily —
EP.lazyDumbAwareExtensions(project), not
extensionList, unless you truly need all extensions eagerly.
- Mark long-computation extensions as
DumbAware when they don't need
indexes; otherwise gate them behind DumbService.isDumb(project).
- Cache PSI-derived values with
CachedValuesManager + a
PsiModificationTracker. Don't hand-roll memoization.
- Every user-visible string comes from a
<resource-bundle> — no inline
English.
- Bump
getVersion() on any index whose key/value layout changes —
otherwise stale data persists across upgrades.
- Inspections ship with an HTML description at
inspectionDescriptions/<shortName>.html, or they won't pass verification.
Typical workflow for a new feature
- Decide scope — application / project / module / per-PSI-element.
- Pick the right mechanism (service vs. EP vs. listener vs. action).
- Declare it in
plugin.xml (or the right config-file for an optional
dependency).
- Implement in Kotlin. Keep classes
final unless you need extensibility.
- Wire i18n via
TemporalBundle.message("key").
- Run
./gradlew build runIde verifyPlugin — all three must pass.
- Add tests per
/kotlin-test.
Reference links (official)