Skip to content

Commit da9687c

Browse files
committed
feat(intellij): dynamic update of test run buttons on file and configuration changes
1 parent e4aef80 commit da9687c

File tree

11 files changed

+353
-138
lines changed

11 files changed

+353
-138
lines changed

Diff for: intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/RobotCodePostStartupActivity.kt

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
package dev.robotcode.robotcode4ij
22

3+
import com.intellij.openapi.Disposable
34
import com.intellij.openapi.project.Project
45
import com.intellij.openapi.startup.ProjectActivity
56
import com.intellij.openapi.vfs.VirtualFileManager
67
import com.intellij.platform.backend.workspace.workspaceModel
78
import com.intellij.platform.workspace.jps.entities.ModuleEntity
89
import com.intellij.platform.workspace.storage.EntityChange
10+
import dev.robotcode.robotcode4ij.listeners.RobotCodeVirtualFileListener
911
import dev.robotcode.robotcode4ij.lsp.langServerManager
1012
import dev.robotcode.robotcode4ij.testing.testManger
1113
import kotlinx.coroutines.flow.collect
1214
import kotlinx.coroutines.flow.onEach
1315

14-
class RobotCodePostStartupActivity : ProjectActivity {
16+
class RobotCodePostStartupActivity : ProjectActivity, Disposable {
1517
override suspend fun execute(project: Project) {
1618
project.langServerManager.start()
1719
project.testManger.refresh()
1820

19-
project.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, RobotCodeVirtualFileListener(project))
21+
VirtualFileManager.getInstance().addAsyncFileListener(RobotCodeVirtualFileListener(project), this)
22+
2023
project.workspaceModel.eventLog.onEach {
2124
val moduleChanges = it.getChanges(ModuleEntity::class.java)
2225
if (moduleChanges.filterIsInstance<EntityChange.Replaced<ModuleEntity>>().isNotEmpty()) {
@@ -27,5 +30,9 @@ class RobotCodePostStartupActivity : ProjectActivity {
2730
}.collect()
2831
}
2932

33+
override fun dispose() {
34+
// do nothing
35+
}
36+
3037
}
3138

Diff for: intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/RobotCodeVirtualFileListener.kt

-18
This file was deleted.

Diff for: intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/debugging/RobotCodeDebugProcess.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@ class RobotCodeDebugProcess(
5555

5656
init {
5757
session.setPauseActionSupported(true)
58-
state.afterInitialize.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) {
59-
runBlocking { sendBreakpointRequest() }
60-
}
58+
59+
state.afterInitialize.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) { sendBreakpointRequest() }
6160
debugClient.onStopped.adviseSuspend(Lifetime.Eternal, Dispatchers.IO, this::handleOnStopped)
6261
}
6362

Diff for: intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/debugging/RobotCodeDebugProtocolClient.kt

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dev.robotcode.robotcode4ij.debugging
22

33
import com.intellij.openapi.diagnostic.thisLogger
44
import com.jetbrains.rd.util.reactive.Signal
5+
import org.eclipse.lsp4j.debug.ExitedEventArguments
56
import org.eclipse.lsp4j.debug.OutputEventArguments
67
import org.eclipse.lsp4j.debug.StoppedEventArguments
78
import org.eclipse.lsp4j.debug.TerminatedEventArguments
@@ -143,6 +144,7 @@ data class RobotLogMessageEventArguments(
143144
@Suppress("unused") class RobotCodeDebugProtocolClient : IDebugProtocolClient {
144145
var onStopped = Signal<StoppedEventArguments>()
145146
val onTerminated = Signal<TerminatedEventArguments?>()
147+
val onExited = Signal<ExitedEventArguments?>()
146148

147149
val onRobotEnqueued = Signal<RobotEnqueuedArguments>()
148150
val onRobotStarted = Signal<RobotExecutionEventArguments>()
@@ -153,6 +155,11 @@ data class RobotLogMessageEventArguments(
153155
val onRobotMessage = Signal<RobotLogMessageEventArguments>()
154156
val onOutput = Signal<OutputEventArguments>()
155157

158+
override fun exited(args: ExitedEventArguments?) {
159+
super.exited(args)
160+
onExited.fire(args)
161+
}
162+
156163
override fun terminated(args: TerminatedEventArguments?) {
157164
super.terminated(args)
158165
onTerminated.fire(args)

Diff for: intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/execution/RobotCodeRunLineMarkerContributor.kt

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import dev.robotcode.robotcode4ij.testing.testManger
99
class RobotCodeRunLineMarkerContributor : RunLineMarkerContributor() {
1010
override fun getInfo(element: PsiElement): Info? {
1111
var testElement = element.project.testManger.findTestItem(element) ?: return null
12+
if (testElement.type != "test" && testElement.children.isNullOrEmpty()) {
13+
return null
14+
}
1215

1316
val uri = newUrl(
1417
"robotcode", "/", newLocalFileUrl(testElement.source!!).toString()

Diff for: intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/highlighting/Colors.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ object Colors {
2828
createTextAttributesKey("ROBOTFRAMEWORK_VARIABLE", DefaultLanguageHighlighterColors.GLOBAL_VARIABLE)
2929
val VARIABLE_EXPRESSION: TextAttributesKey =
3030
createTextAttributesKey("ROBOTFRAMEWORK_VARIABLE_EXPRESSION", DefaultLanguageHighlighterColors.GLOBAL_VARIABLE)
31+
3132
val VARIABLE_BEGIN: TextAttributesKey =
3233
createTextAttributesKey("ROBOTFRAMEWORK_VARIABLE_BEGIN", DefaultLanguageHighlighterColors.BRACES)
3334
val VARIABLE_END: TextAttributesKey =
34-
createTextAttributesKey("ROBOTFRAMEWORK_VARIABLE_BEGIN", DefaultLanguageHighlighterColors.BRACES)
35+
createTextAttributesKey("ROBOTFRAMEWORK_VARIABLE_END", DefaultLanguageHighlighterColors.BRACES)
3536

3637
val NAMESPACE: TextAttributesKey =
3738
createTextAttributesKey("ROBOTFRAMEWORK_NAMESPACE", DefaultLanguageHighlighterColors.CLASS_REFERENCE)

Diff for: intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/highlighting/RobotCodeLexer.kt

+1-4
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,7 @@ class RobotCodeLexer : LexerBase() {
7878

7979

8080
private val myLexer =
81-
TextMateLexer(
82-
TextMateBundleHolder.descriptor, Registry.get("textmate.line.highlighting.limit").asInteger(),
83-
true
84-
)
81+
TextMateLexer(TextMateBundleHolder.descriptor, Registry.get("textmate.line.highlighting.limit").asInteger())
8582
private var currentLineTokens = LinkedList<TextMateLexer.Token?>()
8683
private lateinit var buffer: CharSequence
8784
private var endOffset = 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package dev.robotcode.robotcode4ij.listeners
2+
3+
import com.intellij.openapi.project.Project
4+
import com.intellij.openapi.vfs.AsyncFileListener
5+
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
6+
import dev.robotcode.robotcode4ij.lsp.langServerManager
7+
import dev.robotcode.robotcode4ij.testing.testManger
8+
9+
class RobotCodeVirtualFileListener(private val project: Project) : AsyncFileListener {
10+
companion object {
11+
val PROJECT_FILES = arrayOf("robot.toml", ".robot.toml", "pyproject.toml")
12+
}
13+
14+
override fun prepareChange(events: List<VFileEvent>): AsyncFileListener.ChangeApplier? {
15+
return object : AsyncFileListener.ChangeApplier {
16+
override fun afterVfsChange() {
17+
if (events.any { it.file?.name in PROJECT_FILES }) {
18+
project.langServerManager.restart()
19+
project.testManger.refreshDebounced()
20+
}
21+
}
22+
}
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package dev.robotcode.robotcode4ij.testing
2+
3+
import kotlinx.serialization.Serializable
4+
import kotlinx.serialization.json.JsonElement
5+
6+
@Serializable
7+
data class Position(val line: UInt, val character: UInt)
8+
9+
@Serializable data class Range(val start: Position, val end: Position)
10+
11+
@Serializable data class RobotCodeTestItem(
12+
val type: String,
13+
val id: String,
14+
val name: String,
15+
val longname: String,
16+
val lineno: Int? = null,
17+
val description: String? = null,
18+
val uri: String? = null,
19+
val relSource: String? = null,
20+
val source: String? = null,
21+
val needsParseInclude: Boolean? = null,
22+
var children: Array<RobotCodeTestItem>? = null,
23+
val range: Range? = null,
24+
val error: String? = null,
25+
val tags: Array<String>? = null,
26+
val rpa: Boolean? = null
27+
) {
28+
override fun equals(other: Any?): Boolean {
29+
if (this === other) return true
30+
if (javaClass != other?.javaClass) return false
31+
32+
other as RobotCodeTestItem
33+
34+
if (needsParseInclude != other.needsParseInclude) return false
35+
if (type != other.type) return false
36+
if (id != other.id) return false
37+
if (name != other.name) return false
38+
if (longname != other.longname) return false
39+
if (description != other.description) return false
40+
if (uri != other.uri) return false
41+
if (relSource != other.relSource) return false
42+
if (children != null) {
43+
if (other.children == null) return false
44+
if (!children.contentEquals(other.children)) return false
45+
} else if (other.children != null) return false
46+
if (range != other.range) return false
47+
if (error != other.error) return false
48+
if (tags != null) {
49+
if (other.tags == null) return false
50+
if (!tags.contentEquals(other.tags)) return false
51+
} else if (other.tags != null) return false
52+
53+
return true
54+
}
55+
56+
override fun hashCode(): Int {
57+
var result = needsParseInclude?.hashCode() ?: 0
58+
result = 31 * result + type.hashCode()
59+
result = 31 * result + id.hashCode()
60+
result = 31 * result + name.hashCode()
61+
result = 31 * result + longname.hashCode()
62+
result = 31 * result + description.hashCode()
63+
result = 31 * result + (uri?.hashCode() ?: 0)
64+
result = 31 * result + (relSource?.hashCode() ?: 0)
65+
result = 31 * result + (children?.contentHashCode() ?: 0)
66+
result = 31 * result + (range?.hashCode() ?: 0)
67+
result = 31 * result + (error?.hashCode() ?: 0)
68+
result = 31 * result + (tags?.contentHashCode() ?: 0)
69+
return result
70+
}
71+
}
72+
73+
@Serializable data class RobotCodeDiscoverResult(
74+
val items: Array<RobotCodeTestItem>? = null,
75+
val diagnostics: Map<String, JsonElement>? = null // TODO val diagnostics: { [Key: string]: Diagnostic[] };
76+
) {
77+
override fun equals(other: Any?): Boolean {
78+
if (this === other) return true
79+
if (javaClass != other?.javaClass) return false
80+
81+
other as RobotCodeDiscoverResult
82+
83+
if (items != null) {
84+
if (other.items == null) return false
85+
if (!items.contentEquals(other.items)) return false
86+
} else if (other.items != null) return false
87+
if (diagnostics != other.diagnostics) return false
88+
89+
return true
90+
}
91+
92+
override fun hashCode(): Int {
93+
var result = items?.contentHashCode() ?: 0
94+
result = 31 * result + (diagnostics?.hashCode() ?: 0)
95+
return result
96+
}
97+
}

0 commit comments

Comments
 (0)