Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 9 additions & 25 deletions Sources/GhosttyNSView+IMEComposition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ extension GhosttyNSView {
// events out of the terminal so keys such as Down can open
// candidates instead of moving the shell cursor.
guard textInputHandledEvent, isInputMethodSource(inputSourceId) else { return false }
return shouldKeepNoMarkedIMECommandInsideTextInput(event)
&& !shouldAllowDeferredNumpadIMEFallback(event)
return !shouldAllowDeferredNumpadIMEFallback(event)
}

if before.text != after.text {
Expand Down Expand Up @@ -86,27 +85,6 @@ extension GhosttyNSView {
return flags == [.numericPad]
}

func hasOnlyTextInputCommandModifiers(_ event: NSEvent) -> Bool {
let flags = event.modifierFlags
.intersection(.deviceIndependentFlagsMask)
.subtracting([.numericPad, .function, .capsLock])
return flags.isEmpty || flags == [.shift]
}

func shouldKeepNoMarkedIMECommandInsideTextInput(_ event: NSEvent?) -> Bool {
guard let event else { return false }
guard hasOnlyTextInputCommandModifiers(event) else { return false }

switch Int(event.keyCode) {
case kVK_LeftArrow, kVK_RightArrow, kVK_UpArrow, kVK_DownArrow,
kVK_PageUp, kVK_PageDown, kVK_Home, kVK_End,
kVK_Space:
return true
default:
return false
}
}

func isTraditionalZhuyinInputSource(_ sourceId: String?) -> Bool {
guard let sourceId else { return false }
return sourceId.localizedCaseInsensitiveContains("TCIM.Zhuyin")
Expand Down Expand Up @@ -147,7 +125,10 @@ extension GhosttyNSView {
return false
}

guard hasOnlyTextInputCommandModifiers(event) else { return false }
let flags = event.modifierFlags
.intersection(.deviceIndependentFlagsMask)
.subtracting([.numericPad, .function, .capsLock])
guard flags.isEmpty || flags == [.shift] else { return false }
Comment on lines +128 to +131

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Duplicated modifier-flag computation

The hasOnlyTextInputCommandModifiers helper that was removed alongside shouldKeepNoMarkedIMECommandInsideTextInput contained the modifier-flag computation now inlined verbatim into both shouldRememberZhuyinCandidateInteraction (line 128–131) and shouldKeepIMECompositionCommandInsideTextInput (line 158–161). If the set of stripped modifiers ever needs to change, two sites must be updated in sync. Extracting this as a private helper would keep the rule in one place.


switch Int(event.keyCode) {
case kVK_DownArrow, kVK_UpArrow, kVK_PageUp, kVK_PageDown, kVK_Space:
Expand All @@ -174,7 +155,10 @@ extension GhosttyNSView {
/// Returns true for active-composition command keys that belong to AppKit's
/// text input manager even when marked text itself does not change.
func shouldKeepIMECompositionCommandInsideTextInput(_ event: NSEvent) -> Bool {
guard hasOnlyTextInputCommandModifiers(event) else { return false }
let flags = event.modifierFlags
.intersection(.deviceIndependentFlagsMask)
.subtracting([.numericPad, .function, .capsLock])
guard flags.isEmpty || flags == [.shift] else { return false }

switch Int(event.keyCode) {
case kVK_LeftArrow, kVK_RightArrow, kVK_UpArrow, kVK_DownArrow,
Expand Down
66 changes: 0 additions & 66 deletions cmuxTests/CJKIMEMarkedSelectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -651,72 +651,6 @@ final class CJKIMEMarkedSelectionTests: XCTestCase {
)
}

func testZhuyinReturnForwardsToTerminalAfterCompositionIsCleared() throws {
let hostedTerminal = try makeHostedTerminalWindow()
let terminalSurface = hostedTerminal.surface
let window = hostedTerminal.window
let surfaceView = hostedTerminal.surfaceView
let previousKeyEventObserver = GhosttyNSView.debugGhosttySurfaceKeyEventObserver
let previousInputSourceOverride = KeyboardLayout.debugInputSourceIdOverride
let previousInterpretHook = cjkIMEInterpretKeyEventsHook
defer {
GhosttyNSView.debugGhosttySurfaceKeyEventObserver = previousKeyEventObserver
KeyboardLayout.debugInputSourceIdOverride = previousInputSourceOverride
cjkIMEInterpretKeyEventsHook = previousInterpretHook
window.orderOut(nil)
withExtendedLifetime(terminalSurface) {}
}

KeyboardLayout.debugInputSourceIdOverride = "com.apple.inputmethod.TCIM.Zhuyin"
surfaceView.setMarkedText(
"ㄋ",
selectedRange: NSRange(location: 1, length: 0),
replacementRange: NSRange(location: NSNotFound, length: 0)
)
surfaceView.unmarkText()
XCTAssertFalse(surfaceView.hasMarkedText())

installCJKIMEInterpretKeyEventsSwizzle()
cjkIMEInterpretKeyEventsHook = { candidateView, events in
guard candidateView === surfaceView,
let event = events.first,
Int(event.keyCode) == kVK_Return else {
return false
}
return true
}

var forwardedText: [String] = []
var forwardedPressKeyCodes: [UInt32] = []
GhosttyNSView.debugGhosttySurfaceKeyEventObserver = { keyEvent in
previousKeyEventObserver?(keyEvent)
guard keyEvent.action == GHOSTTY_ACTION_PRESS else { return }
if let text = keyEvent.text {
forwardedText.append(String(cString: text))
} else {
forwardedPressKeyCodes.append(keyEvent.keycode)
}
}

let event = try keyEvent(
text: "\r",
keyCode: UInt16(kVK_Return),
windowNumber: window.windowNumber
)

window.makeFirstResponder(surfaceView)
withExtendedLifetime(terminalSurface) {
surfaceView.keyDown(with: event)
}

XCTAssertEqual(forwardedText, [])
XCTAssertEqual(
forwardedPressKeyCodes,
[UInt32(kVK_Return)],
"Plain Return after Zhuyin composition is cleared must execute in the terminal"
)
}

func testDoesNotSuppressNonInputMethodDeadKeyCommandWhenMarkedTextIsUnchanged() throws {
let view = GhosttyNSView(frame: .zero)
let event = try keyEvent(
Expand Down
Loading