Skip to content

Commit b76ded2

Browse files
committed
docs(openspec): update the intellij-discovery-refresh plan to the current plugin
The plan now builds on the archived settings, profiles, environment-check, project-status and task-marker changes: both capabilities are modified ones, the restart decision runs only with a usable interpreter and RobotCode switched on, and Disable extension keeps its own handling.
1 parent 9961025 commit b76ded2

4 files changed

Lines changed: 27 additions & 23 deletions

File tree

‎openspec/changes/intellij-discovery-refresh/design.md‎

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@
55
See proposal.md for the motivation. The state that shapes the approach, checked against the code on main:
66

77
- **Discovery** lives in `testing/RobotCodeTestManager`:
8-
- It listens to document changes of suite files in its project, to VFS events through an application-wide `AsyncFileListener` without a project filter that reacts only to content events of suite files, and to opening and closing editors.
9-
- Opening or closing a suite file starts a per-file discovery. Folder events are ignored. After a file is deleted, its per-file discovery gets `{"items": []}` with exit code 0, so the empty suite stays in the model.
8+
- It listens to document changes of suite files in its project, to VFS events through an application-wide `AsyncFileListener` without a project filter, and to opening and closing editors. Every VFS event of a suite file requests a per-file discovery when the model knows the file and a full discovery otherwise, so a suite file of another open project starts a full discovery here.
9+
- Opening or closing a suite file starts a per-file discovery. Folder events are ignored, so the suites of a deleted folder stay in the model. Since the task-marker change, the per-file discovery runs `discover all` with `-I` and `--suite` and falls back to a full discovery when the result has no suite with children for the file, so a deleted file's suite disappears.
1010
- Discoveries run on an own `CoroutineScope(Dispatchers.IO.limitedParallelism(1))` that is never cancelled, and wait in `executeOnPooledThread { … }.get()`. Cancelling a job does not stop the running `robotcode` process.
11-
- A failure inside the pooled callable is logged by the platform as SEVERE and returns `null` (analysis notes, `test-discovery-errors`). The full discovery then sets the model to empty; the per-file discovery sets the suite's children to empty.
11+
- A failure inside the pooled callable is logged by the platform as SEVERE and returns `null` (analysis notes, `test-discovery-errors`). The full discovery then sets the model to empty; the per-file discovery leaves it unchanged.
12+
- Both discoveries return at once while the project's interpreter is not usable or "Disable extension" is checked; a result that is not usable empties the model (`clearTestItems()`).
1213
- `testItems` is a plain property written from the IO thread and read by run markers and producers; the per-file discovery assigns `children` of a suite in place.
1314
- The JSON is decoded with the default, strict `Json`.
1415
- **What discovery reports about broken files,** checked with Robot Framework 6.0, 7.0 and 7.5: robotcode's `discover all` leaves a file that Robot Framework cannot build into a suite out of the item tree, for example a file that is not valid UTF-8 or that has both tests and tasks. It reports the problem in `diagnostics`, keyed by the file's URI, with a line and a message. Files with tolerable problems, such as `Test Template` set twice, keep their items and get a diagnostic as well. The plugin decodes `diagnostics` as untyped JSON and ignores it; `RobotCodeTestItem.error` is decoded but unused, and it was not set in these cases.
1516
- **Configuration files:** `listeners/RobotCodeVirtualFileListener` reacts to `robot.toml`, `.robot.toml`, `pyproject.toml` and `robocop.toml` by name, from any project, and calls `restartAll()`. The startup activity registers it application-wide for each project.
16-
- **Restarts:** `restartAll()` in `RobotCodeHelpers.kt` debounces for 500 ms, then restarts the language server and refreshes discovery.
17-
- **Planned changes this builds on, none of them implemented yet:**
18-
- `intellij-profiles`, which this change needs: the personal profile selection, `-p` on every process through a pure argument function, and `restartAll()` after a change.
19-
- The settings pages and their mapper (`intellij-settings-pages`), the Analysis, Diagnostics and Robocop pages with one debounced restart per Apply and initialization options (`intellij-analysis-settings`), and the extra arguments with their personal state and page-specific Apply rules (`intellij-extra-args`).
20-
- `intellij-environment-check`, which restarts on interpreter changes by itself.
21-
- `intellij-rpa-task-markers`, which changes the arguments of the per-file discovery and falls back to a full discovery when a file yields no suite.
17+
- **Restarts:** `restartAll(reset)` in `RobotCodeHelpers.kt` returns for the default project and while "Disable extension" is checked. It debounces for 500 ms, waits for a running environment check (with `reset`, it checks again and also undoes a disable from the Language Servers tool window), sets `restartPending` so that the environment listener does not start the server a second time, then restarts the language server and requests a full discovery. The Editing, Analysis and Robocop pages call it on every Apply, the Language Server page when its extra arguments changed, the General page and Select Configuration Profiles when the profile selection changed; the General page requests a discovery directly when only the robotcode extra arguments changed.
18+
- **Archived changes this builds on:**
19+
- `intellij-profiles`: the personal profile selection and `-p` on every process through `robotCodeArguments`.
20+
- `intellij-settings-pages`, `intellij-analysis-settings` and `intellij-extra-args`: the pages, the settings mapper and initialization options, and the personal extra arguments.
21+
- `intellij-environment-check`, which restarts on interpreter changes by itself and starts the server and discovery when the interpreter becomes usable.
22+
- `intellij-project-status`: "Disable extension", which stops and starts the server and discovery directly and makes discovery and `restartAll()` do nothing while it is checked.
23+
- `intellij-rpa-task-markers`: the arguments of the per-file discovery (`fileDiscoveryArguments`) and its fallback to a full discovery (`findSuiteChildren`).
2224

2325
## Goals / Non-Goals
2426

@@ -45,13 +47,15 @@ The restart manager keeps two snapshots:
4547

4648
On a settings event it waits for the 500-millisecond debounce, builds both snapshots from the current state, and compares them. It restarts the server if the server's snapshot differs, and runs a full discovery if the discovery snapshot differs. The comparison is a pure function of the two old and the two new snapshots, which unit tests cover. A new snapshot is taken whenever the server starts and whenever a full discovery runs.
4749

50+
Snapshots exist only while the interpreter is usable and RobotCode is switched on, because the command line cannot be built otherwise; without them the decision does nothing, and the environment check or the switch start the server and discovery later. The restart and the discovery go through the existing restart path, with its waiting for a running check and its `restartPending`, split into the server alone, discovery alone, or both.
51+
4852
Alternatives:
4953
- VS Code's list of setting sections that restart (`index.ts:174-205`): it needs care for every new setting, and it misses inputs such as the interpreter or the profiles on the command line.
50-
- The page-specific rules of the planned pages: each page decides on its own, and the rules drift apart.
54+
- The page-specific rules of today's pages: each page decides on its own, and the rules drift apart.
5155

5256
### One settings topic, fed by pages and stored state
5357

54-
A project topic announces that RobotCode settings changed. Every RobotCode settings page publishes it after `apply()`, and every RobotCode state component publishes it in `loadState()`. The platform calls `loadState()` again when the storage file changes outside the IDE, for example through a VCS update; this is documented platform behaviour and is checked in the harness. The direct restart and discovery calls that the planned pages and the Select Configuration Profiles action make are replaced by publishing. Events for the default project are ignored, as the planned pages already do for it.
58+
A project topic announces that RobotCode settings changed. Every RobotCode settings page publishes it after `apply()`, and every RobotCode state component publishes it in `loadState()`. The platform calls `loadState()` again when the storage file changes outside the IDE, for example through a VCS update; this is documented platform behaviour and is checked in the harness. The direct restart and discovery calls that the pages and the Select Configuration Profiles action make today are replaced by publishing. Events for the default project are ignored, as `restartAll()` already does for it. "Disable extension" keeps its own handling, because it stops and starts the server instead of restarting it.
5559

5660
Alternative: publishing only from the pages misses external edits of `.idea/robotcodeSettings.xml`.
5761

@@ -66,7 +70,7 @@ Alternative: publishing only from the pages misses external edits of `.idea/robo
6670
- A per-file discovery runs without progress.
6771
- A cancelled discovery destroys its `robotcode` process.
6872

69-
The model is an immutable tree in a `@Volatile` property. A full discovery replaces it. A per-file discovery builds a copy of the path from the root to the changed suite and swaps the root. A failed or cancelled discovery leaves the property as it is. Decoding uses `Json { ignoreUnknownKeys = true }`, and the existing test that documents the strict decoding changes accordingly.
73+
The model is an immutable tree in a `@Volatile` property. A full discovery replaces it. A per-file discovery keeps the arguments and the fallback of the task-marker change, builds a copy of the path from the root to the changed suite and swaps the root. A failed or cancelled discovery leaves the property as it is. Decoding uses `Json { ignoreUnknownKeys = true }`, and the existing test that documents the strict decoding changes accordingly.
7074

7175
Alternative: keeping `executeOnPooledThread { }.get()`, which can neither be cancelled nor fail without a SEVERE entry.
7276

@@ -78,7 +82,7 @@ Alternative: keeping `executeOnPooledThread { }.get()`, which can neither be can
7882
- Settings events go through the comparison above.
7983
- Tools | RobotCode | Refresh Robot Framework Tests requests a full discovery. It is enabled only with a project.
8084

81-
The planned task-marker change states the per-file rediscovery as happening when a suite file is "opened, closed or edited". This change drops opening and closing as triggers, so when both are archived, that requirement's list of triggers needs the same edit.
85+
The archived requirement of the task-marker change names an edit as its example trigger and does not list opening or closing, so it stays as it is.
8286

8387
### Failures: keep the model, one notification
8488

@@ -104,8 +108,7 @@ Alternative: a run marker without actions for such files; whether the run gutter
104108
- [The comparison restarts the server for settings it could also take without a restart] → VS Code restarts for them too, and the analysis found that many values are read only once per server process.
105109
- [The platform's `loadState()` call on external changes is an assumption] → The harness changes the stored settings file outside the IDE and checks the restart.
106110
- [A visible progress for every full discovery] → Full discoveries run only on real changes, and per-file discoveries stay silent.
107-
- [Conflict with the planned single-file requirement, which names opening and closing as triggers] → Noted above; the requirement's trigger list changes when both are archived.
108-
- [The per-file arguments change in the task-marker change] → This change wraps the per-file discovery without depending on its arguments.
111+
- [The per-file discovery of the task-marker change assigns a suite's children in place] → This change keeps its arguments and its fallback and only replaces how the result reaches the model.
109112

110113
## Migration Plan
111114

‎openspec/changes/intellij-discovery-refresh/proposal.md‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,30 @@ When discovery fails, for example because robot.toml has a syntax error, every r
1717
- one discovery runs at a time, a newer one cancels it and ends its process, and the run markers always show one complete result;
1818
- output fields the plugin does not know are ignored.
1919
- Tools | RobotCode | Refresh Robot Framework Tests runs a full discovery.
20+
- While "Disable extension" is checked or the project's interpreter is not usable, these triggers do nothing; the switch and the environment check start the language server and discovery themselves.
2021
- When discovery fails, the run markers of the last successful discovery stay. One RobotCode notification shows the first lines of robotcode's error output, with "Retry" and, when the project has one, "Open robot.toml". The same failure does not add another notification, a successful discovery removes it, and no IDE error is raised. idea.log gets the command line and the full output.
2122
- Problems that discovery reports for a suite file show in the tooltip of its run marker. A file that Robot Framework cannot turn into a suite, for example because it is not valid UTF-8 or mixes tests and tasks, gets a marker whose tooltip says why.
2223

2324
Behaviour that users notice, for the release notes (not breaking): a discovery failure no longer removes the run markers; settings that only the language server uses no longer cause a discovery, and settings that only discovery uses no longer restart the server.
2425

25-
Not part of this change: restarts on interpreter changes; the handling of task suites in the discovery of a single file; a switch that turns discovery off, a view of all discovered tests, and editor diagnostics from discovery.
26+
Not part of this change: restarts on interpreter changes; the arguments of the discovery of a single file; a switch that turns discovery off, a view of all discovered tests, and editor diagnostics from discovery.
2627

2728
## Capabilities
2829

2930
### New Capabilities
3031

31-
- `intellij-test-discovery`: when discovery runs again, how it stays consistent, and how failures and unreadable suite files are shown.
32-
- `intellij-language-server`: when the language server restarts.
32+
_None._
3333

3434
### Modified Capabilities
3535

36-
_None._
36+
- `intellij-test-discovery`: when discovery runs again, how it stays consistent, and how failures and unreadable suite files are shown.
37+
- `intellij-language-server`: when the language server restarts.
3738

3839
## Impact
3940

4041
- `intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/testing/RobotCodeTestManager.kt` and `DataItems.kt`: triggers, cancellation, the immutable model, tolerant decoding, typed discovery diagnostics, failure handling.
4142
- `intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/RobotCodeHelpers.kt` (restart manager), `listeners/RobotCodeVirtualFileListener.kt` and `RobotCodePostStartupActivity.kt`: the restart decision, the settings topic, the project filter.
42-
- The RobotCode settings pages and state components in `configuration/`: publishing instead of restarting.
43+
- The RobotCode settings pages and state components in `configuration/`, and `actions/RobotCodeSelectConfigurationProfilesAction.kt`: publishing instead of restarting.
4344
- `execution/RobotCodeRunLineMarkerContributor.kt` and a line marker for files without a suite.
4445
- `intellij-client/src/main/resources/META-INF/plugin.xml`: the RobotCode notification group and the refresh action; `messages/RobotCode.properties`: their texts.
4546
- New unit tests under `intellij-client/src/test/kotlin/`.

‎openspec/changes/intellij-discovery-refresh/specs/intellij-test-discovery/spec.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ The plugin SHALL run a full discovery of a project when:
2828
#### Scenario: Deleted suite folder
2929

3030
- **WHEN** the user deletes a folder with suite files
31-
- **THEN** a full discovery runs, and run markers, context runs and the checks of run configurations no longer know the suites of that folder
31+
- **THEN** a full discovery runs, and run markers and context runs no longer know the suites of that folder
3232

3333
#### Scenario: Refresh action
3434

‎openspec/changes/intellij-discovery-refresh/tasks.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
## 1. Restart decision and settings topic
44

55
- [ ] 1.1 Add the snapshots (language server: command line with environment and working directory, settings tree, initialization options; full discovery: command line with environment and working directory) and the pure decision function from old and new snapshots to "restart the server" and "run discovery". Verify with JUnit 4 tests: a changed header style in the settings tree restarts only the server; changed robotcode extra arguments run only discovery; a changed profile does both; identical snapshots do nothing.
6-
- [ ] 1.2 Add the project topic for RobotCode settings changes; let every RobotCode settings page publish it after `apply()` and every RobotCode state component in `loadState()`, and replace the direct restart and discovery calls of the pages and of Select Configuration Profiles with publishing; ignore events of the default project. Let the restart manager take the snapshots when the server starts and when a full discovery runs, and apply the decision after the 500 ms debounce. Verify with a light platform test that two events within the debounce lead to one decision, and in task 4.2.
6+
- [ ] 1.2 Add the project topic for RobotCode settings changes; let every RobotCode settings page publish it after `apply()` and every RobotCode state component in `loadState()`, and replace the direct restart and discovery calls of the pages and of Select Configuration Profiles with publishing, but keep the direct handling of "Disable extension"; ignore events of the default project, and do nothing while "Disable extension" is checked or the interpreter is not usable. Let the restart manager take the snapshots when the server starts and when a full discovery runs, and apply the decision after the 500 ms debounce. Verify with a light platform test that two events within the debounce lead to one decision, and in task 4.2.
77
- [ ] 1.3 Let `RobotCodeVirtualFileListener` handle `.gitignore` and `.robotignore` as well, keep only events for files in the project's content or below the project folder (decided in `prepareChange`), request a server restart for `robocop.toml` and a restart plus a full discovery for the other files. Verify with light platform tests that an event for a file of another project is dropped and that each file name requests the right work.
88

99
## 2. Discovery engine
1010

1111
- [ ] 2.1 Give `RobotCodeTestManager` the injected coroutine scope, run one discovery at a time, run full discoveries inside `withBackgroundProgress`, let a new full discovery cancel the running one and the pending per-file ones, and destroy the `robotcode` process of a cancelled discovery. Verify with a light platform test that cancelling a discovery whose process is the test JVM's own `java` running a sleeping class ends that process, and that the model stays as before.
12-
- [ ] 2.2 Keep the model as an immutable tree in a `@Volatile` property: a full discovery replaces it, a per-file discovery swaps in a copy of the path to the changed suite, a failed or cancelled discovery leaves it unchanged. Decode with `Json { ignoreUnknownKeys = true }` and update `DataItemsTest`. Verify with JUnit 4 tests of the tree update (the old tree object is unchanged after a per-file update; siblings are shared) and of decoding output with an unknown key.
12+
- [ ] 2.2 Keep the model as an immutable tree in a `@Volatile` property: a full discovery replaces it, a per-file discovery keeps `fileDiscoveryArguments`, `findSuiteChildren` and the fallback to a full discovery and swaps in a copy of the path to the changed suite, a failed or cancelled discovery leaves it unchanged. Decode with `Json { ignoreUnknownKeys = true }` and update `DataItemsTest`. Verify with JUnit 4 tests of the tree update (the old tree object is unchanged after a per-file update; siblings are shared) and of decoding output with an unknown key.
1313
- [ ] 2.3 Change the triggers: content changes of suite files request a per-file discovery; creating, deleting, moving or renaming suite files and folders in the project request a full discovery; opening and closing editors request nothing; drop events of other projects. Add Tools | RobotCode | Refresh Robot Framework Tests, enabled only with a project. Verify with light platform tests that map VFS events to the requested work, and in task 4.2.
1414

1515
## 3. Failures and problems at markers

0 commit comments

Comments
 (0)