Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an apply-command-to-each-line-in-region in emacs?

Tags:

emacs

elisp

I have a bunch of links saved in an orgmode file, say...

http://www.stackoverflow.com
http://www.google.com
http://www.github.com

I can open each one by having the cursor on the link and doing C-c C-o, and it conveniently pops up my default browser and opens that link in a tab.

Now suppose I have like 20 of these links. Is there a convenient way to apply a function like this to each line within a selected region, without recording an explicit macro?

I'd imagine it looking something like...

Select region
M-x foreach-in-region
Keystrokes to apply to each line: C-c C-o

And this is just for functions already defined. I imagine the way without would be something like...

with cursor on first line of link
F3 # to start record macro
C-c C-o 
down arrow
F4
Select region (omitting the first line, since that's now already opened in my browser)
C-x C-k r

Does this exist? If not, how would I lisp this?

like image 814
Mittenchops Avatar asked Aug 02 '15 01:08

Mittenchops


2 Answers

You should record the macro for one line, then use apply-macro-to-region-lines to execute it for all lines in region. C-x C-k r

Alternatively, you can use multiple-cursors to create a cursor on each line and C-c C-o to open all. multiple-cursors will transform your usage patterns over time for the better if you give it a chance.

like image 135
event_jr Avatar answered Sep 28 '22 02:09

event_jr


(defun do-lines (fun &optional start end)
  "Invoke function FUN on the text of each line from START to END."
  (interactive
   (let ((fn   (intern (completing-read "Function: " obarray 'functionp t))))
     (if (use-region-p)
         (list fn (region-beginning) (region-end))
       (list fn (point-min) (point-max)))))
  (save-excursion
    (goto-char start)
    (while (< (point) end)
      (funcall fun (buffer-substring (line-beginning-position) (line-end-position)))
      (forward-line 1))))

Update after your comment --

Now it sounds like you want to not enter a function name but hit a key, and have the command bound to that key be applied to each line in the region (or buffer).

Something like the following will do that. However, be aware that command often have particular behavior wrt lines. For example, if you were to hit key C-k (kill-lines) then it already moves forward after each line it kills. Because do-lines does not know what kind of function (command) you will invoke, it advances to the next line after each invocation. For a command such as kill-lines this will thus do the wrong thing: it will end up advancing two lines, not one, thus skipping lines. IOW, be aware that the code for do-lines cannot compensate for what a particular function it invokes might do that might not correspond to what you expect. Instead, it does what it says it does.

(defun do-lines (command &optional start end)
  "Invoke COMMAND on the text of each line from START to END."
  (interactive
   (let* ((key  (read-key-sequence-vector "Hit key sequence: "))
          (cmd  (lookup-key global-map key t)))
     (when (numberp cmd) (error "Not a valid key sequence"))
     (unless (commandp cmd) (error "Key `%s' is not defined" (key-description key)))
     (if (use-region-p)
         (list cmd (region-beginning) (region-end))
       (list cmd (point-min) (point-max)))))
  (setq start  (copy-marker start)
        end    (copy-marker end))
  (save-excursion
    (goto-char start)
    (while (< (point) end)
      (funcall command (buffer-substring (line-beginning-position) (line-end-position)))
      (forward-line 1))))
like image 27
Drew Avatar answered Sep 28 '22 02:09

Drew