дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
fix(run): drop a moved file's temporary context configuration (#2037)
A single file's context configuration keeps the path it was produced for, so moving main.cxx out of src/ left 'context:file:src/main.cxx' selected while the gutter produced a second configuration for the new path — the run command still compiled the old file. fsChanged now drops temporary context configurations (context:file: and the compiled context:build: partner) whose file was removed, the way IDEA deletes the context configuration with the file. Saved and detected configurations stay. Fixes l0ng-ai/openidea#2035
Цей коміт міститься в:
1 джерело
27e73ea2cb
коміт
dfcdd8d764
3 files changed
+59
-1
No files matched your search
@@ -71,6 +71,7 @@ import {
|
||||
resetRuns,
|
||||
runningSessions,
|
||||
saveUserConfigs,
|
||||
dropStaleContextConfigs,
|
||||
selectConfig,
|
||||
selectedConfig,
|
||||
startRun,
|
||||
@@ -654,6 +655,8 @@ export function activate(): () => void {
|
||||
disposers.push(
|
||||
onIdeEvent("fsChanged", (e) => {
|
||||
const paths = [...e.created, ...e.modified, ...e.removed];
|
||||
// A file that moved or was deleted leaves its context configuration behind.
|
||||
dropStaleContextConfigs(e.removed);
|
||||
if (paths.some((p) => /(?:^|\/)go\.(?:mod|work)$/.test(p))) invalidateGoModules();
|
||||
if (paths.some((p) => p.endsWith("/__init__.py"))) invalidatePythonPackages();
|
||||
if (paths.some((p) => CONFIG_FILES.test(p))) scheduleReload();
|
||||
|
||||
@@ -19,7 +19,15 @@ import type { RunConfig } from "@/platform/ipc";
|
||||
import { useProject } from "@/platform/project";
|
||||
import { EditConfigsOverlay } from "./EditConfigsDialog";
|
||||
import { ConfigChooserOverlay } from "./ConfigChooser";
|
||||
import { adoptTemporary, canDeleteConfig, deleteConfig, loadConfigs, resetRuns, useRun } from "./store";
|
||||
import {
|
||||
adoptTemporary,
|
||||
canDeleteConfig,
|
||||
deleteConfig,
|
||||
dropStaleContextConfigs,
|
||||
loadConfigs,
|
||||
resetRuns,
|
||||
useRun,
|
||||
} from "./store";
|
||||
import { registerBuiltinTypes } from "./types";
|
||||
|
||||
const config = (kind = "npm"): RunConfig => ({
|
||||
@@ -112,6 +120,27 @@ it("temporary deletion does not persist suppression", async () => {
|
||||
expect(useRun.getState().configs).toEqual([]);
|
||||
});
|
||||
|
||||
it("a moved file drops its stale temporary context configuration", async () => {
|
||||
const old: RunConfig = { ...config("cc"), id: "context:file:src/main.cxx", name: "main.cxx", detected: false };
|
||||
adoptTemporary(old);
|
||||
expect(useRun.getState().configs.map((c) => c.id)).toContain("context:file:src/main.cxx");
|
||||
dropStaleContextConfigs(["/test-removal/src/main.cxx"]);
|
||||
expect(useRun.getState().configs.map((c) => c.id)).not.toContain("context:file:src/main.cxx");
|
||||
expect(useRun.getState().temporary).toEqual([]);
|
||||
expect(ipc.runSaveConfigs).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps other temporary and non-temporary configurations on removal", async () => {
|
||||
const moved: RunConfig = { ...config("cc"), id: "context:file:src/main.cxx", name: "main.cxx", detected: false };
|
||||
const other: RunConfig = { ...config("go"), id: "context:file:app/main.go", name: "main.go", detected: false };
|
||||
adoptTemporary(moved);
|
||||
adoptTemporary(other);
|
||||
dropStaleContextConfigs(["/test-removal/src/main.cxx"]);
|
||||
expect(useRun.getState().temporary).toEqual([other.id]);
|
||||
// The saved/detected npm config and an unrelated file stay.
|
||||
expect(useRun.getState().configs.map((c) => c.id)).toEqual(expect.arrayContaining(["npm:app", other.id]));
|
||||
});
|
||||
|
||||
it("chooser popup Delete removes the selected detection", async () => {
|
||||
useRun.setState({ chooserOpen: true, editorOpen: false });
|
||||
render(<ConfigChooserOverlay />);
|
||||
|
||||
@@ -411,6 +411,32 @@ export function adoptTemporary(config: RunConfig): RunConfig {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* A file removed (or moved away) takes the temporary context configurations its
|
||||
* path produced (`context:file:` and the compiled `context:build:` partner), like
|
||||
* IDEA deleting the context configuration with the file. Saved and detected
|
||||
* configurations the user made stay.
|
||||
*/
|
||||
export function dropStaleContextConfigs(removed: readonly string[]) {
|
||||
const { configs, temporary, selectedId } = useRun.getState();
|
||||
const root = useProject.getState().project?.root;
|
||||
if (!root || !removed.length || !temporary.length) return;
|
||||
// Temporary ids carry the project-relative path (the full path outside the root).
|
||||
const gone = new Set(removed.map((p) => (p.startsWith(`${root}/`) ? p.slice(root.length + 1) : p)));
|
||||
const stale = (id: string) => {
|
||||
const m = /^context:(?:file|build):(.+?)(?:#.+)?$/.exec(id);
|
||||
return !!m && gone.has(m[1]!);
|
||||
};
|
||||
const dropped = temporary.filter(stale);
|
||||
if (!dropped.length) return;
|
||||
useRun.setState({
|
||||
configs: configs.filter((c) => !dropped.includes(c.id)),
|
||||
temporary: temporary.filter((id) => !stale(id)),
|
||||
});
|
||||
persistTemporary();
|
||||
if (selectedId && dropped.includes(selectedId)) selectConfig(null);
|
||||
}
|
||||
|
||||
/** "Save Configuration": a temporary configuration becomes a user configuration. */
|
||||
export async function saveTemporary(id: string): Promise<void> {
|
||||
const { configs, temporary } = useRun.getState();
|
||||
|
||||
Reference in new issue
Заблокувати користувача