diff --git a/src/latex_suite.ts b/src/latex_suite.ts index 96b8d6f..8e48e40 100644 --- a/src/latex_suite.ts +++ b/src/latex_suite.ts @@ -14,7 +14,7 @@ import { clearSnippetQueue } from "./snippets/codemirror/snippet_queue_state_fie import { handleUndoRedo } from "./snippets/codemirror/history"; import { handleMathTooltip } from "./editor_extensions/math_tooltip"; -import { isComposing } from "./utils/editor_utils"; +import { isComposing, forceEndComposition } from "./utils/editor_utils"; export const handleUpdate = (update: ViewUpdate) => { const settings = getLatexSuiteConfig(update.state); @@ -28,7 +28,38 @@ export const handleUpdate = (update: ViewUpdate) => { handleUndoRedo(update); } +let lastKeyboardEvent: KeyboardEvent | null = null; +let useNextTextInput = false; + +export const onInput = (view: EditorView, from: number, to: number, text: string) => { + if (text === "\0\0") { + return true; + } + if (text.length == 1 && useNextTextInput) { + const success = handleKeydown( + text, + lastKeyboardEvent.shiftKey, + lastKeyboardEvent.ctrlKey || lastKeyboardEvent.metaKey, + isComposing(view, lastKeyboardEvent), + view + ); + if (success) { + forceEndComposition(view); + return true; + } + } +} + export const onKeydown = (event: KeyboardEvent, view: EditorView) => { + // the input event handler `onInput` will try to handle the unknown key. + if (event.key == "Unidentified" || event.key == "Process" || event.key == "Dead") { + useNextTextInput = true; + lastKeyboardEvent = event; + return; + } else { + useNextTextInput = false; + } + const success = handleKeydown(event.key, event.shiftKey, event.ctrlKey || event.metaKey, isComposing(view, event), view); if (success) event.preventDefault(); diff --git a/src/main.ts b/src/main.ts index c64b25c..72dfe50 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,7 @@ import { ICONS } from "./settings/ui/icons"; import { getEditorCommands } from "./features/editor_commands"; import { getLatexSuiteConfigExtension } from "./snippets/codemirror/config"; import { SnippetVariables, parseSnippetVariables, parseSnippets } from "./snippets/parse"; -import { handleUpdate, onKeydown } from "./latex_suite"; +import { handleUpdate, onKeydown, onInput } from "./latex_suite"; import { EditorView, tooltips } from "@codemirror/view"; import { snippetExtensions } from "./snippets/codemirror/extensions"; import { mkConcealPlugin } from "./editor_extensions/conceal"; @@ -163,6 +163,7 @@ export default class LatexSuitePlugin extends Plugin { this.editorExtensions.push([ getLatexSuiteConfigExtension(this.CMSettings), Prec.highest(EditorView.domEventHandlers({ "keydown": onKeydown })), + Prec.highest(EditorView.inputHandler.of(onInput)), EditorView.updateListener.of(handleUpdate), snippetExtensions, ]); diff --git a/src/utils/editor_utils.ts b/src/utils/editor_utils.ts index 1cdc94d..b33f15c 100644 --- a/src/utils/editor_utils.ts +++ b/src/utils/editor_utils.ts @@ -139,3 +139,38 @@ export function isComposing(view: EditorView, event: KeyboardEvent): boolean { // Note that keyCode is deprecated - it is used here because it is apparently the only way to detect the first keydown event of an IME composition. return view.composing || event.keyCode === 229; } + +/** + * Force end an IME composition. + * + * MIT License + * Copyright (C) 2018-2021 by Marijn Haverbeke and others + */ +export function forceEndComposition(view: EditorView) { + let parent = view.scrollDOM.parentElement; + if (!parent) return; + + let sibling = view.scrollDOM.nextSibling; + let selection = window.getSelection(); + let savedSelection = selection && { + anchorNode: selection.anchorNode, + anchorOffset: selection.anchorOffset, + focusNode: selection.focusNode, + focusOffset: selection.focusOffset + }; + + view.scrollDOM.remove(); + parent.insertBefore(view.scrollDOM, sibling); + try { + if (savedSelection && selection) { + selection.setPosition(savedSelection.anchorNode, savedSelection.anchorOffset); + if (savedSelection.focusNode) { + selection.extend(savedSelection.focusNode, savedSelection.focusOffset); + } + } + } catch(e) { + console.error(e); + } + view.focus(); + view.contentDOM.dispatchEvent(new CustomEvent("compositionend")); +} \ No newline at end of file