Skip to content

Commit b453e0a

Browse files
ystemsrxclaude
andcommitted
fix(editor): sync CodeMirror placeholder on language switch
Wrap the placeholder extension in a Compartment and reconfigure it when the prop changes, so switching language updates the editor hint without rebuilding the view. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 58238c0 commit b453e0a

1 file changed

Lines changed: 18 additions & 3 deletions

File tree

src/editor.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55
import { useEffect, useRef } from "react";
66
import { EditorView, basicSetup } from "codemirror";
7-
import { EditorState } from "@codemirror/state";
7+
import { Compartment, EditorState } from "@codemirror/state";
88
import { placeholder as placeholderExtension } from "@codemirror/view";
99
import { sql, PostgreSQL } from "@codemirror/lang-sql";
1010
import type { ERNodeModel, GraphLike, GraphNodeLike } from "./types";
@@ -29,6 +29,8 @@ export const CodeEditor = ({ value, onChange, placeholder }: CodeEditorProps) =>
2929
// 把最新的 onChange 装进 ref,避免在外部回调变化时重建 EditorView。
3030
const onChangeRef = useRef(onChange);
3131
onChangeRef.current = onChange;
32+
// 用 Compartment 包装 placeholder 扩展,便于语言切换时通过 reconfigure 热更新。
33+
const placeholderCompartmentRef = useRef(new Compartment());
3234

3335
useEffect(() => {
3436
if (!hostRef.current) return;
@@ -39,7 +41,9 @@ export const CodeEditor = ({ value, onChange, placeholder }: CodeEditorProps) =>
3941
basicSetup,
4042
sql({ dialect: PostgreSQL, upperCaseKeywords: false }),
4143
EditorView.lineWrapping,
42-
placeholderExtension(placeholder ?? ""),
44+
placeholderCompartmentRef.current.of(
45+
placeholderExtension(placeholder ?? ""),
46+
),
4347
EditorView.updateListener.of((update) => {
4448
if (update.docChanged) {
4549
onChangeRef.current(update.state.doc.toString());
@@ -54,10 +58,21 @@ export const CodeEditor = ({ value, onChange, placeholder }: CodeEditorProps) =>
5458
view.destroy();
5559
viewRef.current = null;
5660
};
57-
// 仅初次挂载初始化;后续 value 变化由下方 effect 同步。placeholder 静态
61+
// 仅初次挂载初始化;后续 value / placeholder 变化由下方 effect 同步。
5862
// eslint-disable-next-line react-hooks/exhaustive-deps
5963
}, []);
6064

65+
// placeholder 变化(如语言切换)时通过 Compartment 热替换扩展,避免重建编辑器。
66+
useEffect(() => {
67+
const view = viewRef.current;
68+
if (!view) return;
69+
view.dispatch({
70+
effects: placeholderCompartmentRef.current.reconfigure(
71+
placeholderExtension(placeholder ?? ""),
72+
),
73+
});
74+
}, [placeholder]);
75+
6176
// 外部 value 变化时同步进 doc。等值则跳过,避免 dispatch 把光标重置。
6277
useEffect(() => {
6378
const view = viewRef.current;

0 commit comments

Comments
 (0)