1

I want to write a function that copies the current line from a buffer, but transforms it before in certain way. Specifically, I need to generate file names depending from lines of code. For example, having this line of C code:

    __asm__ volatile (".rept 200\n\t"
              "sub r5, r4, #255\n\t"|     // <---- point is the bar
              
              ".endr\n\t"

I want to have in my kill-ring/clipboard the string

sub_r5_r4_255.csv

I came up with this very ugly solution:

(defun my--get-instruction-csv-id ()
  "Generate a file .csv id from current line."
  (interactive)
  (save-excursion
    (move-beginning-of-line nil)
    (set-mark-command nil)
    (move-end-of-line nil)
    (kill-ring-save nil nil t))
  (let ((old-buffer (current-buffer)))
    (with-temp-buffer
      (yank)
      (replace-regexp " " "_" nil (point-min) (point-max))
      (replace-regexp "," "" nil (point-min) (point-max))
      (replace-regexp "#" "" nil (point-min) (point-max))
      (replace-regexp "\\\\t" "" nil (point-min) (point-max))
      (replace-regexp "\\\\n" "" nil (point-min) (point-max))
      (replace-regexp "\"" "" nil (point-min) (point-max))
      (move-end-of-line nil)
      (insert ".csv")
      (print (buffer-string))      ;(kill-new
      )))

(where in the last line kill-new has been substituded by print for debug purposes). However, I recognize this is inefficient and non idiomatic. How can I improve it?

For example, Emacs tells me I should replace replace-regexp with re-search-forward and replace-match: should I do that with a while loop? Is there an equivalent non regexp function that I can use? Can I avoid killing + yanking from the C buffer?

2
  • 2
    You should certainly avoid killing and yanking just to get your hands on a string. Doing (buffer-substring (line-beginning-position) (point)) would get you the current line (up to where point is - if you want the whole line, replace the (point) call with (line-end-position)) as a string that you can insert into your temp buffer instead of yanking. I'm sure you'll get more suggestions for the rest as well.
    – NickD
    Commented Oct 16, 2022 at 16:24
  • Thanks, I was searching for line-beginning-position and line-end-position but I couldn't find them Commented Oct 17, 2022 at 11:38

1 Answer 1

2

There are many ways to do this, so I'll start the ball rolling. I would certainly replace the killing+yanking as I mentioned in my comment. For the rest, I would use the string manipulation library s.el which you can install as a package from MELPA.

This library provides a convenient s-replace-all function that can do all of the little replacements in a single call. Here is a function along these lines:

(defun my--get-instruction-csv-id ()
  "Generate a file .csv id from current line."
  (interactive)
  (let ((s (buffer-substring (line-beginning-position) (point))))
    (kill-new
      (s-append ".csv"
        (s-replace-all '(("\"" . "") ("," . "") ("#" . "") ("\\n" . "") ("\\t" . "") (" " . "_"))
                       (s-trim s))))))

So we save the buffer substring between the position of the beginning of the line and the current point, use s-trim to trim whitespace from both ends of the string, use s-replace-all with a list of replacements (which can be augmented if necessary), append the suffix and pass the result to kill-new.

Since we are not moving point or changing buffers, there is no need for save-excursion either.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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