10

In Vim you can press . to repeat the latest change, including inserting text.

While in Emacs C-x z only repeats the last command. I am aware that macros can be used here, but more than often I realize that I need to re-insert the same text too late. When I need to re-insert it only one more time, defining a macro then is pointless.

Q: Is there a way to repeat the insertion of text inside of Emacs?

programking
  • 7,064
  • 9
  • 41
  • 62
ooi
  • 273
  • 1
  • 7
  • 1
    What is the "text" you want to repeat? Technically speaking, the last insertion is just the last character, which is probably not what you're after. So, how would you define a "text insertion" for your purpose? Clarifying it may also help you in finding ways to achieve it. – T. Verron Dec 02 '14 at 22:01
  • Hmm good question - how to define that _last continuous buffer modification_. Perhaps a buffer modification between two point moves which are not a result of a buffer modification (`C-f`, search, etc.)? If that makes any sense... – ooi Dec 02 '14 at 22:37
  • Clarify what you want with a recipe. There are many kinds of "insertion" (ways to insert text). Are you inserting interactively? If so, how? – Drew Dec 02 '14 at 22:59
  • Interactively. But ideally it wouldn't matter how or what. It's **as if I forgot to start defining a macro**. In which case the problem is _when_ is that. Maybe some [hook](http://www.gnu.org/software/emacs/manual/html_node/elisp/Change-Hooks.html) could be used. – ooi Dec 02 '14 at 23:35
  • I've been using Vim for too long - there is a clear distinction there - entering and leaving Insert mode (although `.` doesn't really repeat _everything_ done while in Insert mode). I'd prefer to keep Evil mode commented out. :) – ooi Dec 02 '14 at 23:39
  • 2
    How do you define 'last insert' in Emacs? Since there is no edit/command mode switch like in vi, how far in the history should we go? I usually just select "the last insert" (`C-space`, move the cursor, `M-w` to copy) and then `C-y` to paste as many time as needed. – Alain Dec 03 '14 at 00:00
  • [dot-mode](http://www.emacswiki.org/emacs/dot-mode.el) it pretty much what I had in mind. It uses `pre-command-hook`, `post-command-hook` and `after-change-functions` to "repeat sequences of commands and/or typing delimited by motion events". – ooi Dec 03 '14 at 18:16

3 Answers3

10

dot-mode is a "minor mode to repeat typing or commands," and may do what you need. It basically just repeats the last insertions/deletions that you do. It is pretty similar to . in Vi(m).

T. Verron
  • 4,233
  • 1
  • 22
  • 55
  • 2
    What _does_ it do? – Malabarba Dec 03 '14 at 00:00
  • 1
    Welcome to Emacs.SE. Right now, you're answer is closer to a comment than an answer. Could you please edit it to explain what `dot-mode` does and how it provides the functionality that OP is trying to get? – Dan Dec 03 '14 at 00:48
7

It's as if I forgot to start defining a macro

You could use kmacro-edit-lossage (C-x C-k l) for that: it allows you to view your 300 last keystrokes (including simple insertions), and edit them as a macro.

For simple usage, you would just locate the beginning of the insertion you want to repeat, delete all text from "Macro:" to that point, and press C-c C-c. Now you have a macro you can use to perform the same actions, as if you didn't forget to record it in the first place.

Sadly, this fails if the last 300 keystrokes include mouse clicks.

Edit: The following code changes it so that only the keys after the last mouse event are kept.

(defadvice recent-keys (after tv/recent-keys-no-mouse first ())
  "Return only the keys since the last mouse event"
  (let* ((vec ad-return-value)
         (lst (append vec nil))
         (nmax (length vec))
         (i (- nmax 1)))
    (while
        (and
         (>= i 0)
         (not
          (let ((mod (event-modifiers (elt vec i))))
            (or (memq 'click mod)
                (memq 'double mod)
                (memq 'triple mod)
                (memq 'drag mod)
                (memq 'down mod)))))
      (setq i (- i 1)))
    (setq ad-return-value (vconcat (nthcdr (+ i 1) lst) nil))))

(defun tv/kmacro-edit-lossage-no-mouse ()
  "Same as `kmacro-edit-lossage', but fallback if the keys contain mouse events.

Source: http://emacs.stackexchange.com/a/4071/184"
  (interactive)
  (ad-activate-regexp "tv/recent-keys-no-mouse")
  (call-interactively #'kmacro-edit-lossage)
  (ad-deactivate-regexp "tv/recent-keys-no-mouse"))

;; If you want this function to replace the default one, uncomment this line:
; (global-set-key (kbd "C-x C-k l") 'tv/kmacro-edit-lossage-no-mouse)
T. Verron
  • 4,233
  • 1
  • 22
  • 55
  • I do often scroll using the mouse, it would be cool to see how to remove those from macro. – ooi Dec 03 '14 at 18:19
  • Wow, this command would be useful if it didn't completely fail if the last 300 included mouse clicks. Maybe just don't include the clicks and let the user know instead of just refusing to work. – nanny Dec 03 '14 at 19:54
  • @nanny I added some elisp to enable a (imo) better fallback than just failing with an error message: with this function, only the keys after the last mouse event will be kept. Dropping the mouse events completely should be possible too, but the result of the macro would be somewhat unpredictable. – T. Verron Dec 03 '14 at 20:41
  • @ooi No idea if you got automatically pinged by the previous comment, so here is your ping too. – T. Verron Dec 03 '14 at 20:42
  • Thanks, I was working on the same thing. I think it may be possible to filter the args of `edmacro-fix-menu-commands`. It takes an optional argument which can ignore the mouse events. But I don't know enough about the advice system to get it to work. – nanny Dec 03 '14 at 20:44
  • @T.Verron Works great, thanks! [dot-mode](https://github.com/wyrickre/dot-mode) was closer answer to this question, but this deserves a lot of votes. Very useful and flexible. – ooi Dec 03 '14 at 21:00
  • @T.Verron It seems that after using a double-click we still get an error? – ooi Dec 03 '14 at 21:29
  • @ooi Oh, indeed. The manual page I read led me to believe a double click would be marked as both `'double` and `'click`, but apparently it is only `'double`. Easy to fix though. – T. Verron Dec 03 '14 at 22:25
4

I think dabbrev-expand function could do the trick. Let's suppose you've already typed some words: emacs, emotion, emoji, ... The next time you'll type a word beginning by 'em', you can hit M-/ and emacs will propose you to insert one of the already inserted words begining by 'em'. Keep hitting M-/ and emacs will show you 'emoji', then 'emotion, then ...

You may also have a look to repeat-complex-command. The popular example being: you want to repeat the command query-replace in many different buffers always using the same arguments, replace “pitt” by “patt”.

You run it once, go to a different window, and type C-x ESC ESC to run it again.

And to finish, may I suggest repeat-insert? This el file will help you to do insertion, using some kind of logic. It is more like, "reapeat last insertion, but for each iteration, the arguments will change.

This example could be found in the el file:

;; You can also iterate over lists:
;;   My desk is cluttered
;;   My desk is a real mess
;;   My desk is very hard to see
;;   My desk is buried
;;
;; from m-x insert-patterned
;;    ";;   My desk is %s"
;;    (cluttered "a real mess" "very hard to see" buried)

I hope that will help.

Nsukami _
  • 6,341
  • 2
  • 22
  • 35