|
| 1 | +# VS Code Terminal SDKMAN Java Auto-Switch Design |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +本设计只解决一个明确目标:在 VS Code 集成终端中打开 Java 项目后,如果当前目录或后续 `cd` 到的目录包含 `.sdkmanrc`,则自动切换到该文件声明的 Java 版本。 |
| 6 | + |
| 7 | +本方案不修改普通终端行为,不尝试完整复刻 SDKMAN 的 auto-env 生命周期,也不处理离开项目目录后的自动恢复。 |
| 8 | + |
| 9 | +## Context |
| 10 | + |
| 11 | +当前仓库中的 [`shell/shared.d/java.sh`](/home/administrator/projects/env/powershellScripts/shell/shared.d/java.sh) 负责初始化 SDKMAN。实际排查发现: |
| 12 | + |
| 13 | +- SDKMAN 本机配置已经启用 `sdkman_auto_env=true` |
| 14 | +- 在普通交互式 Bash 中,进入 `/home/administrator/projects/work/java/mdm-boot` 后,`java -version` 可以自动切换到 `.sdkmanrc` 中声明的 `8.0.482.fx-zulu` |
| 15 | +- 在 VS Code 集成终端中,`sdkman_auto_env` 函数存在,但 `PROMPT_COMMAND` 被 VS Code 的 shell integration 改写为 `__vsc_prompt_cmd_original` |
| 16 | +- 用户的真实目标不是修复全部 auto-env 机制,而是确保 VS Code 新开终端进入项目后,Java 版本就是 `.sdkmanrc` 中指定的版本 |
| 17 | + |
| 18 | +## Goals |
| 19 | + |
| 20 | +- 仅在 VS Code 集成终端中生效 |
| 21 | +- 尽量少改动现有逻辑 |
| 22 | +- 避免引入轮询、延迟任务或额外性能开销 |
| 23 | +- 让“新开 VS Code 终端后进入项目目录”这一常见路径能正确切换 Java 版本 |
| 24 | + |
| 25 | +## Non-Goals |
| 26 | + |
| 27 | +- 不修复所有 shell 环境下的 SDKMAN auto-env 行为 |
| 28 | +- 不处理 `pushd`、`popd` 或其他目录栈命令 |
| 29 | +- 不在离开项目目录后自动执行 `sdk env clear` |
| 30 | +- 不改动 VS Code 配置、SDKMAN 全局配置或其他 shell 模块 |
| 31 | + |
| 32 | +## Constraints |
| 33 | + |
| 34 | +- 修改范围限定在 [`shell/shared.d/java.sh`](/home/administrator/projects/env/powershellScripts/shell/shared.d/java.sh) |
| 35 | +- 需要兼容当前机器上可能发生的重复 `source` 场景,避免重复包装 `cd` |
| 36 | +- 只在 `sdk` 已完成初始化且当前 shell 为交互式时触发附加逻辑 |
| 37 | + |
| 38 | +## Chosen Approach |
| 39 | + |
| 40 | +采用“仅 VS Code 环境包装 `cd`”的最小方案。 |
| 41 | + |
| 42 | +### Trigger Scope |
| 43 | + |
| 44 | +- 通过 `TERM_PROGRAM=vscode` 判断当前是否为 VS Code 集成终端 |
| 45 | +- 非 VS Code 环境直接保持现状,不附加任何兼容逻辑 |
| 46 | + |
| 47 | +### Startup Behavior |
| 48 | + |
| 49 | +- 在 `java.sh` 完成 SDKMAN 初始化后,如果当前 shell 是 VS Code 集成终端,并且当前目录存在 `.sdkmanrc`,执行一次 `sdk env` |
| 50 | +- 这样可以覆盖“终端启动后初始目录已经是项目目录”的情况 |
| 51 | + |
| 52 | +### Directory Change Behavior |
| 53 | + |
| 54 | +- 为 VS Code 环境包装 `cd` |
| 55 | +- 新包装函数先调用原始 `cd` |
| 56 | +- 仅当 `cd` 成功时才继续检查新目录 |
| 57 | +- 若新目录中存在 `.sdkmanrc`,执行 `sdk env` |
| 58 | +- 若新目录中不存在 `.sdkmanrc`,则不执行任何额外动作 |
| 59 | + |
| 60 | +### Idempotency |
| 61 | + |
| 62 | +- 包装前先检查是否已经完成包装,避免因为重复 `source` 造成多层嵌套 |
| 63 | +- 启动期的一次性同步也应避免重复触发 |
| 64 | + |
| 65 | +## Rationale |
| 66 | + |
| 67 | +选择该方案的原因如下: |
| 68 | + |
| 69 | +- 它直接命中用户真实诉求:只要 VS Code 终端进入项目目录时 Java 版本正确即可 |
| 70 | +- 它绕开了 VS Code 对 `PROMPT_COMMAND` 的包装影响,不再依赖提示符钩子链条 |
| 71 | +- 它比重新实现完整的 SDKMAN 环境清理逻辑更短、更稳定、更容易理解 |
| 72 | +- 它不会影响普通终端,也不会在每次提示符刷新时增加额外开销 |
| 73 | + |
| 74 | +## Trade-offs |
| 75 | + |
| 76 | +- 当用户离开项目目录后,当前 shell 可能仍保持项目使用的 Java 版本 |
| 77 | +- 只有 `cd` 触发目录变更时才会补切换,`pushd`/`popd` 不在本次范围内 |
| 78 | +- 如果 VS Code 或其他工具以非 `cd` 方式修改当前目录,本方案不会额外兜底 |
| 79 | + |
| 80 | +这些取舍是有意为之,用来换取最小改动和最低复杂度。 |
| 81 | + |
| 82 | +## Verification Plan |
| 83 | + |
| 84 | +以下验证以 `java -version` 为准,而不是 `sdk current java`: |
| 85 | + |
| 86 | +1. 在 VS Code 中新开一个 Bash 终端,切到 `/home/administrator/projects/work/java/mdm-boot`,确认 `java -version` 显示 `1.8.0_482` |
| 87 | +2. 在同一个 VS Code Bash 终端中,从其他目录执行 `cd /home/administrator/projects/work/java/mdm-boot`,确认 `java -version` 显示 `1.8.0_482` |
| 88 | +3. 在普通非 VS Code 终端中确认 `cd` 行为不变,没有新增输出或异常副作用 |
| 89 | + |
| 90 | +## Deferred Work |
| 91 | + |
| 92 | +如果后续需要更完整的体验,可以在后续独立变更中再考虑: |
| 93 | + |
| 94 | +- 为 `pushd` / `popd` 增加一致行为 |
| 95 | +- 离开项目目录时恢复默认 Java 版本 |
| 96 | +- 针对 Zsh 和 Bash 的目录切换路径做统一抽象 |
| 97 | + |
| 98 | +本设计不包含这些扩展内容。 |
0 commit comments