org-roam-search.el 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. ;;Copyright (C) 2021 Category <category@[no_spam]quintendo.uk>
  2. ;;This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2.
  3. ;;This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  4. ;;You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  5. (defgroup org-roam-search nil
  6. "Variables for the org-roam-search interface to org-roam databases."
  7. :group 'applications)
  8. (defcustom org-roam-search-keep-window nil
  9. "Sets wether or not the search results window should be kept when a link is opened from org-roam-search.
  10. If nil, the window is killed.
  11. If true, the results window is kept.
  12. Defaults to nil."
  13. :type 'boolean
  14. :group 'org-roam-search)
  15. (defun org-roam-search (&optional search-term)
  16. "A function for searching the org-roam-database.
  17. This function will ask user for a search term (grep-style regexp is accepted), then will iterate through all .org files within the org-roam-directory path searching for that pattern wihtin them. Results are displayed in read-only buffer, with links to follow directly.
  18. If called with the argument SEARCH-TERM, user input is skipped and that pattern is searched directly."
  19. (interactive)
  20. ;; If old buffer exists, remove window (if it exists), and kill the buffer
  21. (if (get-buffer "*org-roam-search*")
  22. (progn
  23. (if (get-buffer-window "*org-roam-search*")
  24. (delete-window (get-buffer-window "*org-roam-search*")))
  25. (kill-buffer "*org-roam-search*")))
  26. ;; Check if org-roam has been initialized
  27. (if (not (boundp 'org-roam-directory))
  28. (error "org-roam-directory is void, has org-roam started?"))
  29. ;; Remember old mini-buffer settings
  30. ;; Prevents short(ish) output going into minibuffer
  31. (setq ors-old-mini-resize resize-mini-windows)
  32. (setq resize-mini-windows nil)
  33. ;; Get search-term from user if not provided
  34. (if (not search-term)
  35. (setq search-term (read-string "Search query: ")))
  36. ;; Save the search term in external variable for later use/hinting
  37. (setq org-roam-search-query search-term)
  38. (generate-new-buffer "*org-roam-search*")
  39. ;; Grep through all "*.org" files in the org-roam-directory, using search term as a regexp
  40. (shell-command (format "grep -r -e %s %s" search-term (concat org-roam-directory "/*.org")) "*org-roam-search*")
  41. ;; If this results in an empty buffer, show no results and throw an error
  42. (if (eq 0 (buffer-size (get-buffer "*org-roam-search*")))
  43. (progn
  44. (set-buffer "*org-roam-search*")
  45. (insert (format "No results found for search: %s" search-term))
  46. ;; Restore minibuffer setting before exit
  47. (setq resize-mini-windows ors-old-mini-resize)
  48. (error "No org-roam-search results found.")))
  49. ;; switch buffer (and enable org-mode)
  50. (set-buffer "*org-roam-search*")
  51. (org-mode)
  52. ;; Begin looping through search results to make links
  53. (beginning-of-buffer)
  54. (while (not (eq (point) (point-max))) ; While not at the end of the buffer...
  55. (progn
  56. ;; Replace filename in grep results with org link
  57. (beginning-of-line)
  58. (insert "[[")
  59. (goto-char (string-match ".org\:" (buffer-string) (point)))
  60. (forward-char 5)
  61. (insert "]]")
  62. ;; Extract current filename from beginning of this line
  63. (beginning-of-line)
  64. (setq-local current-link-end (string-match ":" (buffer-string) (point)))
  65. (forward-char 2)
  66. (kill-ring-save (point) (- current-link-end 1)) ; Store on the kill ring without killing
  67. (setq-local current-file (current-kill 0))
  68. ;; Go to the end of the link, extract org-roam title from within the saved file (using grep again) and format to use as the text for the link
  69. (goto-char current-link-end)
  70. (setq-local current-link-title (replace-regexp-in-string "\n$" "" (shell-command-to-string (format "grep -m 1 title %s | cut -c 10-" current-file))))
  71. (insert (format "[%s]" current-link-title))
  72. (forward-char)
  73. (insert " ")
  74. (beginning-of-line)
  75. (insert "Node: ")
  76. ;; Go to next line in the file and loop to update the next link
  77. (next-logical-line)))
  78. ;; Highlight the search term in the results buffer
  79. (highlight-regexp search-term)
  80. ;; Add info about search/keybinds etc
  81. (beginning-of-buffer)
  82. (newline 2)
  83. (previous-line 2)
  84. (insert (format "| Open link %s | Next link %s | Prev link %s | Close %s |\nSearch results for query \"%s\""
  85. (format-kbd-macro (where-is-internal 'org-roam-search-open-link org-roam-search-keymap t))
  86. (format-kbd-macro (where-is-internal 'org-roam-search-next-link org-roam-search-keymap t))
  87. (format-kbd-macro (where-is-internal 'org-roam-search-prev-link org-roam-search-keymap t))
  88. (format-kbd-macro (where-is-internal 'delete-window org-roam-search-keymap t))
  89. search-term))
  90. ;; Set the buffer read only
  91. (read-only-mode 1)
  92. (beginning-of-buffer)
  93. ;; Enable org-roam-serach-mode for special keybinds
  94. (org-roam-search-mode t)
  95. ;; Restore minibuffer setting
  96. (setq resize-mini-windows ors-old-mini-resize)
  97. ;; Give search result window focus andstart on first link
  98. (select-window (get-buffer-window "*org-roam-search*"))
  99. (org-roam-search-next-link))
  100. (defun org-roam-search--close-or-keep-window ()
  101. "Closes or keep the org-roam-search results window, based on user preference."
  102. (interactive)
  103. (if (not org-roam-search-keep-window)
  104. (if (get-buffer-window "*org-roam-search*")
  105. (delete-window (get-buffer-window "*org-roam-search*")))))
  106. (defun org-roam-search-next-link ()
  107. "Navigate to the next link in the org-roam-search results buffer, moving via logical line."
  108. (interactive)
  109. ;; Avoid jumping to next line if already on the last-ine of the buffer
  110. (if (eq (org-current-line) (+ 1 (car (buffer-line-statistics))))
  111. (error "End of search buffer")
  112. (next-logical-line))
  113. (beginning-of-line)
  114. (goto-char (string-match "]\\[" (buffer-string) (point))))
  115. (defun org-roam-search-prev-link ()
  116. "Navigate to the previous link in the org-roam-search results buffer, moving via logical line."
  117. (interactive)
  118. (previous-logical-line)
  119. (beginning-of-line)
  120. (goto-char (string-match "]\\[" (buffer-string) (point))))
  121. (defun org-roam-search-open-link ()
  122. "Open the link at the current point, without highlighting result in the open buffer.
  123. Respect rules for closing the search results window"
  124. (interactive)
  125. (org-open-at-point)
  126. (other-window 1)
  127. (org-roam-search--close-or-keep-window))
  128. (defun org-roam-search-open-link-hinted ()
  129. "Open the link at the current point, without highlighting result in the open buffer.
  130. Respect rules for closing the search results window"
  131. (interactive)
  132. (org-open-at-point)
  133. (if org-roam-search-query
  134. (highlight-regexp org-roam-search-query))
  135. (other-window 1)
  136. (org-roam-search--close-or-keep-window))
  137. (setq org-roam-search-keymap (make-sparse-keymap "org-roam-search-keymap"))
  138. (define-key org-roam-search-keymap "q" 'delete-window)
  139. (define-key org-roam-search-keymap "s" 'org-roam-search)
  140. (define-key org-roam-search-keymap "o" 'org-roam-search-open-link)
  141. (define-key org-roam-search-keymap "\r" 'org-roam-search-open-link-hinted) ;For return key
  142. (define-key org-roam-search-keymap "n" 'org-roam-search-next-link)
  143. (define-key org-roam-search-keymap "p" 'org-roam-search-prev-link)
  144. (define-minor-mode org-roam-search-mode
  145. "org-roam-search-mode provides keybinding and settings for use in results buffers after calling org-roam-search. This mode is not intended for use outside of this purpose."
  146. :lighter " ors"
  147. :keymap org-roam-search-keymap
  148. :interactive t)