fix(editor): preserve IME composition in comments

This commit is contained in:
t8y2 committed 2026-06-11 22:55:16 +08:00
1 parent a017e04d4c
commit c9d655324d
3 files changed
+138 -8

No files matched your search

@@ -181,6 +181,8 @@ let semanticDiagnosticTimer: ReturnType<typeof setTimeout> | null = null;
let semanticDiagnosticRunId = 0;
let editorIsActive = true;
let tableReferenceDropListenerRegistered = false;
let imeCompositionActive = false;
let pendingImeModelEmit = false;
function editorThemeAppearance() {
return isDark.value ? "dark" : "light";
@@ -904,6 +906,7 @@ async function provideElasticsearchCompletions(currentState: import("@codemirror
}
async function provideSqlCompletions(currentState: import("@codemirror/state").EditorState, position: number, explicit: boolean) {
if (imeCompositionActive || view.value?.compositionStarted || view.value?.composing) return null;
if (!props.connectionId) return null;
const fullDoc = currentState.doc.toString();
if (props.databaseType === "elasticsearch") {
@@ -990,6 +993,23 @@ async function provideSqlCompletions(currentState: import("@codemirror/state").E
}
}
function isEditorComposing(currentView: EditorViewType): boolean {
return imeCompositionActive || currentView.compositionStarted || currentView.composing;
}
function flushImeComposition() {
const currentView = view.value;
if (!currentView || !pendingImeModelEmit) return;
pendingImeModelEmit = false;
emit("update:modelValue", currentView.state.doc.toString());
scheduleSemanticDiagnostics();
syncContextMenuState(currentView);
emit("selectionChange", selectedSqlFromView(currentView));
emit("cursorChange", currentView.state.selection.main.head);
latestSelection = readEditorSelection(currentView);
if (editorIsActive) emitEditorSelection(latestSelection);
}
function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number) {
if (!props.connectionId || props.database == null) return null;
const databaseNames = supportsDatabaseQualifierCompletion() && completionContext.suggestTables && !completionContext.insertTable ? connectionStore.lookupLocalCompletionDatabases(props.connectionId, completionContext.qualifier || completionContext.prefix, MAX_COMPLETION_TABLES) : [];
@@ -1547,14 +1567,19 @@ onMounted(async () => {
rectangularSelection({ eventFilter: (e: MouseEvent) => e.altKey || e.button === 1 }),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
emit("update:modelValue", update.state.doc.toString());
scheduleSemanticDiagnostics();
let insertedText = "";
update.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
insertedText += inserted.toString();
});
if (insertedText.endsWith(".")) {
startCompletion(update.view);
if (isEditorComposing(update.view)) {
pendingImeModelEmit = true;
completionEpoch++;
} else {
emit("update:modelValue", update.state.doc.toString());
scheduleSemanticDiagnostics();
let insertedText = "";
update.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
insertedText += inserted.toString();
});
if (insertedText.endsWith(".")) {
startCompletion(update.view);
}
}
}
if (update.selectionSet || update.docChanged) {
@@ -1586,6 +1611,16 @@ onMounted(async () => {
if (editorIsActive) emitEditorSelection(latestSelection);
return false;
},
compositionstart() {
imeCompositionActive = true;
completionEpoch++;
return false;
},
compositionend() {
imeCompositionActive = false;
window.setTimeout(flushImeComposition, 0);
return false;
},
wheel(event) {
if (!event.metaKey && !event.ctrlKey) return false;
event.preventDefault();
@@ -1736,6 +1771,7 @@ watch(
() => props.modelValue,
(val) => {
if (view.value && val !== view.value.state.doc.toString()) {
if (isEditorComposing(view.value)) return;
view.value.dispatch({
changes: { from: 0, to: view.value.state.doc.length, insert: val },
});