- Java 21 — install via your preferred tool (e.g. mise, sdkman, homebrew)
- Gradle — use the
./gradlewwrapper; no separate installation needed
CI uses
mise(mise.tomldefines the full toolchain includinggh,jfrog-cli, andmaven). For local development the wrapper and a JDK 21 are sufficient.
All dependency resolution goes through Artifactory. There is no Maven Central fallback — builds fail without this configuration.
Local development — add to ~/.gradle/gradle.properties:
artifactoryUrl=https://repox.jfrog.io/artifactory
artifactoryUsername=<your-username>
artifactoryPassword=<your-access-token>CI uses environment variables instead (
ARTIFACTORY_URL,ARTIFACTORY_USERNAME,ARTIFACTORY_ACCESS_TOKEN). Environment variables take priority overgradle.propertiesif both are set.
The plugin communicates with SLOOP (SonarLint Out-Of-Process) — the SLCORE analysis engine running as a separate child process.
Communication model
- Plugin → SLOOP (outbound): all calls go through
core/BackendService.kt, an app-level IntelliJ service. It launches SLOOP viaSloopLauncherand communicates over JSON-RPC using thesonarlint-rpc-*libraries. - SLOOP → Plugin (inbound callbacks): SLOOP notifies the plugin through
SonarLintIntelliJClient.kt, a singleton implementingSonarLintRpcClientDelegate.
IntelliJ service layers
Services are accessed via SonarLintUtils.getService(Class) (app-level) or SonarLintUtils.getService(project, Class) (project-level).
- App-level (one instance per IDE):
BackendService,SonarLintPlugin - Project-level (one instance per open project):
AnalysisSubmitter,ProjectBindingManager,AnalysisStatus,ReportTabManager
Where to look by change type
| Change area | Start here |
|---|---|
| New backend RPC call | core/BackendService.kt |
| New SLOOP callback | SonarLintIntelliJClient.kt |
| Analysis trigger logic | analysis/AnalysisSubmitter.kt, trigger/EditorOpenTrigger.kt |
| Current file findings display | ui/currentfile/CurrentFilePanel.kt |
| Full report display | ui/report/ReportPanel.kt |
| SonarQube/SonarCloud connection | core/ProjectBindingManager.java |
| Module | Purpose |
|---|---|
src/ |
Main IntelliJ plugin code |
common/ |
Shared plugin logic |
clion/, clion-resharper/ |
CLion-specific code |
rider/ |
Rider-specific code |
nodejs/ |
Node.js language support |
git/ |
Git integration |
test-common/ |
Shared test utilities |
its/ |
UI integration tests (CI-only) |
Compile only — fastest, catches syntax/type errors:
./gradlew compileKotlin compileTestKotlinUnit tests only — excludes ITs and build artifact:
./gradlew -x :its:check -x :buildPlugin -x :buildPluginBlockmap -x :cyclonedxBom checkBuild plugin only — no tests:
./gradlew -x test -x :its:check :buildPluginFull check and build (excludes ITs, which are CI-only):
./gradlew -x :its:check check buildPluginPlugin compatibility verification (local, minimal):
./gradlew :verifyPlugin -PverifierEnv=CITo launch an IntelliJ instance with the plugin installed for manual validation:
./gradlew :runLocalIdeThis downloads IntelliJ Community 2024.2 on first run. To use a local IDE installation instead:
./gradlew :runLocalIde -PrunIdeDirectory=<path-to-ide>On macOS,
<path-to-ide>must point to theContentsdirectory inside the.appbundle (e.g."/Applications/IntelliJ IDEA.app/Contents").
The sandbox (instance files, JDK config) is stored under build/sonarlint-test. Running ./gradlew clean will wipe it.
ITs require a running display and SONARCLOUD_IT_TOKEN. Supported on Windows and Linux only — macOS is not supported due to native file dialog incompatibility with the UI test framework.
On headless Linux, xvfb is required. On Windows and Linux with a display, start the IDE with the plugin, wait for it to be ready, then run the tests:
./gradlew :its:runIdeForUiTests -PijVersion=IC-2024.2 &
./gradlew :its:check -PijVersion=IC-2024.2Set these to skip downloading the IDE during builds (significantly faster first run):
IDEA_HOME=<path-to-intellij-installation>
CLION_HOME=<path-to-clion-installation>
RIDER_HOME=<path-to-rider-installation>
ULTIMATE_HOME=<path-to-intellij-ultimate-installation>Alternatively, pass via Gradle property:
./gradlew :runLocalIde -PrunIdeDirectory=<path-to-ide>Dependencies are declared in gradle/libs.versions.toml. Lock files (gradle.lockfile in each module) are committed and must stay in sync.
After adding, removing, or updating any dependency, regenerate all lock files:
./gradlew --write-locks dependencies \
:common:dependencies \
:clion:dependencies \
:clion-resharper:dependencies \
:nodejs:dependencies \
:git:dependencies \
:rider:dependencies \
:test-common:dependencies \
:its:dependenciesAlways commit the updated lock files together with the dependency change.