mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
fix(keymap): Don't run the Enter that commits an input-method candidate
WebKit sends the Enter (or Space) that commits a candidate after compositionend, with isComposing already false and keyCode 229. Handlers that checked isComposing alone ran it: dialogs confirmed and shortcuts fired while typing Chinese or Japanese. isImeKey() checks both, and every keydown handler that skipped composition uses it.
This commit is contained in:
@@ -6,6 +6,7 @@ import { sshLabel } from "./model";
|
||||
import { SourceDialog } from "./SourceDialog";
|
||||
import { closeDialog, useDatabase } from "./store";
|
||||
import styles from "./Database.module.css";
|
||||
import { isImeKey } from "@/platform/ime";
|
||||
|
||||
const ObjectDialogs = lazyComponent(() => import("./ObjectDialogs").then((m) => m.ObjectDialogs));
|
||||
const ExportDialog = lazyComponent(() => import("./ExportDialog").then((m) => m.ExportDialog));
|
||||
@@ -138,7 +139,7 @@ function ParamsDialog({
|
||||
<div
|
||||
className={styles.form}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key !== "Enter" || e.nativeEvent.isComposing) return;
|
||||
if (e.key !== "Enter" || isImeKey(e.nativeEvent)) return;
|
||||
e.preventDefault();
|
||||
done(values);
|
||||
}}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { colorVar, connectRemedy, fileSourceName, newSourceId, parseSourceUrl, s
|
||||
import { closeDialog, loadDatabases, loadSchemas, saveSource, useDatabase } from "./store";
|
||||
import { VENDORS, sourceUrl, vendorById, vendorOf } from "./vendors";
|
||||
import styles from "./Database.module.css";
|
||||
import { isImeKey } from "@/platform/ime";
|
||||
|
||||
type Tab = "general" | "options" | "ssh" | "schemas" | "advanced";
|
||||
|
||||
@@ -284,7 +285,7 @@ function NamePicker({
|
||||
aria-label={`Add ${title} pattern`}
|
||||
onChange={(e) => setPattern(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && !e.nativeEvent.isComposing) {
|
||||
if (e.key === "Enter" && !isImeKey(e.nativeEvent)) {
|
||||
e.preventDefault();
|
||||
add();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import { closeBreakpointPopover, useOverlays } from "./popoverStore";
|
||||
import { VariablesView, type VarRoot } from "./VariablesView";
|
||||
import { pushHistory, readHistory } from "./expressionHistory";
|
||||
import styles from "./Overlays.module.css";
|
||||
import { isImeKey } from "@/platform/ime";
|
||||
|
||||
export function DebuggerOverlays() {
|
||||
return (
|
||||
@@ -710,7 +711,7 @@ function EvaluateDialogInner({ initial }: { initial: string }) {
|
||||
});
|
||||
};
|
||||
const onKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.nativeEvent.isComposing) return;
|
||||
if (isImeKey(e.nativeEvent)) return;
|
||||
const el = e.currentTarget;
|
||||
if (e.key === "Enter" && e.shiftKey && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
useMacros,
|
||||
type MacroStep,
|
||||
} from "./model";
|
||||
import { isImeKey } from "@/platform/ime";
|
||||
|
||||
/** Records typing and editor keys of every text editor while a macro records. */
|
||||
const recorder = [
|
||||
@@ -31,7 +32,7 @@ const recorder = [
|
||||
EditorView.domEventHandlers({
|
||||
keydown(e) {
|
||||
// Keys the IDE keymap ran as actions were recorded as those actions.
|
||||
if (e.defaultPrevented || e.isComposing || !useMacros.getState().recording) return false;
|
||||
if (e.defaultPrevented || isImeKey(e) || !useMacros.getState().recording) return false;
|
||||
const key = editorKeyName(e);
|
||||
if (key) recordStep({ kind: "key", key });
|
||||
return false;
|
||||
|
||||
@@ -57,6 +57,7 @@ import {
|
||||
widenSearchEverywhere,
|
||||
} from "./store";
|
||||
import styles from "./SearchEverywhere.module.css";
|
||||
import { isImeKey } from "@/platform/ime";
|
||||
|
||||
type SeScope = "allPlaces" | ProjectFileSet;
|
||||
|
||||
@@ -315,7 +316,7 @@ function SearchEverywhereDialog() {
|
||||
};
|
||||
|
||||
const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.nativeEvent.isComposing) return;
|
||||
if (isImeKey(e.nativeEvent)) return;
|
||||
if (e.key === "Tab") {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -33,6 +33,7 @@ import { trackRuntimeSettings } from "./runtime";
|
||||
import { installActionProxies, installSettingsHost } from "./window/bridge";
|
||||
import { AssignShortcutOverlay, showAssignShortcut } from "./keymap/AssignShortcut";
|
||||
import { settingsSearchEntries } from "./searchIndex";
|
||||
import { isImeKey } from "@/platform/ime";
|
||||
|
||||
export { settingsWindowParent } from "./window/bridge";
|
||||
export const SettingsWindow = lazyComponent(() => import("./window/SettingsWindow").then((m) => m.SettingsWindow));
|
||||
@@ -165,7 +166,7 @@ function listenLaunch(): () => void {
|
||||
/** Esc and ⌘W cancel, like closing the window; ⌘, stays here. */
|
||||
function installWindowKeys(): () => void {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.defaultPrevented || e.isComposing) return;
|
||||
if (e.defaultPrevented || isImeKey(e)) return;
|
||||
const mod = e.metaKey || e.ctrlKey;
|
||||
if ((e.key === "Escape" && !isPopupOpen()) || (mod && !e.altKey && !e.shiftKey && e.key.toLowerCase() === "w")) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -18,6 +18,7 @@ import { executeAction } from "@/platform/actions";
|
||||
import { effectiveBindings, strokeOf } from "@/platform/keymap";
|
||||
import { workflowSettings } from "@/platform/workflowSettings";
|
||||
import { terminalRuntime } from "./loader";
|
||||
import { isImeKey } from "@/platform/ime";
|
||||
|
||||
export type TerminalKeyResult =
|
||||
| { kind: "send"; data: string }
|
||||
@@ -128,7 +129,7 @@ export function isAllowedIdeShortcut(stroke: string, bindings: ReadonlyArray<rea
|
||||
export function installTerminalKeys(target: Window = window): () => void {
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
const rt = terminalRuntime();
|
||||
if (e.isComposing || !rt) return;
|
||||
if (isImeKey(e) || !rt) return;
|
||||
// The terminal find bar: Escape closes it (instead of "focus editor"), ⌘F reselects the query.
|
||||
if ((e.target as Element | null)?.closest?.("[data-terminal-find]")) {
|
||||
const plain = !e.ctrlKey && !e.altKey && !e.shiftKey;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* True while an input method is composing, including the key that ends it: WebKit sends the
|
||||
* Enter / Space that commits a candidate after `compositionend`, with `isComposing` already
|
||||
* false and keyCode 229, so a handler checking `isComposing` alone runs that Enter.
|
||||
*/
|
||||
export function isImeKey(e: KeyboardEvent): boolean {
|
||||
return e.isComposing || e.keyCode === 229;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import { intellijWindows } from "./keymaps/intellij-windows";
|
||||
import { vscodeMacos } from "./keymaps/vscode-macos";
|
||||
import { vscodeWindows } from "./keymaps/vscode-windows";
|
||||
import { usePlatform } from "./platformInfo";
|
||||
import { isImeKey } from "./ime";
|
||||
|
||||
export type Keymap = Record<string, string[]>;
|
||||
|
||||
@@ -439,7 +440,7 @@ export function installKeyboardDispatcher(target: Window = window, now: () => nu
|
||||
if (runHeldGesture(e)) return reset();
|
||||
resetTap();
|
||||
otherKeyAt = e.key === "Escape" || e.key === "Tab" ? -Infinity : now();
|
||||
if (e.isComposing) return;
|
||||
if (isImeKey(e)) return;
|
||||
const stroke = strokeOf(e);
|
||||
if (!stroke) return;
|
||||
const chord = pending.length > 0;
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ import { FOCUSABLE, onWindowDeactivate, openLayer, trapTab } from "./popupState"
|
||||
import { useTx } from "@/platform/i18n";
|
||||
import { selectableText } from "./selectableText";
|
||||
import styles from "./Dialog.module.css";
|
||||
import { isImeKey } from "@/platform/ime";
|
||||
|
||||
/**
|
||||
* `default`: filled accent button, triggered by Enter. `cancel`: Esc / close
|
||||
@@ -256,7 +257,7 @@ function DialogInner({
|
||||
onCloseRef.current();
|
||||
} else if (e.key === "Tab") {
|
||||
trapTab(e, ref.current);
|
||||
} else if (e.key === "Enter" && !e.defaultPrevented && !e.isComposing && !e.shiftKey && !e.altKey) {
|
||||
} else if (e.key === "Enter" && !e.defaultPrevented && !isImeKey(e) && !e.shiftKey && !e.altKey) {
|
||||
const t = e.target instanceof Element ? e.target : null;
|
||||
const mod = e.metaKey || e.ctrlKey;
|
||||
if (t && t !== document.body && !ref.current.contains(t)) return;
|
||||
|
||||
Reference in New Issue
Block a user