{"id":313,"date":"2014-05-06T10:00:21","date_gmt":"2014-05-06T14:00:21","guid":{"rendered":"http:\/\/blog.mozilla.org\/nfroyd\/?p=313"},"modified":"2014-05-06T10:00:21","modified_gmt":"2014-05-06T14:00:21","slug":"my-code-search-engine","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/nfroyd\/2014\/05\/06\/my-code-search-engine\/","title":{"rendered":"my code search engine"},"content":{"rendered":"<p>Christian Legnitto wrote <a href=\"http:\/\/christian.legnitto.com\/blog\/2014\/05\/05\/firefox-29s-best-feature\/\">a blog post<\/a> where he mentioned Firefox developers being forced to deal with &#8220;crufty code-search tools&#8221; (and many other perceived suboptimalities in the development process).\u00a0 I&#8217;m looking forward to reading his followup, but I also thought it was worth blogging about what I use for my day-to-day code search needs.<\/p>\n<p>I use Emacs&#8217;s <code>rgrep<\/code>.\u00a0 <code>rgrep<\/code> (and its cousin commands <code>grep<\/code> and <code>lgrep<\/code>) executes grep on a group of files in a given directory, recursively.  The grep results are then displayed in a hyperlinked format for easy browsing between matches.  Well, being Emacs, I use it with a few modifications of my own.\u00a0 Here&#8217;s my setup.<\/p>\n<p>First, a small utility I wrote for making quick selections with a single keypress:<\/p>\n<pre>(defun character-prompt (alist description)\r\n  (message \"Select [%s]: \"\r\n           (apply #'string (mapcar #'car alist)))\r\n  (let* ((ch (save-window-excursion\r\n               (select-window (minibuffer-window))\r\n               (read-char)))\r\n         (x (find ch alist :key #'car)))\r\n    (cond\r\n      ((null x)\r\n       (message (format \"No %s for character: %s\" description ch))\r\n       (sleep-for 1)\r\n       (discard-input)\r\n       (character-prompt alist description))\r\n      (t (cdr x)))))<\/pre>\n<p>This function gets used in the small wrapper I wrote around <code>rgrep<\/code>.  Some preliminaries first, like where the Firefox tree lives, files that contain  overly long lines and therefore mess with Emacs&#8217;s hyperlinking, and directories that I generally don&#8217;t deal with in my day-to-day work.<\/p>\n<pre>(defvar froydnj-mozilla-srcdir (expand-file-name \"~\/src\/gecko-dev.git\/\"))\r\n(defvar froydnj-mozilla-ignored-files\r\n  (list \"searchindex.js\"\r\n        \"jquery.js\"\r\n        \"jquery.min.js\"\r\n        \"interfaces.js\"\r\n        \"socket.io.min.js\"\r\n        \"jquery-ui-1.7.1.custom-min.js\"\r\n        \"edit_area_full.js\"\r\n        \"php.js\"\r\n        \"packed.js\"\r\n        \"socket.io.min.js\"\r\n        \"named-character-references.html\"\r\n        \"edit_area_full_with_plugins.js\"\r\n        \"string-tagcloud.js\"\r\n        \"string-unpack-code.js\"\r\n        \"check-string-tagcloud.js\"\r\n        \"check-string-unpack-code.js\"\r\n        \"string-unpack-code.html\"))\r\n(defvar froydnj-mozilla-ignored-directories\r\n  (list \"nss\" \"nsprpub\" \"js\/src\/tests\" \"intl\/icu\"))<\/pre>\n<p>Next, the way I select subsets of files to search in.  I learned after writing all this that <code>rgrep<\/code> already has built-in functionality for this (see the <code>grep-files-aliases<\/code> variable), but I like my setup better.<\/p>\n<pre>(defvar froydnj-mozilla-files\r\n  '((?a . \"*\")                    ; All of it\r\n    (?c . \"*.[cm]*\")              ; C\/C++\/Obj-C\r\n    (?C . \"*.[cmh]*\")             ; Same, plus headers (and HTML, sadly)\r\n    (?h . \"*.h\")\r\n    (?H . \"*.html\")\r\n    (?i . \"*.idl\")\r\n    (?j . \"*.js*\")\r\n    (?l . \"*.lisp\")\r\n    (?m . \"Makefile.in\")\r\n    (?p . \"*.py\")\r\n    (?v . \"*.java\")\r\n    (?w . \"*.webidl\")))<\/pre>\n<p>Finally, the wrapper itself, which prompts for the search pattern, the filename pattern, makes sure the directories and files above are ignored, and executes the search.<\/p>\n<pre>(defun froydnj-mozilla-rgrep ()\r\n  (interactive)\r\n  (let ((regexp (grep-read-regexp))\r\n        (files (character-prompt froydnj-mozilla-files \"filename pattern\"))\r\n        (grep-find-ignored-files (append grep-find-ignored-files\r\n                                         froydnj-mozilla-ignored-files))\r\n        (grep-find-ignored-directories (append grep-find-ignored-directories\r\n                                               froydnj-mozilla-ignored-directories)))\r\n    (rgrep regexp files froydnj-mozilla-srcdir)))\r\n<\/pre>\n<p>One other bit that I find useful is a custom name for each buffer.  By default, the <code>rgrep<\/code> results are deposited in a buffer named <code>*grep*<\/code> (likewise for <code>grep<\/code> and <code>lgrep<\/code>; the following advice applies to them automatically), and future greps overwrite this buffer.  Having records of your greps lying around is occasionally useful, so I&#8217;ve changed the hook that determines the buffer name for <code>rgrep<\/code>.  The comments that refer to compilation are from a previous job where it was useful to launch compilation jobs from within Emacs.  I keep meaning to tweak those bits to launch <code>mach<\/code> in various configurations instead, but I haven&#8217;t done so yet.<\/p>\n<pre>(defun froydnj-compilation-buffer-name (major-mode-name)\r\n  (let ((cfg-regexp \"\\\\([-0-9_.a-z\/+]+\\\\.cfg\\\\)\"))\r\n    (cond\r\n      ;; We're doing local compilation, stick the name of the release\r\n      ;; configuration in the buffer name.\r\n      ((or (string-match \"^cd \/scratch\/froydnj\/\\\\([^ ;]+\\\\)\" command)\r\n           (string-match \"^build-config\" command))\r\n       (string-match cfg-regexp command)\r\n       (concat \"*compilation \" (match-string 1 command) \"*\"))\r\n      ;; We're doing remote compilation, note the machine name and\r\n      ;; the release configuration name.\r\n      ((string-match \"^ssh \\\\([^ ]+\\\\)\" command)\r\n       (let ((machine (match-string 1 command)))\r\n         (string-match cfg-regexp command)\r\n         (concat \"*compilation@\" machine \" \" (match-string 1 command) \"*\")))\r\n      ;; grep.el invokes compile, we might as well take advantage of that.\r\n      ((string-equal major-mode-name \"grep\")\r\n       (if (boundp 'regexp)\r\n           (concat \"*grep for \" regexp \"*\")\r\n           \"*grep*\"))\r\n      ;; We have no idea, just use the default.\r\n      (t\r\n       \"*compilation*\"))))\r\n\r\n(setq compilation-buffer-name-function 'froydnj-compilation-buffer-name)\r\n<\/pre>\n<p>Search times are comparable to web-based tools, and browsing results is more convenient.  It has its shortcomings (overloaded C++ method names can be a pain to deal with, for instance), but it works well enough for 95%+ of my searching needs.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Christian Legnitto wrote a blog post where he mentioned Firefox developers being forced to deal with &#8220;crufty code-search tools&#8221; (and many other perceived suboptimalities in the development process).\u00a0 I&#8217;m looking forward to reading his followup, but I also thought it was worth blogging about what I use for my day-to-day code search needs. I use [&hellip;]<\/p>\n","protected":false},"author":320,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[31957,24,31956],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/posts\/313"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/users\/320"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/comments?post=313"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/posts\/313\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/media?parent=313"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/categories?post=313"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/tags?post=313"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}