I like bookmarks, don't you? But something missing was an ability to save a bookmark for an rg.el
search1, 2.
Here is my solution after digging into the rg lib:
Code
- Define a function to serialize the bookmark
To find your way back, you'll need to record information. Bookmarks will jam all your data into a big list.What data to record? Here, I had to search for local variables that mached
^rg-
. Check out using(buffer-local-variables)
(originally I did this the slow way of usingC-h v
). Then I realized it was a struct, and traced through the code to find out what function to be called (rg-run
) and record data to pass into to it:(defun rg-bookmark-make-record () "Make a bookmark record for the current rg buffer." `(,(format "rg: \"%s\" ∋ %s ∀ %s" (rg-search-pattern rg-cur-search) (rg-search-dir rg-cur-search) (rg-search-files rg-cur-search)) ;; pattern is a synonym of query in rg.el (pattern . ,(rg-search-pattern rg-cur-search)) (files . ,(rg-search-files rg-cur-search)) (dir . ,(rg-search-dir rg-cur-search)) (literal . ,(rg-search-literal rg-cur-search)) ;; Found some state in the source code; ;; I don't think these are needed, but they are present ;; (search-cfg ,(rg-set-search-defaults (cdr body))) ;; (local-bindings (rg-search-parse-local-bindings search-cfg)) ;; (iargs (rg-search-parse-interactive-args search-cfg)) (flags . ,(rg-search-flags rg-cur-search)) (handler . rg-bookmark-handler)))
- Now we need a way to read that stored data and jump to the bookmark
(defun rg-bookmark-handler (record) "Jump to a bookmark's url with bookmarked location." (let ((query (bookmark-prop-get record 'pattern)) (files (bookmark-prop-get record 'files)) (dir (bookmark-prop-get record 'dir)) (literal (bookmark-prop-get record 'literal)) (flags (bookmark-prop-get record 'flags))) (rg-run query files dir literal nil flags)))
We could shorten this code since there's so much repitition, but let's keep the simplicity for now.
- And finally, bookmark-make-record-function must be overridden in the buffer-local vars for the mode:
(defun rg-set-bookmark-handler () "Assigns `bookmark-make-record-function' to a custom function." (set (make-local-variable 'bookmark-make-record-function) #'rg-bookmark-make-record)) (add-hook 'rg-mode-hook #'rg-set-bookmark-handler)
Test
- I activate a rg search for the regexp
version: draft
and tweak a flag. - I
M-x bookmark-set
. - The prompt shows
Set bookmark named (default rg: "version: draft" ∋ /home/refurb/src/my-site/ ∀ org):
I press
<Enter>
- I inspected the saved bookmark:
("rg: \"version: draft\" ∋ /home/refurb/src/my-site/src/content/ ∀ org" (pattern . "version: draft") (files . "org") (dir . "/home/refurb/src/my-site/src/content/") (literal) (flags) (handler . rg-bookmark-handler))
- Close the ripgrep buffer.
- Using
M-x bookmark-jump
, I can jump and see the ripgrep search I just saved, the buffer loads up, I see my results!
Conclusion
Now you've got another person (me) telling you how great bookmarks are in emacs. Give it a shot and start making your own bookmark types!
I did not cover making a "category" of bookmarks. Drew covers it here on emacs.stackexchange.
The search hits were sparse when I looked for what this use case of bookmarks. Maybe I'm alone out here.
If this post looks similar to one by overvale, it's because I read one of their articles: https://www.overvale.com/emacs/extending-emacs-bookmarks.html
Merry Christmas!
Full code
Maybe I should package this up?
(defun rg-bookmark-make-record () "Make a bookmark record for the current rg buffer." `(,(format "rg: \"%s\" ∋ %s ∀ %s" (rg-search-pattern rg-cur-search) (rg-search-dir rg-cur-search) (rg-search-files rg-cur-search)) ;; pattern is a synonym of query in rg.el (pattern . ,(rg-search-pattern rg-cur-search)) (files . ,(rg-search-files rg-cur-search)) (dir . ,(rg-search-dir rg-cur-search)) (literal . ,(rg-search-literal rg-cur-search)) ;; Found some state in the source code; ;; I don't think these are needed, but they are present ;; (search-cfg ,(rg-set-search-defaults (cdr body))) ;; (local-bindings (rg-search-parse-local-bindings search-cfg)) ;; (iargs (rg-search-parse-interactive-args search-cfg)) (flags . ,(rg-search-flags rg-cur-search)) (handler . rg-bookmark-handler))) (defun rg-bookmark-handler (record) "Jump to a bookmark's url with bookmarked location." (let ((query (bookmark-prop-get record 'pattern)) (files (bookmark-prop-get record 'files)) (dir (bookmark-prop-get record 'dir)) (literal (bookmark-prop-get record 'literal)) (flags (bookmark-prop-get record 'flags))) (rg-run query files dir literal nil flags))) (defun rg-set-bookmark-handler () "Assigns `bookmark-make-record-function' to a custom function." (set (make-local-variable 'bookmark-make-record-function) #'rg-bookmark-make-record)) (add-hook 'rg-mode-hook #'rg-set-bookmark-handler)
In my opinion, rg.el
doesn't have good support for persisting saved searches (nor should that be its focus).