Apple recently broke Ctrl-Y

Ever since MacOS 10.0 was released, text fields have supported Emacs bindings for the single-character Control commands, showing that there were people inside Apple (and, presumably, NeXT) who were right-thinking and sensible.

But some time late in the 10.6 release cycle, and continuing into 10.7.1, the binding for Ctrl-K ("kill to end of line") became broken.

I care deeply about this, because these commands have been burned into my hands' muscle memory since dinosaurs walked the earth, and every time it screws up, my brain basically falls over.

Proper behavior for "Kill" is this:

  1. If the last command was not "kill", truncate the kill buffer;
  2. Append the deleted text to the kill buffer.

Specifically, cursor motion, self-inserting characters, and basically everything else takes it out of kill-appends mode. The only time kills should append is if you type ^K twice in a row with no other intervening typing or motion.

This is how it used to work in 10.5.8 (I just tried it) and, I think, at least part of the way through the 10.6.x series. But it changed for some reason!

From my experiments, it seems that what Apple currently does is this:

Kill:

  1. If "no more kills" flag is set, truncate the kill buffer.
  2. Append the deleted text to the kill buffer.

Yank:

  1. Set "no more kills" flag.
  2. Insert contents of kill buffer.

That's wrong. Proper behavior is that if you kill text, move, kill more text, and yank, you get only the second text inserted. Current Mac behavior is that you get both pieces of text inserted -- in fact, all text that was killed since the last yank!

To demonstrate, type this into any text area (e.g., a Safari page with a form):

    one ^A ^K ^Y RET
    two RET
    three RET
    four RET
    five RET

Then position the cursor at the beginning of the "two" line, and type this:

    ^K ^K ^N ^K ^K ^N ^Y

You should now be looking at:

    one
    three
    five
    four

But instead you have:

    one
    three
    five
    two
    four

Is there any way for me to fix this myself? It appears that the key bindings are defined in /System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict (a normal plist file) and available commands are listed in /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSResponder.h, but I'm guessing there's no easy way to replace the definition of the deleteToEndOfParagraph: action with something else...


Update: For what it's worth (and for those who can read it) Apple Bug 10532310.


Update: Holy shit! I am told that this bug was fixed FIVE HOURS after I posted this -- Webkit bug 73888 and the patch. Thanks, Alexey!

This right here, people, is a grand triumph of open source.

Maybe the bug would have gotten the same kind of swift attention inside Apple (Alexey does work for Apple, after all) but none of us would have known that it had even been acknowledged until it showed up in a release, due to Apple's unconditional secrecy on all things. But because they do Webkit development out in the open, we all get to follow along! Which is awesome.

Of course I still have to wait who-knows-how-long until this fix is rolled into a build of Safari that I'm able to run, instead of being able to just spin my own or download a nightly like for normal software, so that's not ideal, but... baby steps.


Update 3, Aug 2012: The fix is in the Safari 6 release. So the answer is, "eight months".

Tags: , , , ,

33 Responses:

  1. Grant Paul (chpwn) says:

    You could investigate using SIMBL (http://www.culater.net/software/SIMBL/SIMBL.php) and creating a bundle that overrides -deleteToEndOfParagraph: with a custom implementation, like I do here: http://pastie.org/private/nbaaog7hptyzsytahpc5a

  2. Perry says:

    I have no idea how to fix this, but I'm also desperately interested in the answer (and indeed, how to improve the emacs compatibility in general). After 28 years of using emacs all day, my fingers are no longer capable of using other bindings.

  3. Cobbal says:

    Oddly enough, I still get the correct behavior for most applications (TextEdit, etc.) but I get the wrong behavior in Chrome and Safari. (Firefox doesn't seem to know how to yank)

    It's possible this is a webkit bug.

    • Yes, it seems to work in TextEdit. I sense WebKit trickery afoot!

      • Charles Choi says:

        Believe Charles Stephens is correct in his diagnosis. Just tried this with Mail.app on 10.7.2 and ^Y is working as it should. This is a Webkit bug.

        • jwz says:

          Oh, geez! You're right, works in Mail, fails in Safari.

          Two different libraries to implement text-field editing -- because, you know, that's somewhere you might want to diverge and innovate.

          And people say Jobs ran a tight ship. Pffff.

          • Charles Choi says:

            To be fair, emacs binding support was put into the NextStep/Cocoa text field libs, whereas my understanding on Webkit was that it was initially developed outside of Apple and architected to be multi-platform. I doubt if Jobs even knew those bindings were there, but I for one am super thankful that the legacy of those NextStep devs still remains every time I use a Cocoa app today.

          • Further, I grep the Safari 5.1 branch and trunk for deleteToEndOfParagraph: and see that it's doing something with that binding. However, what is unknown since it's wrapped in a twisty maze with no hope of escape consisting of macros and Objective-C++ and various project specific bits (Safari vs. Chrome vs. the others).

            The WebKit people are reachable through the Interwebs. Oh, and they meet in person, perhaps you can invite them over to the DNA Lounge and don't let them out until you get a patch. I'm not liable for that option.

            cfs

    • Cobbal says:

      Ah, it seems to be a known issue in Chrome at least

      http://code.google.com/p/chromium/issues/detail?id=99885

      • I would still recommend jwz file a bug report so Apple snags the eventual fix into Safari. WebKit nightly source code is huuuuge. I'll download it when I get home and see what changed around Safari 4 to 5 with regards to deleteToEndOfParagraph: handling.

      • jwz says:

        It is kind of awesome that his test-case is almost exactly the same as mine.

    • lowell says:

      Firefox doesn't use the App Kit for its text system or WebKit for its web views (hence Camino), so it shouldn't know how to yank unless it's something it does in BSD, Linux and/or Windows.

  4. You are right, there isn't any convenient way to replace the actions in the Appkit framework. You can remap keys by creating ~/Library/KeyBindings/DefaultKeyBinding.dict and adding keycode = NSResponder_action, but not the action itself. I recommend filing a bug report. Let us know what it is so others can "me too".
    Example:

    {
    "^k" = deleteToEndOfParagraph:;
    }
    cfs

  5. MikeB says:

    In TextEdit it seems to work still

  6. Patrick says:

    And I can't remember the specifics, but poking around in KeyBinding.dict ends up with highly irregular results. I seem to remember that the end result is that some input fields respect your wishes, and others are utterly broken. ISTR that I was using it to map home and end to ^a and ^e, and PgDn to ^v and while I could still scroll in Safari form fields, I couldn't use them as page motion keys outside of form fields.

    It's as though Apple isn't actually testing this stuff. *cough*

    • Patrick says:

      Actually, I should clarify that all the weirdness was tied to Safari so it might just be that Safari's cross platform ways means it doesn't use the input routines that the rest of the OS does, but I didn't investigate it thoroughly.

  7. Michael Abed says:

    You can in theory set per-user keyboard shortcuts for the text fields. There's a ton of info about the system that does this here.

    • jwz says:

      A) That's from 2006, so it actually doesn't work that way any more, and
      B) It still only lets you bind keys to (sequences of) predefined commands, so doesn't let me fix this bug, even if it still worked that way, which it doesn't.
      (Yes, I also googled it.)

  8. Andrew Taumoefolau says:

    Not sure if you're aware, but nightly builds of WebKit are available here:

    http://nightly.webkit.org/

  9. Viswakarma says:

    Why don't you be more specific that your problem is related to the use of EMACS in Terminal?

  10. John says:

    What's worse is that NotationalVelocity 2 and TextMate 2 both mess up this behavior. NV completely takes over ^k for something else, and textmate doesn't do subsequent line kills for some reason, it only takes the current line and not the newline. grrrr