pinard / org-grep

Kind of M-x rgrep adapted for Org mode
15 stars 6 forks source link

+TITLE: Org Grep — Kind of M-x rgrep adapted for Org mode

This tool allows for grepping files in a set of Org directories, formatting the results as a separate Org buffer. This buffer is assorted with a few specific navigation commands so it works a bit like =M-x rgrep=. Optionally, the tool may simultaneously search Unix mailboxes, Gnus mailgroups, or other textual files.

** Installation

This tool has been developed on Linux, and likely requires a Unix-like system. Otherwise, one needs compatible find and grep tools, and a shell able to properly decipher the arguments and establish a pipe.

To install Org Grep, just copy =org-grep.el= somewhere Emacs may find it. Optionally, assign some key bindings to trigger the tool. For one, I added these lines to my =~/.emacs= file:

+BEGIN_SRC elisp

(autoload 'org-grep "org-grep" nil t)
(define-key org-mode-map "\C-cng" 'org-grep-full)
(define-key org-mode-map "\C-cog" 'org-grep)

+END_SRC

yet of course, one may choose any other key binding.

** Usage

To use this tool, call =M-x org-grep= or the keybinding put aside for it, and reply to the prompt with a regular expression to search for. Happily enough, Emacs and the grep command use rather similar syntax for regular expressions. Be well aware that all currently opened files in Emacs are automatically saved to disk before that command gets executed.

If the command is given an prefix argument (that is, if C-u is given immediately before the command), the user may interactively edit the grep options. If the user did not configure org-grep-grep-options otherwise, the default option is =-i=, which ignores case differences while searching. So, if you want to search strictly, use a prefix argument and erase that =-i=.

There is another command =M-x org-grep-full= which also search Unix mailboxes and Gnus mailgroups, given the user configured the appropriate variables. The command is separate from =M-x org-grep= command, giving more control to users who do not like the slowdown.

Both commands create an Org buffer with the found lines, each preceded by the base name of the file containing the line, and the line number within that file.

This is the =[browse]= view, which is a read-only view. Org Grep also offers the =[edit]= view and the =[tree]= view. In all views, buttons on the title line may be used to switch to the other views. (You might want to set org-confirm-elisp-link-function to nil, so avoiding all confirmation requests.) There are also =dired= buttons which may be added at some places: they normally open Emacs /Dired/ on the proper directory for the line.

** Views

In the =[browse]= view, one may use standard Org commands which do not modify the buffer, including of course those able to follow links. A few extra key bindings are also available:

In all Org buffers, command C-x ` uses the contents of an existing =Org Grep= buffer for moving to the next search hit. If that buffer does not exist, or if there is no following hit, the standard Emacs action is used instead: usually moving to the next compilation error.

In the =[edit]= view, special commands of the =[browse]= view are no more available, and all standard Org commands may be used. For convenience, all list items are turned into checklist items.

In the =[tree]= view, like in the =[edit]= view, special commands of the =[browse]= view are no more available, and all standard Org commands may be used. In that view, a hierarchical set of headers represent directories, and all hits are shown under the appropriate headers. This is useful to regroup an overwhelming number of hits under projects, or such things. The headers are sorted lexicographically. Also, they get collapsed to avoid deep nesting whenever possible.

** Configuration

Org Grep may be used immediately, without any configuration. However, a few Emacs variables may be set prior to, or after loading =org-grep.el=, for altering its behavior. These variables are:

** Extra shell commands

Here is an example of org-grep-extra-shell-commands. Let's assume that one want to /also/ search the file system for matching file names. The main trick is to fake that the match occurred on first line of found files. The context is left empty, Org Grep then reacts to this little kludge by showing more information about the full file name:

+BEGIN_SRC elisp

(setq org-grep-extra-shell-commands '(fp-org-grep-in-locate))

(defun fp-org-grep-in-locate (regexp) (concat "locate -e " org-grep-grep-options " -r " (shell-quote-argument regexp) " | sed 's,$,:1:,'"))

+END_SRC

This other example for org-grep-extra-shell-commands takes advantage of Git search speed, when files are under the control of a Git repository. The main trick here is to prepend the directory information to the result, as this information would otherwise be lost after the directory changed. Given the repository is located at =~/share/bin/=, one could use:

+BEGIN_SRC elisp

(setq org-grep-extra-shell-commands '(fp-org-grep-in-share-bin))

(defun fp-org-grep-in-share-bin (regexp) (concat "(cd ~/share/bin && git grep " org-grep-grep-options " -n -e " (shell-quote-argument regexp) " | sed 's,^,~/share/bin/,')"))

+END_SRC

** Purpose, history

Switching to Org, I immediately populated hundreds of Org files with data previously accumulated either as Emacs allout files ([[https://github.com/pinard/FP-etc/tree/master/allout-vim][or Vim!]]), Tomboy notes or Workflowy items. The standard Org mechanics for searching a collection of files requires them under the control of the Org agenda. Given my volume of notes, Org mode was crawling, so I had to relax the agenda and quickly develop some other mean for searching.

The first =org-grep= I wrote was based on Emacs standard =M-x rgrep=, using hooks and other tricky machinery so it works the way I wanted. Yet, =M-x rgrep= is limited to a single directory. Moreover, the =grep= buffer does not render Org lines as nicely as Org mode does, and this became critical for some long Org lines using a lot of heavy markup.

So I rewrote =org-grep= with the resulting output as a genuine Org file. This seems like a cleaner and easier way to proceed.

** Caveats

Org Grep is constantly useful to me, yet a few minor problems remain, which I can easily live with. Here are those I'm aware of: