Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Help writing emacs lisp for emacs etags search

Tags:

emacs

lisp

elisp

I'm looking for some help developing what I think should be an easy program.

I want something similar to Emacs tags-search command, but I want to collect all search results into a buffer. (I want to see all results of M-,)

I'm thinking this python style pseudo code should work, but I have no idea how to do this in emacs lisp? Any help would be greatly appreciated.

def myTagsGrep(searchValue):
    for aFile in the tag list:
        result = grep aFile seachValue
        if len(result) > 0:
            print aFile  # to the buffer
            print result # to the buffer

I would like to be able to browse through the buffer with the same features tags-apropos does.

Note that a similar question has been asked before: Is there a way to get emacs tag-search command to output all results to a buffer?

like image 254
eflanigan00 Avatar asked Dec 17 '10 20:12

eflanigan00


3 Answers

Since I'm such a fan of igrep, I'd use it as the building block. From there it's two simple routines and you're done. With that library and these two functions, all you have to do is:

M-x igrep-tags ^SomeRegexp.*Here RET

Here's the code:

(require 'igrep)
(defun igrep-tags (regex)
  (interactive "sTAGS Regexp: ")
  (igrep igrep-program regex (tags-file-names)))

(defun tags-file-names ()
  (save-excursion
    (visit-tags-table-buffer)
    (mapcar (lambda (f) (file-truename f))
            (tags-table-files))))

And, because the list of files can get really long, and you likely don't care what that list is, you can add these two pieces of code which will make the filenames invisible after the grep has finished:

(add-hook 'compilation-finish-functions 'igrep-tags-hide-filenames)

(defun igrep-tags-hide-filenames (buffer stat)
  "hide the filenames b/c they can get long"
  (save-excursion
    (set-buffer buffer)
    (save-match-data 
      (goto-char (point-min))
      (if (search-forward (combine-and-quote-strings (tags-file-names))
                          nil
                          (save-excursion (forward-line 10) (point)))
          (let ((display-string "..<files from TAGS>.."))
            (put-text-property (match-beginning 0) (match-end 0) 'invisible t)
            (put-text-property (match-beginning 0) (match-end 0) 'display display-string))))))

To avoid the really long command line, you can use the following code (which creates a temporary file containing all the names of files from TAGS file and uses that instead):

(defun igrep-tags (regex)
  (interactive "sTAGS Regexp: ")
  (let ((igrep-find t)
        (igrep-use-file-as-containing-files t))
    (igrep igrep-program regex nil)))

(defvar igrep-use-file-as-containing-files nil)

(defadvice igrep-format-find-command (around igrep-format-find-command-use-filename-instead activate)
  "use the second argument as a file containing filenames"
  (if igrep-use-file-as-containing-files
      (progn (with-temp-file
                 (setq igrep-use-file-as-containing-files (make-temp-file "tags-files"))
               (insert (combine-and-quote-strings (tags-file-names))))
             (setq ad-return-value (format "cat %s | xargs -e %s"
                                           igrep-use-file-as-containing-files
                                           (ad-get-arg 0))))
    ad-do-it))

And, for those using Emacs 22 or earlier, you'll need the routine that's shipped with Emacs 23 (from subr.el)

(defun combine-and-quote-strings (strings &optional separator)
  "Concatenate the STRINGS, adding the SEPARATOR (default \" \").
This tries to quote the strings to avoid ambiguity such that
  (split-string-and-unquote (combine-and-quote-strings strs)) == strs
Only some SEPARATORs will work properly."
  (let* ((sep (or separator " "))
         (re (concat "[\\\"]" "\\|" (regexp-quote sep))))
    (mapconcat
     (lambda (str)
       (if (string-match re str)
           (concat "\"" (replace-regexp-in-string "[\\\"]" "\\\\\\&" str) "\"")
         str))
     strings sep)))
like image 91
Trey Jackson Avatar answered Nov 15 '22 08:11

Trey Jackson


Here is the code I use to create a tag system for my personal notes. It uses bookmarks and treats each word in a bookmark as a single tag. Its not quite what you're looking for but it might get you started.

The first couple of functions are probably already implemented in emacs, but I wrote my own for reasons that I no longer recall.

;; FILTER keeps only elements of li for which pred returns true
(defun filter (pred li)
  (let (acc)
    (dolist (elem li)
      (if (funcall pred elem)
    (setq acc (cons elem acc))))
  (reverse acc)))


(defun string-match-all-p (str li)
   (if li
      (if (string-match-p (car li) str)
    (string-match-all-p str (cdr li))
   nil)
   t))

;;bookmarks as tags

(defun lookup-bookmark-tags (tagstring)
  (interactive "s")
   (let ((taglist (split-string tagstring " ")))
      (let ((bookmark-alist (filter 
           (lambda (elem)
             (string-match-all-p (car elem) taglist))
           bookmark-alist)))
    (call-interactively 'list-bookmarks))))

I then bind the 'tagging' behavior to a key (F11) and the 'lookup' behavior to another (F12).

(global-set-key [f11] 'bookmark-set)
(global-set-key [f12] 'lookup-bookmark-tags))

Hope that is useful to you.

like image 38
Aliud Alius Avatar answered Nov 15 '22 08:11

Aliud Alius


This is what you want:

http://www.emacswiki.org/emacs/Icicles_-_Emacs_Tags_Enhancements#icicle-tags-search

This is the doc string for icicle-tags-search:

    Search all source files listed in tags tables for matches for REGEXP.
    You are prompted for the REGEXP to match.  Enter REGEXP with `RET'.
    You do not need `M-,' - you see all matches as search hits to visit.

    All tags in a tags file are used, including duplicate tags from the
    same or different source files.

    By default, all tags files are used, but if you provide a prefix
    argument then only the current tag table is used.

    If your TAGS file references source files that no longer exist, those
    files are listed.  In that case, you might want to update your TAGS
    file.


    You can alternatively choose to search, not the search contexts as
    defined by the context regexp you provide, but the non-contexts, that
    is, the text in the files that does not match the regexp.  To do this,
    use `C-M-~' during completion.  (This is a toggle, and it affects only
    future search commands, not the current one.)

See also this page for more explanation about Icicles search:

http://www.emacswiki.org/emacs/Icicles_-_Search_Commands%2c_Overview

like image 28
Drew Avatar answered Nov 15 '22 08:11

Drew