From 55a42597bb888e2027ec6154913f4074acac110b Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Sun, 14 Jun 2026 06:43:52 -0600 Subject: [PATCH] docs: add implementation plan for code formatting feature --- .../plans/2026-06-14-code-formatting.md | 474 ++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-14-code-formatting.md diff --git a/docs/superpowers/plans/2026-06-14-code-formatting.md b/docs/superpowers/plans/2026-06-14-code-formatting.md new file mode 100644 index 0000000..e31e1b9 --- /dev/null +++ b/docs/superpowers/plans/2026-06-14-code-formatting.md @@ -0,0 +1,474 @@ +# 代码格式化(整理代码)Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 给 `codenext` 编辑器增加"整理代码"按钮,后端通过 `POST /format` 调用 ruff(Python/海龟绘图)和 clang-format(C/C++)格式化代码。 + +**Architecture:** `codeapinew` 新增 `formatter.py`(封装 subprocess 调用 ruff/clang-format),`main.py` 新增 `/format` 端点;`codenext` 新增 `api.ts` 的 `formatCode`、`composables/code.ts` 的 `format()`,并在 `CodeSection.vue` 加按钮。 + +**Tech Stack:** FastAPI, Pydantic, subprocess, ruff (CLI), clang-format (CLI), Vue 3, axios, naive-ui + +--- + +## Task 1: 后端依赖(ruff、pytest、clang-format) + +**Files:** +- Modify: `codeapinew/pyproject.toml` +- Modify: `codeapinew/requirements.txt` +- Modify: `codeapinew/Dockerfile` + +- [ ] **Step 1: 添加 ruff 为运行依赖** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && uv add ruff +``` + +Expected: `pyproject.toml` 的 `dependencies` 列表新增 `"ruff>=..."`,`uv.lock` 更新。 + +- [ ] **Step 2: 添加 pytest 为开发依赖** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && uv add --dev pytest +``` + +Expected: `pyproject.toml` 新增 `[dependency-groups]` / `dev = ["pytest>=..."]`。 + +- [ ] **Step 3: 查询已安装的 ruff 版本,同步到 requirements.txt** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && uv run ruff --version +``` + +记录输出的版本号(如 `ruff 0.8.x`),然后在 `requirements.txt` 中按字母序插入一行(例如插在 `pyyaml` 和 `pytz` 之间,requirements.txt 当前是字母序排列): + +``` +ruff==<上一步输出的版本号> +``` + +- [ ] **Step 4: Dockerfile 中加入 clang-format** + +修改 `codeapinew/Dockerfile` 中的 apt 安装步骤: + +```dockerfile +# 安装系统依赖 +RUN apt-get update && apt-get install -y \ + gcc \ + clang-format \ + && rm -rf /var/lib/apt/lists/* +``` + +- [ ] **Step 5: Commit** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && git add pyproject.toml uv.lock requirements.txt Dockerfile && git commit -m "build: add ruff and pytest dependencies, install clang-format in image" +``` + +--- + +## Task 2: `formatter.py` — 格式化核心逻辑(TDD) + +**Files:** +- Create: `codeapinew/formatter.py` +- Test: `codeapinew/test_formatter.py` + +- [ ] **Step 1: 写失败的测试** + +创建 `codeapinew/test_formatter.py`: + +```python +import pytest + +from formatter import FormatError, format_code + + +def test_format_python_normalizes_spacing(): + result = format_code("x=1\n", "python") + assert result == "x = 1\n" + + +def test_format_turtle_uses_python_formatter(): + result = format_code("t=1\n", "turtle") + assert result == "t = 1\n" + + +def test_format_python_syntax_error_raises(): + with pytest.raises(FormatError): + format_code("def foo(:\n", "python") + + +def test_format_c_uses_allman_braces(): + result = format_code("int main(){int x=1;return x;}", "c") + assert result.rstrip("\n") == "int main()\n{\n int x = 1;\n return x;\n}" + + +def test_format_cpp_uses_allman_braces(): + result = format_code("class Foo{};", "cpp") + assert result.rstrip("\n") == "class Foo\n{\n};" + + +def test_format_unsupported_language_raises(): + with pytest.raises(FormatError): + format_code("print(1)", "java") +``` + +- [ ] **Step 2: 运行测试,确认失败** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && uv run pytest test_formatter.py -v +``` + +Expected: FAIL,`ModuleNotFoundError: No module named 'formatter'`(或 collection error)。 + +- [ ] **Step 3: 实现 `formatter.py`** + +创建 `codeapinew/formatter.py`: + +```python +import subprocess + +TIMEOUT_SECONDS = 5 + +CLANG_FORMAT_STYLE = "{BasedOnStyle: LLVM, IndentWidth: 4, BreakBeforeBraces: Allman}" + + +class FormatError(Exception): + """代码格式化失败时抛出,message 为可展示给用户的错误信息""" + + +def _run(cmd: list[str], code: str) -> str: + try: + result = subprocess.run( + cmd, + input=code, + capture_output=True, + text=True, + timeout=TIMEOUT_SECONDS, + ) + except subprocess.TimeoutExpired: + raise FormatError("格式化超时") + + if result.returncode != 0: + raise FormatError(result.stderr.strip() or "格式化失败") + + return result.stdout + + +def format_code(code: str, language: str) -> str: + if language in ("python", "turtle"): + return _run(["ruff", "format", "-", "--stdin-filename", "main.py"], code) + + if language in ("c", "cpp"): + ext = "c" if language == "c" else "cpp" + return _run( + [ + "clang-format", + f"-style={CLANG_FORMAT_STYLE}", + f"-assume-filename=main.{ext}", + ], + code, + ) + + raise FormatError(f"不支持的语言: {language}") +``` + +- [ ] **Step 4: 运行测试,确认通过** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && uv run pytest test_formatter.py -v +``` + +Expected: 全部 6 个测试 PASS。 + +- [ ] **Step 5: Commit** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && git add formatter.py test_formatter.py && git commit -m "feat: add formatter module wrapping ruff and clang-format" +``` + +--- + +## Task 3: `POST /format` 端点(TDD) + +**Files:** +- Modify: `codeapinew/schemas.py` +- Modify: `codeapinew/main.py` +- Test: `codeapinew/test_main.py` + +- [ ] **Step 1: 写失败的测试** + +创建 `codeapinew/test_main.py`: + +```python +from fastapi.testclient import TestClient + +from main import app + +client = TestClient(app) + + +def test_format_endpoint_formats_python_code(): + response = client.post("/format", json={"code": "x=1\n", "language": "python"}) + assert response.status_code == 200 + assert response.json() == {"code": "x = 1\n"} + + +def test_format_endpoint_returns_400_on_syntax_error(): + response = client.post( + "/format", json={"code": "def foo(:\n", "language": "python"} + ) + assert response.status_code == 400 +``` + +- [ ] **Step 2: 运行测试,确认失败** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && uv run pytest test_main.py -v +``` + +Expected: FAIL,两个测试均得到 404(路由不存在)。 + +- [ ] **Step 3: 在 `schemas.py` 新增请求/响应模型** + +在 `codeapinew/schemas.py` 末尾追加: + +```python + + +class FormatRequest(BaseModel): + """格式化代码的请求模式""" + + code: str + language: str + + +class FormatResponse(BaseModel): + """格式化代码的响应模式""" + + code: str +``` + +- [ ] **Step 4: 在 `main.py` 新增 `/format` 端点** + +修改 `codeapinew/main.py` 第 8 行的导入,加入新模型: + +```python +from schemas import ( + PresetCodeCreate, + AIAnalysisRequest, + DebugRequest, + FormatRequest, + FormatResponse, +) +``` + +在文件顶部其他导入旁加入: + +```python +from formatter import format_code, FormatError +``` + +在 `/debug` 端点(第 127-139 行)之后新增: + +```python +@app.post("/format", response_model=FormatResponse) +async def format_code_endpoint(request: FormatRequest) -> FormatResponse: + """格式化代码""" + try: + formatted = format_code(request.code, request.language) + except FormatError as e: + raise HTTPException(status_code=400, detail=str(e)) + return FormatResponse(code=formatted) +``` + +- [ ] **Step 5: 运行测试,确认通过** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && uv run pytest -v +``` + +Expected: 全部测试(`test_formatter.py` + `test_main.py`)PASS。 + +- [ ] **Step 6: Commit** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && git add schemas.py main.py test_main.py && git commit -m "feat: add POST /format endpoint" +``` + +--- + +## Task 4: 前端 API 客户端 `formatCode` + +**Files:** +- Modify: `codenext/src/api.ts` + +- [ ] **Step 1: 修改导入并新增 `formatCode`** + +将 `codenext/src/api.ts` 第 3 行: + +```ts +import { Code, Submission } from "./types" +``` + +改为: + +```ts +import { Code, LANGUAGE, Submission } from "./types" +``` + +在文件末尾(`debug` 函数之后)新增: + +```ts +export async function formatCode(code: string, language: LANGUAGE) { + const res = await api.post("/format", { code, language }) + return res.data.code as string +} +``` + +- [ ] **Step 2: Commit** + +```bash +cd /home/xuyue/Projects/Code/codenext && git add src/api.ts && git commit -m "feat: add formatCode API client" +``` + +--- + +## Task 5: 前端状态 action `format()` + +**Files:** +- Modify: `codenext/src/composables/code.ts` + +- [ ] **Step 1: 修改导入并新增 `format` action** + +将 `codenext/src/composables/code.ts` 第 5 行: + +```ts +import { getCodeByQuery, submit } from "../api" +``` + +改为: + +```ts +import { formatCode, getCodeByQuery, submit } from "../api" +``` + +在文件末尾(`share` 函数之后)新增: + +```ts +export async function format() { + code.value = await formatCode(code.value, code.language) +} +``` + +- [ ] **Step 2: Commit** + +```bash +cd /home/xuyue/Projects/Code/codenext && git add src/composables/code.ts && git commit -m "feat: add format action to code composable" +``` + +--- + +## Task 6: "整理代码"按钮(`CodeSection.vue`)+ 手动验证 + +**Files:** +- Modify: `codenext/src/desktop/CodeSection.vue` + +- [ ] **Step 1: 修改导入** + +将 `codenext/src/desktop/CodeSection.vue` 第 7 行: + +```ts +import { code, input, reset, size } from "../composables/code" +``` + +改为: + +```ts +import { code, format, input, reset, size } from "../composables/code" +``` + +- [ ] **Step 2: 新增 `handleFormat` 函数** + +在 `copy` 函数(第 20-23 行)之后新增: + +```ts +async function handleFormat() { + try { + await format() + message.success("代码已整理") + } catch (err: any) { + message.error( + `整理失败: ${err?.response?.data?.detail ?? err?.message ?? "未知错误"}`, + ) + } +} +``` + +- [ ] **Step 3: 在 actions 插槽中加入按钮** + +将模板中第 68-80 行的 `#actions` 插槽: + +```vue + +``` + +改为: + +```vue + +``` + +- [ ] **Step 4: 启动前后端,手动验证** + +```bash +cd /home/xuyue/Projects/Code/codeapinew && uv run python main.py +``` + +```bash +cd /home/xuyue/Projects/Code/codenext && npm run start +``` + +打开 `http://localhost:3000`,对每种语言验证: + +- Python:输入 `x=1\ny =2`,点击"整理代码",确认变为 `x = 1\ny = 2`,并提示"代码已整理" +- C:选择 C 语言,输入 `int main(){int x=1;return x;}`,点击"整理代码",确认大括号换行(Allman 风格)、缩进 4 空格 +- C++:同上,验证 `class Foo{};` 格式化结果 +- 海龟绘图:输入含 `t=1` 的代码,验证按 Python 格式化 +- 错误场景:输入语法错误的 Python(如 `def foo(:`),点击"整理代码",确认弹出错误提示且编辑器内容不变 + +- [ ] **Step 5: Commit** + +```bash +cd /home/xuyue/Projects/Code/codenext && git add src/desktop/CodeSection.vue && git commit -m "feat: add format code button to editor toolbar" +``` + +--- + +## Self-Review Notes + +- Spec coverage: `/format` 接口(Task 2-3)、依赖安装(Task 1)、前端按钮与调用链(Task 4-6)、错误处理(Task 3 的 400 响应 + Task 6 的 message.error)、海龟绘图复用 python 格式化(Task 2/3 测试覆盖)均已覆盖。移动端按设计明确排除,无需任务。 +- 类型一致性:`formatCode(code, language)` 在 `api.ts`、`composables/code.ts`、`formatter.py` 的 `format_code(code, language)` 参数顺序与命名一致;`FormatRequest`/`FormatResponse` 字段名 `code`/`language` 与前端请求体一致。