Files
OnlineJudge/docs/superpowers/specs/2026-06-14-code-format-design.md
yuetsh 1bbc149e55 Add design spec for pre-submit code auto-formatting
Plan: ruff for Python3, clang-format (LLVM/4-space/Attach) for C/C++,
called via new /api/format_code endpoint before submission.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 07:41:03 -06:00

133 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 提交前自动代码格式化 — Design Spec
**Date:** 2026-06-14
**Status:** Approved
---
## Overview
为 Python3、C、C++ 提供"提交前自动格式化"功能:用户点击"提交代码"时,前端先调用后端格式化接口
Python3 用 `ruff format`C/C++ 用 `clang-format`),将格式化后的代码写回编辑器(含 Yjs 协作同步)并提交。
其他语言Java/Go/JavaScript跳过格式化原样提交。无开关默认开启。
---
## Backend
### Endpoint
```
POST /api/format_code
```
- 放在 `submission` 应用,与 `SubmissionAPI` 同级(`submission/views/oj.py` / `submission/urls/oj.py` / `submission/serializers.py`
- 需要登录(`@login_required`),与提交代码一致
### Request
```json
{ "code": "...", "language": "python" }
```
- `language`: `ChoiceField`,取值 `"python"` / `"c"` / `"cpp"`(对应前端 `LANGUAGE_FORMAT_VALUE`
- `code`: `CharField(max_length=1024 * 1024)`,与 `CreateSubmissionSerializer.code` 一致
### 格式化实现
新增 `submission/utils.py`(或现有 utils 模块)中的 `format_code(code: str, language: str) -> str`
- `language == "python"`:
```python
subprocess.run(
["ruff", "format", "--stdin-filename", "code.py", "-"],
input=code, capture_output=True, text=True, timeout=5,
)
```
- `returncode != 0` → 视为语法错误,抛出携带 `stderr` 的异常view 返回 `err="format-error"`
- `language in ("c", "cpp")`:
```python
filename = "code.c" if language == "c" else "code.cpp"
subprocess.run(
["clang-format", f"-assume-filename={filename}",
"-style={BasedOnStyle: LLVM, IndentWidth: 4, BreakBeforeBraces: Attach}"],
input=code, capture_output=True, text=True, timeout=5,
)
```
- `returncode != 0` 或 subprocess 异常(超时等)→ view 返回 `err="server-error"`clang-format 正常情况下几乎不会因代码内容失败)
### Response
| 情况 | 响应 |
|---|---|
| 成功 | `self.success({"code": "<formatted code>"})` |
| Python 语法错误ruff format 非0 | `self.error(msg="<ruff stderr 摘要>", err="format-error")` |
| 工具执行异常(超时/二进制缺失/clang-format 非0 | `self.error(msg="format failed", err="server-error")` |
### 依赖与部署变更
- `pyproject.toml`: 将 `ruff` 从 `[dependency-groups].dev` 移到主依赖(`[project.dependencies]`),同步更新 `deploy/requirements.txt`
- `Dockerfile`: `apk add` 中新增 `clang-extra-tools`(提供 `clang-format` 二进制),放在常驻依赖区(不在 `apk del` 清理列表中)
---
## Frontend
### API
`ojnext/src/oj/api.ts` 新增:
```ts
export function formatCode(data: { code: string; language: string }) {
return http.post<{ code: string }>("format_code", data)
}
```
### 集成点:`ojnext/src/oj/problem/components/SubmitCode.vue`
在现有 Python 语法检测(`checkPythonSyntax`)之后、构建 `SubmitCodePayload` 之前插入:
```ts
const formatLang = LANGUAGE_FORMAT_VALUE[codeStore.code.language]
if (["python", "c", "cpp"].includes(formatLang)) {
try {
const res = await formatCode({
code: codeStore.code.value,
language: formatLang,
})
codeStore.setCode(res.data.code)
} catch (e: any) {
if (e?.error === "format-error") {
// 仅 Python3 会出现:代码本身有语法问题
message.warning(`代码格式化失败:${e.data},请检查代码后重试`)
return
}
// server-error / 网络异常:工具问题,静默降级,提交原代码
}
}
```
注:`http.ts` 拦截器会剥掉响应信封——成功时 `res` 即 `{error: null, data: {...}}`,失败时 `Promise.reject(res.data)`(即 `{error, data}`),因此用 `try/catch` 而非检查 `res.data.error`。
- `codeStore.setCode(...)` 更新 `code.value``SyncCodeEditor` 通过 `v-model` 绑定自动刷新编辑器显示并同步给协作者
- 提交时使用(可能已更新的)`codeStore.code.value` 构建 `SubmitCodePayload`,逻辑不变
- 不增加 loading 文案,格式化请求耗时很短,按钮状态保持原有的"正在提交"流程
---
## Error Handling Summary
| 失败类型 | Python3 | C/C++ |
|---|---|---|
| 格式化工具报代码语法错误(`format-error` | 提示用户检查代码,**阻止提交** | 不会出现clang-format 容错) |
| 工具/服务异常(`server-error`、网络错误、超时) | 静默降级,提交原代码 | 静默降级,提交原代码 |
---
## Out of Scope
- Java / Go / JavaScript 格式化(暂无对应工具集成)
- 格式化开关 / 用户偏好设置
- `CodeEditor.vue`(只读展示组件)不涉及