5

Being fairly new to GNU Emacs and intend to use it as a replacement to vi/vim where possible, I came across an obstacle, I seem not be able to overcome.

In vim (similar to less) you can provide a command line argument to open the editor already highlighting the first occurrence of a search.

Searching my ways through man pages, reading help files, going through the built-in tutorial and looking on the net, all I found was specific line numbers (and columns) to jump to. However, what I am typically looking for is not at a line number I know of when opening files.

Is there an Emacs way to invoke an (incremental) search right at the start when opening a file, something along the way of emacs --search <search_term> <file>?

cyberbisson
  • 887
  • 1
  • 6
  • 17
Phoenix
  • 341
  • 1
  • 8
  • 1
    It doesn't seem like Emacs implements such options, but it has `--eval`, which can take a script to start `isearch`, and that can be put into a wrapper script. –  Apr 26 '19 at 19:20
  • Thanks. Having seen that `--eval` states "evaluate Emacs Lisp expression EXPR". Do you have an example for this (or a link to examples I can dive into)? I am still getting used to the editor and scripting in lisp is something I have never seen before I touched Emacs. I did manage (somehow) the startup configuration, so Emacs opens in a way I want and I understood most of what I did there. – Phoenix Apr 26 '19 at 19:24
  • I've been trying to write such an expression, but it seems it involves some trickery with calling commands interactively. If you want some pointers, Emacs ships with info manuals for elisp. Here are online versions of these: https://www.gnu.org/software/emacs/manual/html_node/elisp/index.html https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html –  Apr 26 '19 at 19:30
  • Thanks a lot! I will try my luck with that. If it gets too complicated (for now), I can always use `emacs +$(grep -hn "" | head -n 1 | cut -d":" -f1) `. Just wanted to avoid the extra commands to get the line numbers if it would work within Emacs natively. – Phoenix Apr 26 '19 at 19:33

2 Answers2

4

According to Preset search isearch-string from command line, you can search for PATTERN with Isearch programmatically using:

(isearch-forward nil t)
(isearch-yank-string "PATTERN")

So open FILE and search for PATTERN:

$ emacs FILE --eval '(isearch-forward nil t)'  --eval '(isearch-yank-string "PATTERN")'

And if you put the following in your init file

(defun my-search-for (pattern)
  "Search for PATTERN with Isearch."
  (interactive "sSearch for: ")
  (isearch-forward nil t)
  (isearch-yank-string pattern))

you can also type less from the command line

$ emacs FILE --eval '(my-search-for "also")'
xuchunyang
  • 14,302
  • 1
  • 18
  • 39
  • Trying your second part of it: While initially working (it finds the item of interest), when closing `FILE`, it opens a buffer for `pattern`. So, something is off and I cannot pin-point it. Do you have any idea of what is happening? – Phoenix Apr 27 '19 at 16:35
  • Correction: I made a mistake in the script calling it. Using your example, it opens the scratch buffer (default for me) with the message in the echo area `Symbol’s value as variable is void: ` where `` is the word I was looking for. – Phoenix Apr 27 '19 at 16:42
  • @Phoenix You missed the double-quotes around ``. The right command line in your case is: `emacs FILE --eval '(my-search-for "")'` – Tobias Apr 27 '19 at 23:10
  • Thanks! I will give it another try once back at that computer. As my pattern can be provided through a variable, I will try not to use the single quotations and instead see about escaping the ones around the variable. – Phoenix Apr 27 '19 at 23:17
  • @Tobias, just tested it and I can now confirm that with (escaped) quotations around the pattern, it will work. While I like the other provided solution better and will keep it as accepted answer, I still added this to my Emacs init file, so I can use this alternative as well. Thanks for pointing out the missing quotations! – Phoenix Apr 28 '19 at 00:04
4

The eval solution will work, but this is a lot of stuff to type on the command-line if you use this search frequently. We can actually change the command-line parameters so that --search <string> starts up with Emacs. Just modify your initialization file with this (if you are using lexical-binding):

(add-to-list 'command-switch-alist '("--search"  . command-line-search))

(defun command-line-search (_switch)
  (let ((search-text (pop command-line-args-left)))
    (add-hook ; Use a hook that runs after files load.  Otherwise, your CLI
              ; option will have to be given AFTER any buffers that you wish to
              ; search, which is not great.
     'window-setup-hook
     #'(lambda ()
         (isearch-forward
          nil ; This should be t if you want regex searching.
          t)  ; This must be t, or the call will block, preventing the next
              ; line.
         (isearch-yank-string search-text)))))

If you have not set lexical-binding (the default), you have to live with a global variable, but this will do the trick:

(add-to-list 'command-switch-alist '("--search"  . command-line-search))

(defvar search-text nil
  "Initial text given from the CLI that will be searched.")
(defun command-line-search (_switch)
  (setq search-text (pop command-line-args-left))
  (add-hook ; Use a hook that runs after files load.  Otherwise, your CLI option
            ; will have to be given AFTER any buffers that you wish to search,
            ; which is not great.
   'window-setup-hook
   #'(lambda ()
     (isearch-forward
      nil ; This should be t if you want regex searching.
      t)  ; This must be t, or the call will block, preventing the next line.
     (isearch-yank-string search-text))))

Now all you have to do is emacs --search <search_term> <file> like you wanted.

cyberbisson
  • 887
  • 1
  • 6
  • 17
  • 1
    I tried your solution. After opening the file, it states `Failing I-search: ` and remains at the first line. I can, however, just press C-s once without entering `` again and it will find the word immediately. So, it works half way. Not sure why it states initially that it fails though. I tried `isearch-backward` in case the search was already past the word within the file, but I had the same result when opening again. The function to add this as a command-line switch is absolutely stunning though. Would like to see it work for me. – Phoenix Apr 27 '19 at 16:49
  • Hmm, I will try some debugging a bit later. Does it show the correct search term when it fails at first? – cyberbisson Apr 27 '19 at 16:59
  • Yep. It does. That is why it puzzles me. It highlights the search term in red (as if the word was not found in its entirety). And when I press C-s, it just finds and highlights the word inside the file. The echo area states `Overwrapped I-search: `, however, which is why I tried `isearch-backward` as I assumed that point was past the word I searched for (but same result there). – Phoenix Apr 27 '19 at 17:04
  • It's an ordering problem. I'll update the code. Basically, the search is running before the buffer loads if you specify the parameter before it on the CLI. We'll delay the execution of the search until emacs fully starts... – cyberbisson Apr 27 '19 at 17:17
  • With the update I now get `Symbol’s value as variable is void: search-text`. So `search-text` is no longer considered a variable and is taken as search term itself. – Phoenix Apr 27 '19 at 17:26
  • Ha! This is because I have `lexical-binding` set to `t`. I wonder if adding that to the `let` expression would do the trick. – cyberbisson Apr 27 '19 at 17:38
  • 1
    Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/92940/discussion-between-phoenix-and-cyberbisson). – Phoenix Apr 27 '19 at 17:43