From 533f12e8518562a0ee84f42502ab4069a5b61c97 Mon Sep 17 00:00:00 2001 From: xfy Date: Wed, 8 Jul 2026 13:04:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(editor):=20=E4=BF=AE=E5=A4=8D=20CodeMirror?= =?UTF-8?q?=20=E6=8A=98=E5=8F=A0=E5=9B=BE=E6=A0=87=E5=9E=82=E7=9B=B4?= =?UTF-8?q?=E4=B8=8D=E5=B1=85=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 折叠三角 (▾/▸) 在每行行框里偏低,根因有二: 1. 图标字符本身基线不稳。codemirror 包的 basicSetup 调 foldGutter() 用默认 openText "⌄" (U+2304「下花括号」),这是个基线/字形表现不稳定的符号字符, 在多数 mono 字体里墨迹落在字符框底部,三角明显偏低。本地内联一份 basicSetup (CodeMirror 官方推荐的定制方式),改用几何形状块字符 ▾ (U+25BE) / ▸ (U+25B8),字形本身在字符框内居中;同时把 foldGutter() 展开后丢弃的 @codemirror/{lint,search} 显式声明为依赖。 2. core 没给图标 设 vertical-align/display。baseTheme 只给 .cm-foldGutter span 设了 padding/cursor,图标默认按基线贴在格子底部。 themes.ts 加一条 flex 居中规则,把 .cm-foldGutter .cm-gutterElement 变成 flex 容器,三角稳稳落在行框中央。 文章页 CodeRunner 与后台 SQL 控制台共用同一份 editor.js,一处修复两处生效。 --- libs/codemirror-editor/package.json | 2 + libs/codemirror-editor/src/editor.ts | 79 +++++++++++++++++++++++++++- libs/codemirror-editor/src/themes.ts | 23 +++++++- libs/pnpm-lock.yaml | 6 +++ 4 files changed, 107 insertions(+), 3 deletions(-) diff --git a/libs/codemirror-editor/package.json b/libs/codemirror-editor/package.json index 927a0c8..fceef8e 100644 --- a/libs/codemirror-editor/package.json +++ b/libs/codemirror-editor/package.json @@ -18,6 +18,8 @@ "@codemirror/lang-python": "^6.2.0", "@codemirror/lang-sql": "^6.10.0", "@codemirror/language": "^6.11.0", + "@codemirror/lint": "^6.9.7", + "@codemirror/search": "^6.7.1", "@codemirror/state": "^6.5.0", "@codemirror/view": "^6.36.0", "@replit/codemirror-vim": "^6.3.0", diff --git a/libs/codemirror-editor/src/editor.ts b/libs/codemirror-editor/src/editor.ts index c0e06d3..feb4a44 100644 --- a/libs/codemirror-editor/src/editor.ts +++ b/libs/codemirror-editor/src/editor.ts @@ -1,12 +1,87 @@ +import { + autocompletion, + closeBrackets, + closeBracketsKeymap, + completionKeymap, +} from '@codemirror/autocomplete'; +import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'; import { javascript } from '@codemirror/lang-javascript'; import { python } from '@codemirror/lang-python'; import { PostgreSQL, sql } from '@codemirror/lang-sql'; +import { + bracketMatching, + codeFolding, + defaultHighlightStyle, + foldGutter, + foldKeymap, + indentOnInput, + syntaxHighlighting, +} from '@codemirror/language'; +import { lintKeymap } from '@codemirror/lint'; +import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'; import { Compartment, EditorState, type Extension, Prec } from '@codemirror/state'; -import { EditorView, keymap } from '@codemirror/view'; +import { + crosshairCursor, + drawSelection, + dropCursor, + EditorView, + highlightActiveLine, + highlightActiveLineGutter, + highlightSpecialChars, + keymap, + lineNumbers, + rectangularSelection, +} from '@codemirror/view'; import { vim } from '@replit/codemirror-vim'; -import { basicSetup } from 'codemirror'; import { type ThemeName, themeExtension } from './themes'; +/** + * 本地版 basicSetup:与 `codemirror` 包同名的便利数组成员完全一致,唯一差别是 + * 折叠图标换成几何三角形字符。 + * + * 原因:`codemirror` 的 basicSetup 调 `foldGutter()` 用默认 `openText: "⌄"` + * (U+2304 DOWNWARDS HARPOON WITH BARB… 实为「下花括号」),这是个基线/字形表现 + * 不稳定的符号字符——在多数 mono 字体里墨迹落在字符框底部,导致折叠三角在行框里 + * 明显偏低。改成 `▾` (U+25BE BLACK DOWN-POINTING SMALL TRIANGLE) / `▸` + * (U+25B8 BLACK RIGHT-POINTING SMALL TRIANGLE) 两个「几何形状」块字符,字形稳定 + * 居中,配合 themes.ts 的 flex 居中后,三角精准落在每行行框中央。 + * + * 按 CodeMirror 官方建议:basicSetup 「does not allow customization」,需要定制时 + * 「copy this package's source ... and adjust as desired」,此处即采用此法。 + */ +const basicSetup: Extension[] = [ + lineNumbers(), + highlightActiveLineGutter(), + highlightSpecialChars(), + history(), + foldGutter({ + openText: '▾', + closedText: '▸', + }), + drawSelection(), + dropCursor(), + EditorState.allowMultipleSelections.of(true), + indentOnInput(), + syntaxHighlighting(defaultHighlightStyle, { fallback: true }), + bracketMatching(), + closeBrackets(), + autocompletion(), + rectangularSelection(), + crosshairCursor(), + highlightActiveLine(), + highlightSelectionMatches(), + keymap.of([ + ...closeBracketsKeymap, + ...defaultKeymap, + ...searchKeymap, + ...historyKeymap, + ...foldKeymap, + ...completionKeymap, + ...lintKeymap, + ]), + codeFolding(), +]; + /** SQL 补全用 schema 数据(由 Rust 侧从实时库拉取注入)。 */ export interface SqlSchema { tables: { name: string; columns: string[] }[]; diff --git a/libs/codemirror-editor/src/themes.ts b/libs/codemirror-editor/src/themes.ts index 563c894..f04bdd9 100644 --- a/libs/codemirror-editor/src/themes.ts +++ b/libs/codemirror-editor/src/themes.ts @@ -28,8 +28,29 @@ const gutterBackgroundOverride: Extension = EditorView.theme({ }, }); +/** + * 修复 CodeMirror 折叠图标(fold gutter 的 ▾ / ▸)垂直不居中。 + * + * core 的 baseTheme(@codemirror/language)只给 `.cm-foldGutter span` 设了 + * `padding`/`cursor`,没设 `display`/`vertical-align`。折叠图标是个 , + * 装在 `.cm-gutterElement` 里(高度被 core 固定为行高的格子),默认按基线对齐, + * 图标贴在格子底部而非中央。 + * + * editor.ts 已把图标字符从基线不稳的 `⌄`(U+2304)/`›`(U+203A) 换成几何三角形 + * `▾`(U+25BE)/`▸`(U+25B8)——字形本身在字符框内居中;再配合这里的 flex 居中, + * 三角稳稳落在行框中央。只作用到 `.cm-foldGutter`,不影响行号列(行号文本仍按 + * 基线,正常可读)。 + */ +const foldGutterCenterOverride: Extension = EditorView.theme({ + '.cm-foldGutter .cm-gutterElement': { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, +}); + /** 根据主题名返回对应的 CodeMirror 主题 Extension。 */ export function themeExtension(name: ThemeName): Extension { const catppuccin = name === 'light' ? catppuccinLatte : catppuccinMocha; - return [catppuccin, gutterBackgroundOverride]; + return [catppuccin, gutterBackgroundOverride, foldGutterCenterOverride]; } diff --git a/libs/pnpm-lock.yaml b/libs/pnpm-lock.yaml index 6f04c19..87a77e9 100644 --- a/libs/pnpm-lock.yaml +++ b/libs/pnpm-lock.yaml @@ -47,6 +47,12 @@ importers: '@codemirror/language': specifier: ^6.11.0 version: 6.12.4 + '@codemirror/lint': + specifier: ^6.9.7 + version: 6.9.7 + '@codemirror/search': + specifier: ^6.7.1 + version: 6.7.1 '@codemirror/state': specifier: ^6.5.0 version: 6.7.0