Skip to content

Commit 2ebd962

Browse files
ARostovskymeanmail
andauthored
AI debugger (#3)
* Set `duplicatesStrategy` to `EXCLUDE` for resource processing tasks Fixes "Entry courses/Introduction to Python.zip is a duplicate but no duplicate handling strategy has been set" error * Downgrade IntelliJ Platform Plugin to version 2.5.0 for compatibility * Add `ai-debugger` modules * Implement service host management mechanism Introduce a new framework for managing service hosts, including: - `ServiceHostManager` for host state management. - `ChangeServiceHostDialog` for user interaction to modify hosts. - `ServiceHostEnum` interface for defining host behaviors. - `ChangeServiceHostAction` to trigger host change logic. * Add AI Debugger service host management classes Introduce `EduAIDebuggerServiceHost` and `EduAIDebuggerServiceChangeHost` to manage AI Debugger service hosts, including production, staging, and custom environments. Add resource bundle for localization support. * Introduce HintInlineBanner and LikeBlock components for UI feedback Add `HintInlineBanner` UI component for displaying inline banners with customizable actions and AI-generated content tooltips. Add `LikeBlock` for capturing user feedback with like/dislike options. Include new icons, colors, and properties for visual consistency. * Introduce AI Debugger core module with logging, UI components, and breakpoint management * Introduce AI Debugger JVM module with breakpoint types, handler factory, and icons Add support for JVM-specific breakpoints in the AI Debugger: - Implement `JvmAIDebuggerBreakpointType` and `JvmBreakpointTypeManager` for managing JVM line breakpoints. - Add `JvmAIDebuggerBreakpointHandlerFactory` for handling breakpoint integration with the debug process. - Add `JvmAIDebuggerLineBreakpoint` with custom verified icon logic. - Include `bug.svg` icon and associated resource loading via `AIDebuggerIcons`. * Introduce AI Debugger Kotlin module - Add Kotlin-specific breakpoint management with `KtIntermediateBreakpointProcessor` and `ai-debugger-kotlin.xml` integration. - Implement `KtTestFinder` for locating Kotlin test methods. - Extend Kotlin slice analysis with `CodeDependencyAnalyzerTest`, `FunctionControlDependencyTest`, and `FunctionDataDependencyTest`. - Provide tests for control and data dependencies, intermediate breakpoints, and slicing logic. - Update plugin resources to include Kotlin-specific components. * Comment out the `deleteTests` method and its calls in AI Debugger modules * Remove unused `createTests` method and related logic in AI Debugger modules * Restrict test creation to study courses excluding Hyperskill courses in `CheckAction`. * Introduce AI Debugger Java module - Add Java-specific `JTestFinder` for locating test methods. - Extend test language support with `getHyperskillTestLanguage` in `AIDebugUtils`. - Update resource files and plugin configurations to include the new Java module. * Refactor course-related logic in DebuggerHintRequest - Replace `courseId` with `CourseInfo` in `DebuggerHintRequest`. - Add new `CourseInfo` class with course metadata support. - Bump `educational-ml-library` version to `1.0.65`. * Remove AI Debugger feedback-related components and code - Deleted `LikeBlock`, `AIDebugContext`, and associated feedback classes. - Removed feedback dialogs, inline banners, and related UI components. - Cleaned up resource bundle entries, unused icons, and colors. - Simplified `HintInlineBanner` by removing feedback actions. - Updated AI Debugger modules to exclude feedback logic. --------- Co-authored-by: Alexander Petrov <meanmail@meanmail.dev>
1 parent d6750f6 commit 2ebd962

File tree

79 files changed

+3502
-10
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+3502
-10
lines changed

buildSrc/src/main/kotlin/intellij-plugin-common-conventions.gradle.kts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,6 @@ tasks {
7272
inputs.property("environmentName", providers.gradleProperty("environmentName"))
7373
}
7474

75-
// Enforce duplicate handling for all resource and copy-like tasks
76-
withType<ProcessResources> {
77-
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
78-
}
79-
withType<Copy> {
80-
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
81-
}
82-
83-
// Keep explicit configuration for the standard tasks as well
8475
processResources {
8576
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
8677
}

gradle/libs.versions.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[versions]
2+
educational-ml-library = "1.0.65"
23
jackson = "2.17.2"
34
kotlin = "2.1.20"
45
okhttp = "4.12.0"
@@ -8,6 +9,8 @@ retrofit = "2.9.0"
89
annotations = { group = "org.jetbrains", name = "annotations", version = "23.0.0" }
910
clikt-core = { module = "com.github.ajalt.clikt:clikt-core", version = "5.0.1" }
1011
converter-jackson = { group = "com.squareup.retrofit2", name = "converter-jackson", version.ref = "retrofit" }
12+
educational-ml-library-core = { group = "com.jetbrains.educational.ml", name = "educational-ml-library-core", version.ref = "educational-ml-library" }
13+
educational-ml-library-debugger = { group = "com.jetbrains.educational.ml", name="educational-ml-library-debugger", version.ref = "educational-ml-library" }
1114
jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name = "jackson-dataformat-yaml", version.ref = "jackson" }
1215
jackson-datatype-jsr310 = { group = "com.fasterxml.jackson.datatype", name = "jackson-datatype-jsr310", version.ref = "jackson" }
1316
jackson-module-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin", version.ref = "jackson" }

intellij-plugin/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ dependencies {
6969
pluginModule(implementation(project("hs-CSharp")))
7070
pluginModule(implementation(project("hs-sql")))
7171
pluginModule(implementation(project("hs-sql:hs-sql-jvm")))
72+
pluginModule(implementation(project("hs-features:ai-debugger-core")))
73+
pluginModule(implementation(project("hs-features:ai-debugger-java")))
74+
pluginModule(implementation(project("hs-features:ai-debugger-jvm")))
75+
pluginModule(implementation(project("hs-features:ai-debugger-kotlin")))
7276
pluginModule(implementation(project("hs-features:hs-github")))
7377
if (!isAtLeast252) { // BACKCOMPAT: Temporarily exclude for 2025.2 as it doesn't compile
7478
pluginModule(implementation(project("hs-features:hs-remote-env")))
Lines changed: 3 additions & 0 deletions
Loading

intellij-plugin/hs-core/resources/messages/EduCoreBundle.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,13 @@ action.toggle.rest.services.title=Toggle Educational Rest Services
8686
browse.courses=Browse Courses
8787
browse.courses.description=Browse list of available Hyperskill courses
8888

89+
button.select=Select
90+
8991
button.next.task.text=Next: {0}
9092
copy.path.to.clipboard=Copy path to clipboard
9193

94+
change.service.host.label=Choose server:
95+
change.service.host.specify.url.label=Specify full URL:
9296
# ex. The course title is 5 characters longer than the allowed maximum (64)
9397
check.correct=Correct
9498
check.details.feedback.title=Feedback
@@ -262,6 +266,7 @@ group.HyperskillEducational.LearnAndTeachFileMenu.text=Hyperskill Academy
262266
# It's not possible to use tags in courses list dialog, so we have to split the phrase. Do not use pieces separately!
263267
help.use.guide1=For more information, see
264268
help.use.guide2=the Troubleshooting guide
269+
hints.label.ai.generated.content.tooltip=This is AI-generated content
265270
hyperskill.accounts.are.different=Account in IDE: <strong>{0}</strong>. <br>\
266271
Account in browser: <strong>{1}</strong>.
267272
hyperskill.accounts.are.different.continue=Continue as {0}

intellij-plugin/hs-core/src/org/hyperskill/academy/EducationalCoreIcons.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public static final class Actions {
5656
public static final Icon CommentTask = load("/icons/org/hyperskill/academy/learning/commentTask.svg");
5757
public static final Icon EduCourse = load("/icons/org/hyperskill/academy/eduCourseAction.svg");
5858
public static final Icon IgnoreSyncFile = load("/icons/org/hyperskill/academy/actions/syncFilesIgnore.svg");
59+
public static final Icon Hint = load("/icons/org/hyperskill/academy/actions/hint.svg");
5960
public static final Icon RateCourse = load("/icons/org/hyperskill/academy/learning/rateCourse.svg");
6061
public static final Icon ResetTask = load("/icons/org/hyperskill/academy/learning/resetTask.svg");
6162
public static final Icon SyncChanges = load("/icons/org/hyperskill/academy/actions/syncFiles.svg");

intellij-plugin/hs-core/src/org/hyperskill/academy/learning/actions/CheckAction.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import org.hyperskill.academy.learning.courseFormat.ext.configurator
4040
import org.hyperskill.academy.learning.courseFormat.ext.getDir
4141
import org.hyperskill.academy.learning.courseFormat.ext.getDocument
4242
import org.hyperskill.academy.learning.courseFormat.ext.getVirtualFile
43+
import org.hyperskill.academy.learning.courseFormat.hyperskill.HyperskillCourse
4344
import org.hyperskill.academy.learning.courseFormat.tasks.EduTask
4445
import org.hyperskill.academy.learning.courseFormat.tasks.OutputTask
4546
import org.hyperskill.academy.learning.courseFormat.tasks.Task
@@ -152,7 +153,7 @@ class CheckAction() : ActionWithProgressIcon(), DumbAware {
152153
if (checker == null) return CheckResult.NO_LOCAL_CHECK
153154
task.getDir(project.courseDir) ?: return CheckResult.NO_LOCAL_CHECK
154155

155-
if (task.course.isStudy) {
156+
if (task.course.isStudy && task.course !is HyperskillCourse) {
156157
createTests(invisibleTestFiles)
157158
}
158159
return checker.check(indicator)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.hyperskill.academy.learning.actions.changeHost
2+
3+
import com.intellij.openapi.actionSystem.ActionUpdateThread
4+
import com.intellij.openapi.actionSystem.AnActionEvent
5+
import com.intellij.openapi.diagnostic.thisLogger
6+
import com.intellij.openapi.project.DumbAwareAction
7+
8+
abstract class ChangeServiceHostAction<E>(private val serviceHostManager: ServiceHostManager<E>) : DumbAwareAction()
9+
where E : Enum<E>,
10+
E : ServiceHostEnum {
11+
12+
override fun actionPerformed(e: AnActionEvent) {
13+
val selectedHost = ChangeServiceHostDialog(serviceHostManager, e.presentation.text).showAndGetSelectedHost() ?: return
14+
serviceHostManager.selectedHost = selectedHost
15+
thisLogger().info("Host for ${serviceHostManager.name} was changed to ${selectedHost.url}")
16+
}
17+
18+
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
19+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.hyperskill.academy.learning.actions.changeHost
2+
3+
import com.intellij.openapi.ui.DialogWrapper
4+
import com.intellij.openapi.util.NlsContexts
5+
import com.intellij.ui.EnumComboBoxModel
6+
import com.intellij.ui.SimpleListCellRenderer
7+
import com.intellij.ui.dsl.builder.AlignX
8+
import com.intellij.ui.dsl.builder.bindItem
9+
import com.intellij.ui.dsl.builder.bindText
10+
import com.intellij.ui.dsl.builder.panel
11+
import com.intellij.ui.layout.*
12+
import com.intellij.util.ui.JBUI
13+
import org.hyperskill.academy.learning.messages.EduCoreBundle
14+
import javax.swing.JComboBox
15+
import javax.swing.JComponent
16+
17+
class ChangeServiceHostDialog<E>(
18+
private val serviceHostManager: ServiceHostManager<E>,
19+
dialogTitle: @NlsContexts.DialogTitle String
20+
) : DialogWrapper(true) where E : Enum<E>, E : ServiceHostEnum {
21+
22+
private var selectedHost: E?
23+
private var serverUrl: String
24+
25+
init {
26+
val (host, url) = serviceHostManager.selectedHost
27+
selectedHost = host
28+
serverUrl = if (host == serviceHostManager.other) url else serviceHostManager.other.url
29+
title = dialogTitle
30+
setOKButtonText(EduCoreBundle.message("button.select"))
31+
isResizable = true
32+
init()
33+
}
34+
35+
override fun createCenterPanel(): JComponent {
36+
lateinit var comboBox: JComboBox<E>
37+
return panel {
38+
row(EduCoreBundle.message("change.service.host.label")) {
39+
comboBox = comboBox(EnumComboBoxModel(serviceHostManager.hostEnumClass), SimpleListCellRenderer.create("") { it.visibleName() })
40+
.bindItem(::selectedHost)
41+
.focused()
42+
.align(AlignX.FILL)
43+
.component
44+
}
45+
row(EduCoreBundle.message("change.service.host.specify.url.label")) {
46+
textField()
47+
.bindText(::serverUrl)
48+
.align(AlignX.FILL)
49+
}.visibleIf(comboBox.selectedValueMatches { it == serviceHostManager.other })
50+
}.apply {
51+
preferredSize = JBUI.size(WIDTH, HEIGHT)
52+
minimumSize = JBUI.size(WIDTH, HEIGHT)
53+
}
54+
}
55+
56+
fun showAndGetSelectedHost(): ServiceHostManager.SelectedServiceHost<E>? {
57+
if (!showAndGet()) return null
58+
val host = selectedHost ?: return null
59+
return ServiceHostManager.SelectedServiceHost(host, serverUrl)
60+
}
61+
62+
companion object {
63+
private const val WIDTH: Int = 350
64+
private const val HEIGHT: Int = 75
65+
}
66+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.hyperskill.academy.learning.actions.changeHost
2+
3+
import com.intellij.openapi.util.NlsContexts.ListItem
4+
5+
interface ServiceHostEnum {
6+
val url: String
7+
fun visibleName(): @ListItem String
8+
}

0 commit comments

Comments
 (0)