diff --git a/docs/development/platform-parity-matrix.csv b/docs/development/platform-parity-matrix.csv index 6d670ca60..b421f23da 100644 --- a/docs/development/platform-parity-matrix.csv +++ b/docs/development/platform-parity-matrix.csv @@ -36,7 +36,7 @@ ai-chat-session,AI,AI 对话,对话会话与流式响应,未实现,不适用,已 ai-chat-history,AI,AI 对话,对话历史、置顶与归档,未实现,不适用,已实现,待验证,AI,Windows 验证历史加载、重命名、置顶、归档、删除和重启后持久化。,,macos/Sources/Lithe/Platform/MacOS/AI,windows/tauri/src/features/ai/services/ai-chat-history-service.ts; windows/tauri/src/features/layout/components/sidebar/sidebar-history.tsx ai-chat-providers,AI,AI 对话,多 Provider 与工作区范围,未实现,不适用,已实现,待验证,AI,Windows 验证 Provider 切换、凭据边界、工作区范围和模型错误提示。,,macos/Sources/Lithe/Platform/MacOS/AI,windows/tauri/src/features/ai/services/providers; windows/tauri/src/features/ai/lib/ai-workspace-scope.ts java-runtime-discovery,Java,Java/Maven,JDK 与 Maven 工具链发现,已实现,待验证,已实现,待验证,Java / Maven,配置多个 JDK/Maven 候选,验证发现、选择、版本不匹配和错误提示。,,macos/Sources/Lithe/Services/Java; macos/Sources/Lithe/Views/Run,windows/tauri/src/features/maven; windows/tauri/src/features/workspace -java-maven-project-settings,Java,Java/Maven,按项目配置 Maven 工具链、settings.xml 与本地仓库,已实现,待验证,已实现,待验证,Java / Maven,在 macOS 项目设置和 Windows Maven 设置页分别切换两个项目,配置 Maven、JDK、settings.xml 与本地仓库,验证保存重开、自动检测展示及项目间隔离;Windows 还需验证写盘失败后可直接重试。 Windows 自定义 settings.xml 后,仓库留空应显示该文件中的目录;验证 XML 实体和 CDATA、继承的项目 JDK/Maven、切换项目丢弃未保存草稿;冷启动 Wrapper 不应触发下载,版本探测卡住时设置窗口保持响应并在超时后清理探测进程。,,macos/Sources/Lithe/Views/App/ProjectRuntimeSettingsView.swift; macos/Sources/Lithe/Services/Java/ProjectRuntimeService.swift,windows/tauri/src/features/settings/components/tabs/maven-settings-panel.tsx; windows/tauri/src/features/maven/stores/maven.store.ts; windows/tauri/src-tauri/src/maven.rs; windows/tauri/src-tauri/src/maven/settings_xml.rs; windows/tauri/src/features/maven/components/maven-detected-value.test.tsx; windows/tauri/src/features/settings/components/tabs/maven-settings-panel.test.tsx +java-maven-project-settings,Java,Java/Maven,按项目配置 Maven 工具链、settings.xml 与本地仓库,已实现,待验证,已实现,待验证,Java / Maven,在 macOS 项目设置和 Windows Maven 设置页分别切换两个项目,配置 Maven、JDK、settings.xml 与本地仓库,验证保存重开、自动检测展示及项目间隔离;Windows 还需验证写盘失败后可直接重试。 Windows 自定义 settings.xml 后,仓库留空应显示该文件中的目录;验证 XML 实体和 CDATA、继承的项目 JDK/Maven、切换项目丢弃未保存草稿;冷启动 Wrapper 不应触发下载,版本探测卡住时设置窗口保持响应并在超时后清理探测进程;Run 配置显式选择的 Maven/JDK 优先于项目设置,未设置的字段分别继承 Maven 设置;确认主任务与前置任务使用相同工具链,Maven 设置上下文缺失时仍保留显式覆盖,均未配置时交由平台自动发现。,,macos/Sources/Lithe/Views/App/ProjectRuntimeSettingsView.swift; macos/Sources/Lithe/Services/Java/ProjectRuntimeService.swift,windows/tauri/src/features/settings/components/tabs/maven-settings-panel.tsx; windows/tauri/src/features/maven/stores/maven.store.ts; windows/tauri/src-tauri/src/maven.rs; windows/tauri/src-tauri/src/maven/settings_xml.rs; windows/tauri/src/features/maven/components/maven-detected-value.test.tsx; windows/tauri/src/features/settings/components/tabs/maven-settings-panel.test.tsx; windows/tauri/src/features/run/stores/run.store.ts; windows/tauri/src/features/run/stores/run-maven-context.test.ts java-maven-project,Java,Java/Maven,项目根、模块与源码集识别,已实现,待验证,已实现,待验证,Java / Maven,使用单模块、多模块和非标准源码目录 fixture 对比项目模型。,,macos/Sources/Lithe/Services/Java; shared/contracts/application-boundary.md,windows/tauri/src/features/maven; windows/tauri/src/features/workspace java-maven-profiles-dependencies,Java,Java/Maven,Profile 与依赖树,已实现,待验证,已实现,待验证,Java / Maven,"切换 Maven profile 并刷新依赖树,确认模块归属、顺序、版本冲突/重复/依赖管理标注和错误边界;在依赖节点超过 6,000 个的项目中确认依赖树完整加载。",,macos/Sources/Lithe/Views/Run; macos/Sources/Lithe/Services/Java; macos/Sources/LitheExecutionModule/Services/MavenService.swift; macos/Sources/Lithe/Platform/MacOS/Persistence/MacMavenDependencyOutputStore.swift; rust/lithe-core/src/project/maven_dependency_tree.rs,windows/tauri/src/features/maven; windows/tauri/src-tauri/src/maven.rs; rust/lithe-core/src/project/maven_dependency_tree.rs java-maven-toolbar-tooltips,Java,Java/Maven,Maven 工具栏图标悬停说明,已实现,待验证,已实现,待验证,Java / Maven,在 macOS 和 Windows 的 Maven 工具窗口逐一悬停运行、执行目标、重新加载、跳过测试、折叠和设置图标,确认提示与动作一致、提示框宽度适配文字;再检查禁用按钮和窄窗口中的提示换行与边缘约束。,,macos/Sources/Lithe/Views/Run/MavenView.swift; macos/Sources/Lithe/Views/Workbench/WorkbenchHoverTooltip.swift; macos/Tests/LitheTests/WorkbenchHoverTooltipTests.swift,windows/tauri/src/features/maven/components/maven-pane.tsx diff --git a/docs/development/platform-parity-matrix.md b/docs/development/platform-parity-matrix.md index 79a8358d5..22c2864a5 100644 --- a/docs/development/platform-parity-matrix.md +++ b/docs/development/platform-parity-matrix.md @@ -133,7 +133,7 @@ | 功能组 | 能力点 | macOS | Windows | 负责人 | 验证方式 | 备注 | | --- | --- | --- | --- | --- | --- | --- | | Java/Maven | **JDK 与 Maven 工具链发现**
java-runtime-discovery | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Services/Java`、`macos/Sources/Lithe/Views/Run` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/maven`、`windows/tauri/src/features/workspace` | Java / Maven | 配置多个 JDK/Maven 候选,验证发现、选择、版本不匹配和错误提示。 | | -| Java/Maven | **按项目配置 Maven 工具链、settings.xml 与本地仓库**
java-maven-project-settings | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/App/ProjectRuntimeSettingsView.swift`、`macos/Sources/Lithe/Services/Java/ProjectRuntimeService.swift` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/settings/components/tabs/maven-settings-panel.tsx`、`windows/tauri/src/features/maven/stores/maven.store.ts`、`windows/tauri/src-tauri/src/maven.rs`、`windows/tauri/src-tauri/src/maven/settings_xml.rs`、`windows/tauri/src/features/maven/components/maven-detected-value.test.tsx`、`windows/tauri/src/features/settings/components/tabs/maven-settings-panel.test.tsx` | Java / Maven | 在 macOS 项目设置和 Windows Maven 设置页分别切换两个项目,配置 Maven、JDK、settings.xml 与本地仓库,验证保存重开、自动检测展示及项目间隔离;Windows 还需验证写盘失败后可直接重试。 Windows 自定义 settings.xml 后,仓库留空应显示该文件中的目录;验证 XML 实体和 CDATA、继承的项目 JDK/Maven、切换项目丢弃未保存草稿;冷启动 Wrapper 不应触发下载,版本探测卡住时设置窗口保持响应并在超时后清理探测进程。 | | +| Java/Maven | **按项目配置 Maven 工具链、settings.xml 与本地仓库**
java-maven-project-settings | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/App/ProjectRuntimeSettingsView.swift`、`macos/Sources/Lithe/Services/Java/ProjectRuntimeService.swift` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/settings/components/tabs/maven-settings-panel.tsx`、`windows/tauri/src/features/maven/stores/maven.store.ts`、`windows/tauri/src-tauri/src/maven.rs`、`windows/tauri/src-tauri/src/maven/settings_xml.rs`、`windows/tauri/src/features/maven/components/maven-detected-value.test.tsx`、`windows/tauri/src/features/settings/components/tabs/maven-settings-panel.test.tsx`、`windows/tauri/src/features/run/stores/run.store.ts`、`windows/tauri/src/features/run/stores/run-maven-context.test.ts` | Java / Maven | 在 macOS 项目设置和 Windows Maven 设置页分别切换两个项目,配置 Maven、JDK、settings.xml 与本地仓库,验证保存重开、自动检测展示及项目间隔离;Windows 还需验证写盘失败后可直接重试。 Windows 自定义 settings.xml 后,仓库留空应显示该文件中的目录;验证 XML 实体和 CDATA、继承的项目 JDK/Maven、切换项目丢弃未保存草稿;冷启动 Wrapper 不应触发下载,版本探测卡住时设置窗口保持响应并在超时后清理探测进程;Run 配置显式选择的 Maven/JDK 优先于项目设置,未设置的字段分别继承 Maven 设置;确认主任务与前置任务使用相同工具链,Maven 设置上下文缺失时仍保留显式覆盖,均未配置时交由平台自动发现。 | | | Java/Maven | **项目根、模块与源码集识别**
java-maven-project | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Services/Java`、`shared/contracts/application-boundary.md` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/maven`、`windows/tauri/src/features/workspace` | Java / Maven | 使用单模块、多模块和非标准源码目录 fixture 对比项目模型。 | | | Java/Maven | **Profile 与依赖树**
java-maven-profiles-dependencies | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/Run`、`macos/Sources/Lithe/Services/Java`、`macos/Sources/LitheExecutionModule/Services/MavenService.swift`、`macos/Sources/Lithe/Platform/MacOS/Persistence/MacMavenDependencyOutputStore.swift`、`rust/lithe-core/src/project/maven_dependency_tree.rs` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/maven`、`windows/tauri/src-tauri/src/maven.rs`、`rust/lithe-core/src/project/maven_dependency_tree.rs` | Java / Maven | 切换 Maven profile 并刷新依赖树,确认模块归属、顺序、版本冲突/重复/依赖管理标注和错误边界;在依赖节点超过 6,000 个的项目中确认依赖树完整加载。 | | | Java/Maven | **Maven 工具栏图标悬停说明**
java-maven-toolbar-tooltips | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/Run/MavenView.swift`、`macos/Sources/Lithe/Views/Workbench/WorkbenchHoverTooltip.swift`、`macos/Tests/LitheTests/WorkbenchHoverTooltipTests.swift` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/maven/components/maven-pane.tsx` | Java / Maven | 在 macOS 和 Windows 的 Maven 工具窗口逐一悬停运行、执行目标、重新加载、跳过测试、折叠和设置图标,确认提示与动作一致、提示框宽度适配文字;再检查禁用按钮和窄窗口中的提示换行与边缘约束。 | | diff --git a/shared/platform-feature-matrix.json b/shared/platform-feature-matrix.json index 2b37a65f9..9218cf97c 100644 --- a/shared/platform-feature-matrix.json +++ b/shared/platform-feature-matrix.json @@ -986,13 +986,15 @@ "windows/tauri/src-tauri/src/maven.rs", "windows/tauri/src-tauri/src/maven/settings_xml.rs", "windows/tauri/src/features/maven/components/maven-detected-value.test.tsx", - "windows/tauri/src/features/settings/components/tabs/maven-settings-panel.test.tsx" + "windows/tauri/src/features/settings/components/tabs/maven-settings-panel.test.tsx", + "windows/tauri/src/features/run/stores/run.store.ts", + "windows/tauri/src/features/run/stores/run-maven-context.test.ts" ], "implementationStatus": "implemented", "verificationStatus": "pending" }, "owner": "Java / Maven", - "verification": "在 macOS 项目设置和 Windows Maven 设置页分别切换两个项目,配置 Maven、JDK、settings.xml 与本地仓库,验证保存重开、自动检测展示及项目间隔离;Windows 还需验证写盘失败后可直接重试。 Windows 自定义 settings.xml 后,仓库留空应显示该文件中的目录;验证 XML 实体和 CDATA、继承的项目 JDK/Maven、切换项目丢弃未保存草稿;冷启动 Wrapper 不应触发下载,版本探测卡住时设置窗口保持响应并在超时后清理探测进程。" + "verification": "在 macOS 项目设置和 Windows Maven 设置页分别切换两个项目,配置 Maven、JDK、settings.xml 与本地仓库,验证保存重开、自动检测展示及项目间隔离;Windows 还需验证写盘失败后可直接重试。 Windows 自定义 settings.xml 后,仓库留空应显示该文件中的目录;验证 XML 实体和 CDATA、继承的项目 JDK/Maven、切换项目丢弃未保存草稿;冷启动 Wrapper 不应触发下载,版本探测卡住时设置窗口保持响应并在超时后清理探测进程;Run 配置显式选择的 Maven/JDK 优先于项目设置,未设置的字段分别继承 Maven 设置;确认主任务与前置任务使用相同工具链,Maven 设置上下文缺失时仍保留显式覆盖,均未配置时交由平台自动发现。" }, { "id": "java-maven-project", diff --git a/windows/tauri/src/features/run/stores/run-maven-context.test.ts b/windows/tauri/src/features/run/stores/run-maven-context.test.ts index eab684105..af0b7590a 100644 --- a/windows/tauri/src/features/run/stores/run-maven-context.test.ts +++ b/windows/tauri/src/features/run/stores/run-maven-context.test.ts @@ -172,98 +172,103 @@ describe("Maven-backed Run context", () => { ]); }); - test("a configured Maven path overrides the project Maven context", async () => { - const automaticContext: MavenLaunchContext = { - ...mavenContext, - mavenExecutablePath: "D:/project/mvn.cmd", - javaHomePath: "C:/project/jdk", - }; - const resolveRunLaunch = mock(async () => ({ - executable: "D:/Tools/mvn.cmd", - workingDirectory: "D:/work/reactor", - environment: {}, - })); - const dependencies: RunStoreDependencies = { - createLaunchPlan: mock(async () => ({ - executable: { toolchain: "project-maven" as const }, - arguments: ["-B", "spring-boot:run"], - workingDirectory: "reactor", - })), - mavenLaunchContextForWorkspace: mock(async () => automaticContext), - resolveRunLaunch, - saveWorkspaceBeforeLaunch: mock(async () => undefined), - executePreLaunchStep: mock(async () => ({ exitCode: 0, output: "" })), - startRunProcess: mock(async () => undefined), - stopRunProcess: mock(async () => undefined), - seedMavenLocalConfiguration: () => undefined, - prepareJavaRunLaunch: mock(async () => null), - }; - const store = createRunStore("workspace", dependencies); - store.setState({ - root: "D:/work", - configurations: [ - { - ...configuration, - mavenExecutablePath: "D:/legacy/mvn.cmd", - mavenJavaHomePath: "C:/legacy/jdk", - }, - ], - diagnostics: [], - effectiveRuntimeExecutablePaths: {}, - }); + const pathCases = [ + { + name: "explicit configuration overrides project settings", + context: mavenContext, + selected: { mavenExecutablePath: "D:/selected/mvn.cmd", mavenJavaHomePath: "C:/selected/jdk" }, + expected: { mavenExecutablePath: "D:/selected/mvn.cmd", mavenJavaHomePath: "C:/selected/jdk" }, + }, + { + name: "explicit configuration works without a Maven context", + context: null, + selected: { mavenExecutablePath: "D:/selected/mvn.cmd", mavenJavaHomePath: "C:/selected/jdk" }, + expected: { mavenExecutablePath: "D:/selected/mvn.cmd", mavenJavaHomePath: "C:/selected/jdk" }, + }, + { + name: "blank configuration inherits project settings", + context: mavenContext, + selected: { mavenExecutablePath: " ", mavenJavaHomePath: "" }, + expected: { mavenExecutablePath: mavenContext.mavenExecutablePath, mavenJavaHomePath: mavenContext.javaHomePath }, + }, + { + name: "Maven override still inherits the project Maven JDK", + context: mavenContext, + selected: { mavenExecutablePath: "D:/selected/mvn.cmd", mavenJavaHomePath: "" }, + expected: { mavenExecutablePath: "D:/selected/mvn.cmd", mavenJavaHomePath: mavenContext.javaHomePath }, + }, + { + name: "Maven JDK override still inherits the project Maven", + context: mavenContext, + selected: { mavenExecutablePath: "", mavenJavaHomePath: "C:/selected/jdk" }, + expected: { mavenExecutablePath: mavenContext.mavenExecutablePath, mavenJavaHomePath: "C:/selected/jdk" }, + }, + { + name: "blank configuration without context delegates automatic discovery to the host", + context: null, + selected: { mavenExecutablePath: "", mavenJavaHomePath: " " }, + expected: { mavenExecutablePath: "", mavenJavaHomePath: "" }, + }, + ]; - await store.getState().actions.runConfiguration(configuration.id); + for (const scenario of pathCases) { + test(`${scenario.name} for launch and pre-launch`, async () => { + const resolveRunLaunch = mock(async ( + request: Parameters[0], + ) => ({ + executable: request.mavenExecutablePath || "mvn", + workingDirectory: "D:/work/reactor", + environment: { JAVA_HOME: request.mavenJavaHomePath || "C:/automatic/jdk" }, + })); + const executePreLaunchStep = mock(async () => ({ exitCode: 0, output: "" })); + const startRunProcess = mock(async () => undefined); + const dependencies: RunStoreDependencies = { + createLaunchPlan: mock(async () => ({ + executable: { toolchain: "project-maven" as const }, + arguments: ["-B", "spring-boot:run"], + workingDirectory: "reactor", + preLaunchSteps: [{ + executable: { toolchain: "project-maven" as const }, + arguments: ["-B", "compile"], + }], + })), + mavenLaunchContextForWorkspace: mock(async () => scenario.context), + resolveRunLaunch, + saveWorkspaceBeforeLaunch: mock(async () => undefined), + executePreLaunchStep, + startRunProcess, + stopRunProcess: mock(async () => undefined), + seedMavenLocalConfiguration: () => undefined, + prepareJavaRunLaunch: mock(async () => null), + }; + const store = createRunStore("workspace", dependencies); + store.setState({ + root: "D:/work", + configurations: [{ ...configuration, ...scenario.selected }], + diagnostics: [], + effectiveRuntimeExecutablePaths: {}, + }); - expect(resolveRunLaunch).toHaveBeenCalledWith( - expect.objectContaining({ - mavenExecutablePath: "D:/legacy/mvn.cmd", - mavenJavaHomePath: "C:/legacy/jdk", - }), - ); - }); + expect(await store.getState().actions.runConfiguration(configuration.id)).toBe(configuration.id); - test("an empty run configuration Maven path uses the project Maven context", async () => { - const resolveRunLaunch = mock(async () => ({ - executable: "D:/Tools/mvn.cmd", - workingDirectory: "D:/work/reactor", - environment: {}, - })); - const dependencies: RunStoreDependencies = { - createLaunchPlan: mock(async () => ({ - executable: { toolchain: "project-maven" as const }, + expect(resolveRunLaunch).toHaveBeenCalledTimes(2); + for (const [request] of resolveRunLaunch.mock.calls) { + expect(request).toEqual(expect.objectContaining(scenario.expected)); + } + const resolvedPaths = { + executable: scenario.expected.mavenExecutablePath || "mvn", + environment: { JAVA_HOME: scenario.expected.mavenJavaHomePath || "C:/automatic/jdk" }, + }; + expect(executePreLaunchStep).toHaveBeenCalledTimes(1); + expect(executePreLaunchStep).toHaveBeenCalledWith(expect.objectContaining({ + ...resolvedPaths, + arguments: ["-B", "compile"], + })); + expect(startRunProcess).toHaveBeenCalledTimes(1); + expect(startRunProcess).toHaveBeenCalledWith(expect.objectContaining({ + ...resolvedPaths, arguments: ["-B", "spring-boot:run"], - workingDirectory: "reactor", - })), - mavenLaunchContextForWorkspace: mock(async () => mavenContext), - resolveRunLaunch, - saveWorkspaceBeforeLaunch: mock(async () => undefined), - executePreLaunchStep: mock(async () => ({ exitCode: 0, output: "" })), - startRunProcess: mock(async () => undefined), - stopRunProcess: mock(async () => undefined), - seedMavenLocalConfiguration: () => undefined, - prepareJavaRunLaunch: mock(async () => null), - }; - const store = createRunStore("workspace", dependencies); - store.setState({ - root: "D:/work", - configurations: [ - { - ...configuration, - mavenExecutablePath: " ", - mavenJavaHomePath: "", - }, - ], - diagnostics: [], - effectiveRuntimeExecutablePaths: {}, + })); }); - - await store.getState().actions.runConfiguration(configuration.id); - - expect(resolveRunLaunch).toHaveBeenCalledWith( - expect.objectContaining({ - mavenExecutablePath: "D:/Tools/apache-maven", - mavenJavaHomePath: "C:/Java/jdk-21", - }), - ); - }); + } });