[emacs-berlin] How to search and select some text?

Christian Tietze me at christiantietze.de
Thu Jan 19 15:17:12 UTC 2023


Hi,

> My eLisp function should:
> 1. `regexp-replace` some text
> 2. find a specific string, select the first match
> 3. run `mc/mark-all-like-this`

Step 1 doesn't fit to the subject and your main concern; what's that for?

For programmatic searches, this will do, if you don't need a regexp:

(search-forward "foo")

I'd also set the optional params so that it does not produce an error
when nothing is found:

(search-forward "foo" nil t)

It puts the point after the match, though. To select the match, you have
to select from the match backwards:

 (set-mark (point)) ;; Begins the selection with a mark
 (backward-char (length "foo"))
 (exchange-point-and-mark) ;; Selects from the new point to the mark

This is a bit more "imperative" than your regex based SO link.

You can wrap that in a function:

(defun my/search-string-and-set-region (str)
  ;; Abort when no match:
  (when (search-forward str nil t)
    (set-mark (point))
    (backward-char (length str))
    (exchange-point-and-mark)))

You should be able to combine this with `mc/mark-all-like-this`.

> I think I am missing some basic understanding of something (buffers?).
> Can anyone explain me why the function suggested on stackexchange
> selects the text when invoked with `M-x my-search-function` but not when
> called from another function? I think I am naively expecting that any
> function invoked as a command should work also when invoked as an "api",
> since Emacs is basically a macro processor.

You're right, it's not quite the same when you call an interactive (M-x)
function non-interactively ("API" evaluation).

This line in the SO answer declares that:

  (interactive "smy-search-forward: ")

This asks for user input, expecting a string (that's the "s" prefix in
the string argument "smy-search-forward: "). This will populate the
first function parameter with the user-interactive input.

If you call the function non-interactively, you need to the string as a
parameter to it.

  (my-search-forward "foo")

That works fine for me.

Now to combine this into an interactive helper function, you need to ask
for user input in your function, and then pass the string to the search
function:

(defun my/search-and-mark-all (str)
  (interactive "sSearch forward for: ")  ;; ask for user input
  (my-search-forward str) ;; pass the argument
  (mc/mark-all-like-this))

Cheers,
Christian

-- 
Sent from Bielefeld, Germany <3
https://christiantietze.de -- Programming + Personal
https://zettelkasten.de    -- Creative Knowledge Work


More information about the emacs-berlin mailing list