Is there a apply-function-to-region-lines in emacs

2019-04-07 10:50发布

问题:

A lot of my work involves searching and deleting unnecessary lines of code. So I create a macro, and then select all lines (C-x h) and then run the command (apply-macro-to-region-lines). I managed to save that command and placed it in my .emacs file; I called it cut_it_now. But now my function is not a macro anymore, so I can't use the (apply-macro-to-region-lines) function anymore. Do you know if there is (apply-function-to-region-lines) implemented somewhere?

Many thanks,

D

回答1:

I agree with @Lindydancer's answer, and I'd also add that there might be an easier way to accomplish your goal. e.g. the built-in function delete-matching-lines. :-)



回答2:

Note that you can still use apply-macro-to-region-lines with a macro generated from code, provided the macro is defined as a vector or string. With a custom apply-named-macro-to-region-lines[2], you can select the macro to use interactively.

Emacs has two ways of generating code from a keyboard macro, depending upon the method used to name it.

If you use kmacro-name-last-macro (bound to C-xC-kn), then Emacs generates a function from the macro, which is not directly useful for this particular purpose [1].

If you use name-last-kbd-macro to name your macro, it will be generated as a vector or string.

In either case, you then use insert-kbd-macro to obtain the code.

In fact the vector/string format is the default, so you could bypass the naming step and immediately ask for the code (typing RET at the name prompt to indicate the most recently-defined macro), and then manually edit the default name of the inserted code.

[1]: The vector form does appear to simply be embedded in the function definition, so you should be able to extract that from the code to manually re-define a macro function in vector format.

[2]: When I originally wrote this reply, I'd forgotten that this was a custom function. Sorry about that.

(defun apply-named-macro-to-region-lines (top bottom)
  "Apply named keyboard macro to all lines in the region."
  (interactive "r")
  (let ((macro (intern
                (completing-read "kbd macro (name): "
                                 obarray
                                 (lambda (elt)
                                   (and (fboundp elt)
                                        (or (stringp (symbol-function elt))
                                            (vectorp (symbol-function elt))
                                            (get elt 'kmacro))))
                                 t))))
    (apply-macro-to-region-lines top bottom macro)))


回答3:

A simple solution is to define a macro that calls your function then use the good ol' apply-macro-to-region-lines.

Apart from that, I think that you could write a loop in a few lines of elisp that does exactly what you ask for. If you would like to be fancy, you can even prompt the user for the name of the function. I think this is a good exercise for elisp, I can help you with some pointers if you feel like you would like to try it yourself.



回答4:

The following function should do what you want:

(defun apply-function-to-region-lines (fn)
  (interactive "aFunction to apply to lines in region: ")
  (save-excursion
    (goto-char (region-end))
    (let ((end-marker (copy-marker (point-marker)))
          next-line-marker)
      (goto-char (region-beginning))
      (if (not (bolp))
          (forward-line 1))
      (setq next-line-marker (point-marker))
      (while (< next-line-marker end-marker)
        (let ((start nil)
              (end nil))
          (goto-char next-line-marker)
          (save-excursion
            (setq start (point))
            (forward-line 1)
            (set-marker next-line-marker (point))
            (setq end (point)))
          (save-excursion
            (let ((mark-active nil))
              (narrow-to-region start end)
              (funcall fn)
              (widen)))))
      (set-marker end-marker nil)
      (set-marker next-line-marker nil))))

So, if you have the following function that you want to apply against lines in a buffer:

(defun test()
  (insert "> "))

And, if your buffer contains the following contents:

Line 1: blah, blah
Line 2: blah, blah
Line 3: blah, blah
Line 4: blah, blah

If you select a region enclosing just lines 2 & 3, enter "M-x apply-function-to-region-lines", and enter "test" as the function name when prompted, you will get the following result in your buffer:

Line 1: blah, blah
> Line 2: blah, blah
> Line 3: blah, blah
Line 4: blah, blah


回答5:

You could always copy the source to apply-macro-to-region-lines and tweak it to call a passed in function, and thus make your own version.