11

I have the following

(defun isearch-del-fail-or-char ()
  "Delete failed isearch text, or if there is none, a single character."
  (interactive)
  (if (isearch-fail-pos)
      (delete-region (isearch-fail-pos) (point))
    (isearch-del-char)))

(define-key isearch-mode-map (kbd "DEL") 'isearch-del-fail-or-char)

The purpose of the code is to make delete in isearch delete the entire failed string (or if there is no failed string just a single character).

However, delete-region is deleting text from the buffer isearch is searching in, not the isearch buffer itself.

What is the proper way to do this? Critique on the rest of my emacs lisp style is also welcome :)

12

Ah yes. Isearch reads the keys you type, looks them up in isearch-mode-map, and invokes them in the current buffer.

Isearch does not, in spite of appearances, use the minibuffer. It uses the echo area. That is, what you see there is actually output messages, including echoes of the characters you type.

This should do what you ask:

(defun mydelete ()
  "Delete the failed portion of the search string, or the last char if successful."
  (interactive)
  (with-isearch-suspended
      (setq isearch-new-string
            (substring
             isearch-string 0 (or (isearch-fail-pos) (1- (length isearch-string))))
            isearch-new-message
            (mapconcat 'isearch-text-char-description isearch-new-string ""))))

(define-key isearch-mode-map (kbd "DEL") 'mydelete)

(BTW, your question says the delete character, but you wrote DEL, which is the backspace character.)


As @Malabarba points out in a comment, when you set the new search string to "" (empty string), with-isearch-suspended resumes by searching for the last search string, instead starting with an empty search string.

This is a "feature" of with-isearch-suspended, in general. But because you sometimes might really want to empty the search string for resumption, in the version of with-isearch-suspended in isearch+.el I've added variable isearchp-if-empty-prefer-resuming-with-last, to control this. If you bind that to nil and you set isearch-new-string to "" then search resumes with an empty search string.

So with Isearch+ you can do what you want with this definition:

(defun mydelete ()
  "Delete the failed portion of the search string, or the last char if successful."
  (interactive)
  (let ((isearchp-if-empty-prefer-resuming-with-last  nil))
    (with-isearch-suspended
        (setq isearch-new-string
              (substring
               isearch-string 0 (or (isearch-fail-pos) (1- (length isearch-string))))
              isearch-new-message
              (mapconcat 'isearch-text-char-description isearch-new-string "")))))

I notice too now that Emacs 24.4 introduced a regression, which I've filed Emacs bug #20466 for, which means that binding DEL in isearch-mode-map is not sufficient. They added a separate binding for <backspace>, in addition to one for DEL. That means that <backspace> no longer gets translated to DEL, for Isearch (but it does still get so translated for Emacs generally).

So if you want the Backspace key to do what you asked in Emacs 24.4 or later then you cannot just bind DEL to mydelete. You need to bind <backspace> to mydelete. Dumb, AFAICT, mais on n'arrete pas le progres...


I've added a similar command to Isearch+ and bound it to C-M-l (the same key used to remove a completion mismatch in Icicles).

Be aware too that C-g in Isearch will also, when there is a mismatch, remove the mismatched text. (But C-g also has an effect when search is successful.)


I should have mentioned that Isearch+ has also had an optional behavior along similar lines for quite a while now. M-k during Isearch toggles among 3 behaviors, which are controlled by the value of option isearchp-drop-mismatch:

  • replace-last - Your current input replaces the last mismatched text. You can always see your last input, even if it is a mismatch. And it is available for editing using M-e.

  • nil - Your current input is appended, even if the previous input has a mismatched portion.

  • anything else - Your current input is ignored (removed) if it causes a mismatch. The search string always has successful matches.

  • BTW, I've thought about adding a similar command to Isearch+ ever since I added highlighting of the failed portion (years ago, before I added it to GNU Emacs), but I never got around to it. I've done that now. (I didn't include deletion of a character, however. The command I added is a no-op if there is no failed portion.) – Drew Mar 27 '15 at 21:54
  • I'm on OS X. I meant backwards delete. – asmeurer Mar 30 '15 at 18:26
  • The isearch+ replace-last behavior looks pretty nice. Is any way to make it not ring the bell every time the search fails, though? – asmeurer Mar 30 '15 at 23:23
  • 2
    I uploaded a new version of isearch+.el. It (a) fixes automatic mismatch removal so that wraparound works OK. And it adds an option, isearchp-ring-bell-function, which you can use to suppress the bell during search. – Drew Mar 31 '15 at 16:31
  • 3
    Malabarba, interesting. I took a shot at the bug and ended up with this. Mostly cargo-culted from isearch-del-char and Drew's answer but it seems to work as intended. The with-isearch-suspended macro was the culprit. – jbm Apr 30 '15 at 2:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.