Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .codegraph/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
3 changes: 0 additions & 3 deletions .idea/inspectionProfiles/idea_default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

152 changes: 152 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# AGENTS.md

This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.

## Project Overview

This is the **Hyperskill Academy** IntelliJ Platform plugin (formerly JetBrains Academy plugin). It enables educational features in
JetBrains IDEs for learning programming and creating interactive courses. The plugin integrates
with [Hyperskill](https://hi.hyperskill.org/how-we-teach).

## Build Commands

```bash
# Build the entire plugin
./gradlew buildPlugin

# Run tests for a specific module
# Note: hs-core tests must run via :intellij-plugin:test (hs-core uses base plugin without test runtime)
./gradlew :intellij-plugin:test
./gradlew :intellij-plugin:hs-Java:test

# Run a single test class
./gradlew :intellij-plugin:test --tests "org.hyperskill.academy.learning.format.CourseFormatTest"

# Run a single test method
./gradlew :intellij-plugin:test --tests "org.hyperskill.academy.learning.format.CourseFormatTest.testSomething"

# Run IDE with the plugin (various IDEs supported)
./gradlew runIde # Default IDE based on baseIDE property
./gradlew runIdea # IntelliJ IDEA Ultimate
./gradlew runPyCharm # PyCharm
./gradlew runCLion # CLion (classic engine)
./gradlew runCLion-Nova # CLion (Nova engine)
./gradlew runGoLand
./gradlew runWebStorm
./gradlew runRustRover

# Verify plugin compatibility
./gradlew verifyPlugin

# Run in split mode (backend/frontend separation)
./gradlew runInSplitMode
```

## Platform Version Targeting

The plugin supports multiple IntelliJ Platform versions. The target version is controlled by `environmentName` in `gradle.properties`:

- `environmentName=252` targets 2025.2.x, `environmentName=253` targets 2025.3.x
- Version-specific properties are in `gradle-252.properties`, `gradle-253.properties`, etc.
- Branch-specific source overrides go in `branches/<version>/src/` directories

To switch versions, change `environmentName` and reload Gradle.

## Architecture

### Module Structure

- **`hs-edu-format`**: Standalone library containing course format data models (Course, Lesson, Task, etc.), YAML/JSON serialization, and
API definitions. Has no IntelliJ dependencies.

- **`intellij-plugin/hs-core`**: Core plugin functionality - course management, task checking, UI components, Hyperskill integration

- **`intellij-plugin/hs-jvm-core`**: Shared JVM language support (Gradle, JUnit integration)

- **Language modules** (`hs-Java`, `hs-Kotlin`, `hs-Python`, `hs-Rust`, `hs-Cpp`, `hs-Go`, `hs-Php`, `hs-JavaScript`, `hs-Scala`,
`hs-Shell`, `hs-sql`, `hs-CSharp`): Language-specific configurators and checkers

- **Feature modules** (`hs-features/*`): Optional features like AI debugger, GitHub integration, remote environments

### Source Layout

```
src/ # Main source code
resources/ # Resources
testSrc/ # Test source code
testResources/ # Test resources
branches/252/ # Platform-specific overrides for 2025.2
```

### Key Patterns

- All plugin module classes must be in the `org.hyperskill.academy` package (enforced by `verifyClasses` task)
- Kotlin stdlib is excluded from dependencies (bundled with IDE) — see `kotlin.stdlib.default.dependency=false`
- Tests use IntelliJ's test framework (`LightPlatformCodeInsightTestCase`, etc.)
- Convention plugins in `buildSrc/` provide shared Gradle configuration:
- `common-conventions` — base Java/Kotlin setup (Java 21, Kotlin 2.1)
- `intellij-plugin-common-conventions` — IntelliJ plugin modules with branch-specific sources
- `intellij-plugin-module-conventions` — full IntelliJ Platform module setup with IDE caching

### IntelliJ Platform Plugin

Uses [IntelliJ Platform Gradle Plugin](https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin.html) v2.x. Key
extension points:

- `intellijPlatform.pluginConfiguration` for plugin metadata
- `intellijPlatform.pluginVerification` for compatibility checks
- `intellijPlatform.caching { ides { enabled = true } }` for IDE download caching

## Handling Platform Compatibility

When code differs between platform versions:

1. **Prefer deprecated APIs** if they work across all supported versions. Add `// BACKCOMPAT: <version>` comment to mark
for future cleanup when that version is dropped.

2. **Extract platform-specific code** to `branches/<version>/src/` directories when APIs are incompatible. Keep
platform-specific code minimal.

3. **Use runtime checks** with `ApplicationInfo.getInstance().build` when code must compile on all platforms but behave
differently.

4. **Platform-specific XML**: Create `platform-<name>.xml` in `branches/<version>/resources/META-INF/` and include via
XInclude.

## Configuration Files

- `gradle.properties`: Main build configuration (plugin version, target IDE, feature flags)
- `gradle-252.properties`, `gradle-253.properties`: Platform-specific plugin/IDE versions
- `secret.properties`: OAuth client IDs (not committed, created from template)
- `gradle/libs.versions.toml`: Dependency version catalog

## Code Style

- Only English is allowed in the code (comments, identifiers, strings)

## Debugging

When running the plugin with `./gradlew runIde` (or `runIdea`, `runPyCharm`, etc.), logs are located in the **sandbox directory**, not in the system logs:

```
intellij-plugin/build/idea-sandbox/idea-sandbox-<version>/log_runIdea/idea.log
```

For example:
```
intellij-plugin/build/idea-sandbox/idea-sandbox-253/log_runIdea/idea.log
```

To view logs in real-time:
```bash
tail -f intellij-plugin/build/idea-sandbox/idea-sandbox-*/log_runIdea/idea.log
```

To search for specific log entries:
```bash
grep "YourSearchPattern" intellij-plugin/build/idea-sandbox/idea-sandbox-*/log_runIdea/idea.log
```

## Issue Tracker

Report issues to [YouTrack EDU project](https://youtrack.jetbrains.com/issues/EDU).
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ plugins {

idea {
project {
jdkName = "21"
languageLevel = IdeaLanguageLevel("11")
vcs = "Git"
jdkName = "25"
languageLevel = IdeaLanguageLevel("25")
}
module {
excludeDirs.add(file("dependencies"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import groovy.util.Node
import groovy.xml.XmlParser
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
Expand Down Expand Up @@ -34,6 +36,20 @@ kotlin {
}
}

if (environmentName.toInt() >= 261) {
// For 2026.1+ our Kotlin is compiled with `jvmTarget = 25` (see the KotlinCompile block below),
// so our own Kotlin output is Java 25 bytecode. javac from older JDKs cannot read Java 25 class
// files ("class file has wrong version 69.0, should be 65.0"), which breaks any module whose
// Java sources reference Kotlin-compiled classes (e.g. hs-Python). So the Java toolchain must
// match the Kotlin target and use JDK 25 as well, while keeping the Java 21 language level
// (sourceCompatibility/targetCompatibility) common for all platform versions.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
}

// It's not possible to use version catalogs in convention plugin as usual,
// so we have to get the catalog itself and libraries manually
// See https://docs.gradle.org/current/userguide/version_catalogs.html#sec:buildsrc-version-catalog
Expand All @@ -54,6 +70,11 @@ tasks {
withType<KotlinCompile> {
// Prevents unexpected incremental compilation errors after changing value of `environmentName` property
inputs.property("environmentName", providers.gradleProperty("environmentName"))
if (environmentName.toInt() >= 261) {
// IntelliJ Platform 2026.1+ contains inline Kotlin APIs compiled to Java 25 bytecode.
compilerOptions.jvmTarget = JvmTarget.JVM_25
jvmTargetValidationMode.set(JvmTargetValidationMode.WARNING)
}
}

processResources {
Expand Down
49 changes: 38 additions & 11 deletions buildSrc/src/main/kotlin/intellijUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,18 @@ val Project.rustPlugins: List<String>
)

val Project.cppPlugins: List<String>
get() = listOfNotNull(
baseVersion.toTypeWithVersion()
.version
.takeUnless { /* TODO remove with 252 */ it.startsWith("2025.2") }
?.let { "com.intellij.cmake" },
"com.intellij.cidr.lang",
"com.intellij.clion",
"com.intellij.nativeDebug",
"org.jetbrains.plugins.clion.test.google",
"org.jetbrains.plugins.clion.test.catch"
)
get() {
val clionBranch = platformBranch(clionVersion)
return listOfNotNull(
"com.intellij.cmake".takeUnless { /* TODO remove with 252 */ clionBranch == 252 },
// The classic language engine was removed from the CLion distribution in 2026.2 (Nova engine only)
"com.intellij.cidr.lang".takeIf { clionBranch < 262 },
"com.intellij.clion",
"com.intellij.nativeDebug",
"org.jetbrains.plugins.clion.test.google",
"org.jetbrains.plugins.clion.test.catch"
)
}

val Project.sqlPlugins: List<String>
get() = listOf(
Expand Down Expand Up @@ -166,6 +167,32 @@ fun IntelliJPlatformExtension.PluginVerification.Ides.intellijIde(versionWithCod
}
}

// Branch number of an IDE dependency notation, e.g. "IU-262.8665-EAP-CANDIDATE-SNAPSHOT" -> 262,
// "CL-262-EAP-SNAPSHOT" -> 262, "RD-2026.1.2" -> 261, "IU-2025.3" -> 253
fun platformBranch(versionWithCode: String): Int {
val version = versionWithCode.toTypeWithVersion().version
return if (version.startsWith("20")) {
val parts = version.split('.')
(parts[0].substring(2) + parts[1]).toInt()
}
else {
version.substringBefore('.').substringBefore('-').toInt()
}
}

// Since 2026.2 (262), some parts of the platform were extracted from the core classpath
// into separate bundled plugins/modules and require explicit dependencies to compile:
// - SM test runner -> `intellij.testRunner.plugin` plugin, modules `intellij.platform.smRunner` and `intellij.platform.testRunner`
// - JCEF -> `com.intellij.modules.jcef` plugin, modules `intellij.platform.ui.jcef` and `intellij.libraries.jcef`
// - SQLite -> `intellij.platform.sqlite` platform module
// These module names don't exist in the layout of older IDE versions, so they should be added
// only when the IDE the module is compiled against is new enough.
fun IntelliJPlatformDependenciesExtension.bundledModulesSince(ideVersionWithCode: String, sinceBranch: Int, vararg modules: String) {
if (platformBranch(ideVersionWithCode) >= sinceBranch) {
bundledModules(*modules)
}
}

fun IntelliJPlatformDependenciesExtension.intellijPlugins(vararg notations: String) {
for (notation in notations) {
when {
Expand Down
10 changes: 10 additions & 0 deletions gradle-262.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
customSinceBuild=262.8377
customUntilBuild=262.*

# Existent IDE versions can be found in the following repos:
# https://www.jetbrains.com/intellij-repository/releases/
# https://www.jetbrains.com/intellij-repository/snapshots/
ideaVersion=IU-262.8665-EAP-CANDIDATE-SNAPSHOT
clionVersion=CL-262-EAP-SNAPSHOT
pycharmVersion=PC-262-EAP-SNAPSHOT
riderVersion=RD-2026.1.2
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# supported values: 252, 253, 261
environmentName=261
# supported values: 252, 253, 261, 262
environmentName=262

pluginVersion=2026.14
pluginVersion=2026.15

# type of IDE (IDEA, CLion, etc.) used to build/test running
# for more details see `Different IDEs` section in `PlatformVersions.md`
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
4 changes: 3 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
6 changes: 3 additions & 3 deletions gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading