1 ;;; ectags-select.el --- Select from multiple tags
3 ;; Copyright (C) 2007 Scott Frazer and (C) 2008 John Connors
5 ;; Author: John Connors <johnc@yagc.ndo.co.uk>
6 ;; Author: Scott Frazer <frazer.scott@gmail.com>
7 ;; Maintainer: John Connors <johnc@yagc.ndo.co.uk>
9 ;; Keywords: exuberant-ctags ectags tag select
11 ;; This file is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This file is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to
23 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
29 ;; Reworking of etags-select to work with exuberant-ctags and eldoc.
30 ;; ectags-tag-directory will generate a tag file in the current directory
31 ;; with the specified language using exuberant-ctags extended tag format
32 ;; which contains useful information about the current tag such as
33 ;; the signature, it's parent class, which class it is a member of. Eldoc
34 ;; displays this information. At present it does not do a very good job
35 ;; of finding the best candidate tag in an OO language where there may
36 ;; be multiple tags with the same name. It tries, however. The tag file
37 ;; needs to be in a specific format, hence the ectags-tag-directory-command.
38 ;; ectags-visit-tags-table is used to load in a tag table.
40 ;; Open a buffer with file/lines of exact-match tags shown. Select one by
41 ;; going to a line and pressing return. pop-tag-mark still works with this
45 ;; An up to date version of this code lives on repo.or.cz
46 ;; at git://repo.or.cz/ectags.git
51 ;; TO DO : tag completion DONE
52 ;; TO DO : tags search DONE
53 ;; TO DO : eldoc integration DONE
54 ;; TO DO : more than one lang per dir DONE
55 ;; TO DO : compile window for ectags DONE
56 ;; TO DO : do sthing about ginormous tag files
57 ;; TO DO : .. use abbreviated info
58 ;; TO DO : .. use gzipped tag files
59 ;; TO DO : use search for locating tag rather than line #
60 ;; TO DO : some kind of higlighting for select buffer WHY DOESN'T IT WORK!?
61 ;; TO DO : include line matched when searching for references
62 ;; TO DO : investigate CEDET to see if it can guide eldoc to a better
63 ;; match for the tag under the cursor
69 ;; Asm *.asm *.ASM *.s *.S *.A51 *.29[kK] *.[68][68][kKsSxX] *.[xX][68][68]
71 ;; Awk *.awk *.gawk *.mawk
74 ;; C++ *.c++ *.cc *.cp *.cpp *.cxx *.h *.h++ *.hh *.hp *.hpp *.hxx *.C *.H
76 ;; Cobol *.cbl *.cob *.CBL *.COB
78 ;; Erlang *.erl *.ERL *.hrl *.HRL
79 ;; Fortran *.f *.for *.ftn *.f77 *.f90 *.f95 *.F *.FOR *.FTN *.F77 *.F90 *.F95
83 ;; Lisp *.cl *.clisp *.el *.l *.lisp *.lsp *.ml
85 ;; Make *.mak *.mk [Mm]akefile
87 ;; Perl *.pl *.pm *.plx *.perl
88 ;; PHP *.php *.php3 *.phtml
89 ;; Python *.py *.python
90 ;; REXX *.cmd *.rexx *.rx
92 ;; Scheme *.SCM *.SM *.sch *.scheme *.scm *.sm
93 ;; Sh *.sh *.SH *.bsh *.bash *.ksh *.zsh
97 ;; Tcl *.tcl *.tk *.wish *.itcl
98 ;; Vera *.vr *.vri *.vrh
103 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
109 "Exuberant Ctags Support for Emacs"
114 (defcustom ectags-command
"ectags"
115 "Name of the exuberant ctags executable on your system"
120 (defcustom ectags-config-file
"~/.ectags"
121 "Name of the exuberant-ctags configuration file."
126 (defcustom ectags-language-file-suffix-alist
127 '(( "asp" .
( "*.asp" "*.asa" ))
128 ( "awk" .
( "*.awk" "*.gawk" "*.mawk"))
129 ( "c" .
( "*.c" "*.h" ))
130 ( "c++" .
( "*.c++" "*.cc" "*.cp" "*.cpp" "*.cxx" "*.h" "*.h++" "*.hh" "*.hp" "*.hpp" "*.hxx" "*.c" "*.C" "*.h" "*.H"))
132 ( "java" .
( "*.java " ))
133 ( "lisp" .
( "*.cl" "*.clisp" "*.el" "*.l" "*.lisp" "*.lsp" "*.ml"))
134 ( "python" .
( "*.py" "*.python" ))
135 ( "SQL" .
( "*.sql" ))
136 ( "Tcl" .
( "*.tcl" "*.tk" "*.wish" "*.itcl" )))
137 "Association list defining file masks for languages"
142 (defcustom ectags-system-tag-table-list nil
143 "List of tags tables that include system headers"
148 (defcustom ectags-api-files
149 '(( "wx" .
"/usr/local/include/wx" )
150 ( "gtk" .
"/usr/include/gtk-2.0" )
151 ( "glib" .
"/usr/include/glib-2.0" ))
152 "Association list mapping apis to directories"
158 (defcustom ectags-select-mode-hook nil
159 "*List of functions to call on entry to ectags-select-mode mode."
163 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
166 (defvar *ectags-matches
* nil
167 "List of candiate tag matches")
169 (defvar *ectags-regexp
* nil
170 "Holds regexp currently being sought in tags")
172 (defvar *ectags-max-candidates
* 7
173 "How many candidates to select between")
175 (defvar *ectags-case-sensitive
* t
176 "Is the tag matching case sensitive?")
178 (defvar *ectags-autopop-tag
* t
179 "If non-nil, automatically pop the tag off the tag stack when jumped to")
181 (defvar *ectags-tag-stack
* nil
182 "Stack of tag positions for browsing.")
184 (defvar *ectags-obarray
* nil
185 "Obarray used for ectags completions.")
187 (defvar *ectags-select-buffer-name
* "*ectags-select*"
188 "ectags-select buffer name.")
190 (defvar *ectags-reference-buffer-name
* "*ectag References*"
191 "ectags-reference buffer-name")
194 (defvar ectags-select-mode-font-lock-keywords nil
195 "ectags-select font-lock-keywords.")
197 (defvar *ectags-select-source-buffer
* nil
198 "ectags-select source buffer tag was found from.")
200 (defvar *ectags-reference-source-buffer
* nil
201 "ectags-reference source buffer tag was found from.")
203 (defvar *ectags-select-opened-window
* nil
204 "ectags-select opened a select window.")
206 (defvar *ectags-reference-opened-window
* nil
207 "ectags-referecnce opened a reference window.")
210 (defvar *ectags-scan-marks
* nil
211 "Holds markers where matches found.")
213 (defconst ectags-select-non-tag-regexp
"\\(\\s-*$\\|In:\\|Finding tag:\\)"
214 "ectags-select non-tag regex.")
217 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
220 ;; Klaus Berndl <klaus.berndl@sdm.de>: we have to take account that GNU Emacs
221 ;; > 21.3 has changed its split-string function! For the new split-string is
222 ;; (cdr (split-string ...)) not nil (at least in our context below), for GNU
223 ;; Emacs <= 21.3 nil!
224 (defun ectags-left-trim (str)
225 "Return a string stripped of all leading whitespaces of STR."
226 (let ((split-result (split-string str
"^[\n\t ]*")))
227 (or (or (and (cdr split-result
) ;; GNU Emacs > 21.3
228 (car (cdr split-result
)))
232 (defun ectags-right-trim (str)
233 "Return a string stripped of all trailing whitespaces of STR."
234 (or (car (split-string str
"[\n\t ]*$")) ""))
236 (defun ectags-trim (str)
237 "Applies `ectags-right-trim' and `ectags-left-trim' to STR."
238 (ectags-left-trim (ectags-right-trim str
)))
240 (defun ectags-full-trim (str)
241 "Applies `ectags-trim' and `ectags-middle-trim' to STR."
242 (ectags-excessive-trim (ectags-trim str
)))
244 ;; creating tag files ------------------------------------------------------------------------------------
247 (defun make-suffix-clauses (languages)
250 (concat " -iname \"" s
"\""))
251 (cdr (assoc-string l ectags-language-file-suffix-alist
))))
252 (split-string languages
)))
254 (defun make-shell-command-prefix (directory)
255 (concat "find " (expand-file-name directory
)))
257 (defun make-tag-file-name (directory)
258 (expand-file-name (concat directory
(string directory-sep-char
) "tags")))
260 (defun ectag-directory-command (directory languages
)
261 "Produce a command needed to scan the given directory for files
262 of the given language and produce tags"
265 (car (make-suffix-clauses languages
)))
266 (shell-command-prefix
267 (make-shell-command-prefix directory
))
268 (shell-command-suffix
269 (concat " | " ectags-command
" -o " (make-tag-file-name directory
) " --options="
270 (expand-file-name ectags-config-file
) " --verbose --excmd=n --extra=+fq --fields=+afiKlmnsSzt --file-scope=no -L -")))
271 (concat shell-command-prefix
276 (cdr suffix-clauses
)))
277 shell-command-suffix
)))
282 (defun ectags-tag-directory ()
283 "Prompt for a directory and a langage and create a tag file."
285 ;; prompt for directory
287 (read-directory-name "Directory to tag? " default-directory
))
288 (tag-languages (completing-read "Languages to tag? " ectags-language-file-suffix-alist nil nil
)))
289 (add-to-list 'compilation-error-regexp-alist
290 '("^\\([^:]+\\) confusing argument declarations beginning at line \\([0-9]+\\))" 1 2))
291 (compile (ectag-directory-command tag-directory tag-languages
) t
)))
294 ;; building tag completion obarray --------------------------------------------------
296 (defun extract-ectags (&optional tag-buffer obarray
)
297 "Extract a list of tags from a buffer"
300 (set-buffer tag-buffer
))
301 (goto-char (point-min))
303 ;; now point is at first tag
304 (while (/= (point) (point-max))
307 (let* ((start (point-marker))
308 (end (progn (forward-word) (point-marker))))
309 (intern-soft (buffer-substring-no-properties start end
) obarray
)
313 (defun extract-ectags-files (&optional tag-buffer
)
314 "Extract a list of tags from a tag-buffer"
318 (set-buffer tag-buffer
))
319 (goto-char (point-min))
321 ;; now point is at first tag
322 (while (search-forward "kind:file" (point-max) t
)
324 (when (search-forward " " (point-max) t
)
328 (search-forward " " (point-max) t
)
331 (add-to-list 'result
(buffer-substring-no-properties start end
)))
336 (defun make-ectags-obarray ()
337 (let ((result (make-vector 65535 0)))
341 (extract-ectags b result
))))
343 (setq *ectags-obarray
* result
)))
345 (defun flatten-file-list (l)
350 (setq stack
(cons (cdr l
)
353 (setq result
(cons l result
)
359 (defun make-ectags-file-list ()
360 "Create a list of all files in the tags"
363 (mapcar (lambda (b) (extract-ectags-files b
)) (ectags-table-list)))
364 (flatten-file-list result
)))
366 ;; tags table mode ------------------------------------------------------------------------------------
368 (defun ectags-table-list ()
369 "Return a list of available tag tables."
370 (let (tags-table-list)
371 (dolist (buffer (buffer-list) tags-table-list
)
372 (when (assoc 'is-ectag-table
(buffer-local-variables buffer
))
373 (push buffer tags-table-list
)))
377 (defvar ectags-table-mode-syntax-table
378 (let ((ectags-syntax-table text-mode-syntax-table
))
379 (modify-syntax-entry ?_
"w" ectags-syntax-table
)
380 (modify-syntax-entry ?-
"w" ectags-syntax-table
)
381 (modify-syntax-entry ?
# "w" ectags-syntax-table
)
382 (modify-syntax-entry ?
! "w" ectags-syntax-table
)
383 (modify-syntax-entry ?
\" "w" ectags-syntax-table
)
384 (modify-syntax-entry ?
& "w" ectags-syntax-table
)
385 (modify-syntax-entry ?
< "w" ectags-syntax-table
)
386 (modify-syntax-entry ?\
( "w" ectags-syntax-table
)
387 (modify-syntax-entry ?\
) "w" ectags-syntax-table
)
388 (modify-syntax-entry ?
: "w" ectags-syntax-table
)
389 (modify-syntax-entry ?\
; "w" ectags-syntax-table)
390 (modify-syntax-entry ??
"w" ectags-syntax-table
)
391 (modify-syntax-entry ?
@ "w" ectags-syntax-table
)
392 (modify-syntax-entry ?\
"w" ectags-syntax-table
)
393 (modify-syntax-entry ?\
[ "w" ectags-syntax-table
)
394 (modify-syntax-entry ?\
] "w" ectags-syntax-table
)
395 (modify-syntax-entry ?\
{ "w" ectags-syntax-table
)
396 (modify-syntax-entry ?\
} "w" ectags-syntax-table
)
397 (modify-syntax-entry ?|
"w" ectags-syntax-table
)
398 (modify-syntax-entry ?
\' "w" ectags-syntax-table
)
399 (modify-syntax-entry ?^
"w" ectags-syntax-table
)
400 (modify-syntax-entry ?
, "w" ectags-syntax-table
)
401 (modify-syntax-entry ?
` "w" ectags-syntax-table
)
402 (modify-syntax-entry ?~
"w" ectags-syntax-table
)
404 "Punctuation free table")
408 (defun ectags-table-mode ()
409 "Major mode for exuberant ctags table file buffers."
411 (kill-all-local-variables)
412 (setq major-mode
'ectags-table-mode
)
413 (set-syntax-table ectags-table-mode-syntax-table
)
414 (setq mode-name
"ECTags Tags Table")
415 (set (make-local-variable 'is-ectag-table
) t
))
419 ;; removing tags tables ------------------------------------------------------------------------------------
421 (defun ectags-wipe-tag-tables ()
422 "Wipe out all ectags tables"
426 (when (bufferp x
) (progn (bury-buffer x
) (kill-buffer x
))))
428 (setq *ectags-obarray
* nil
))
430 ;; -- adding tags tables ---------------------------------------------------------------------------------
433 ;; Expand tags table name FILE into a complete file name.
434 (defun ectags-expand-table-name (file)
435 (setq file
(expand-file-name file
))
436 (if (file-directory-p file
)
437 (expand-file-name "tags" file
)
440 ;; Return non-nil iff the current buffer is a valid etags TAGS file.
441 (defun ectags-verify-tags-table ()
442 "Is current buffer actually an ectags table."
443 ;; Use eq instead of = in case char-after returns nil.
444 (goto-char (point-min))
445 (looking-at "!_TAG_FILE_FORMAT"))
447 (defun ectags-verify-table (file)
448 "Given a file, read it in to a buffer and validate it as a tags table."
450 (message "Validating tags table %s " file
)
451 (if (get-file-buffer file
)
453 (set-buffer (get-file-buffer file
))
454 (unless (ectags-verify-tags-table)
456 (when (file-exists-p file
)
458 (set-buffer (find-file-noselect file
))
459 (when (ectags-verify-tags-table)
460 (ectags-table-mode)))))
461 (assoc 'is-ectag-table
(buffer-local-variables))))
464 (defun ectags-visit-tags-table (name)
465 "Visit an exuberant ctags file and add it to the current list of tags tables."
467 (list (read-file-name "Visit tags table (default tags): "
469 (expand-file-name "tags"
472 (let ((curbuf (current-buffer))
473 (local-tags-filename (ectags-expand-table-name name
)))
474 (if (ectags-verify-table local-tags-filename
)
475 ;; We have a valid tags table.
476 (progn (message "Valid tags table")
477 (setq *ectags-obarray
* (make-ectags-obarray)))
478 ;; The buffer was not valid. Don't use it again.
479 (progn (error "Not a valid tags table")))))
482 ;; -- saving and reloading sets of tag tables ------------------------------------------------
485 (defun ectags-output (tag-buffer)
486 "Output a line needed to restore this table to the tags buffer list"
487 (insert "(add-working-ectags-table " (buffer-file-name tag-buffer
) ")\n"))
489 (defun save-working-ectags-tables (fname)
490 "Save the current working list of ectags tables in a file"
491 (interactive "fFile to save tags tables in?:")
495 ";; -*- mode: fundamental; coding: emacs-mule; -*-\n"
496 ";; Created " (current-time-string) "\n"
497 ";; Emacs version " emacs-version
"\n\n"
498 (dolist (tagbuff (ectags-table-list))
499 (ectags-output tagbuff
))
501 (write-region (point min
) (point-max) fname nil
'nomessage
))))
503 (defun read-working-ectags-tables (fname)
504 "Read the current working list of ectags tables in a file"
505 (interactive "fFile to read tags tables from?:")
509 ;; actually finding tags and so forth ------------------------------------------------------------------------------------------------
511 (defun ectags-match-tagname (tag-match)
514 (defun ectags-match-filename (tag-match)
517 (defun ectags-match-linenumber (tag-match)
520 (defun ectags-match-tag-info (tag-match)
523 (defun ectags-fname (tag-match))
526 (defun match-ectags (tag fname lnumber info
)
527 "Given a tags match, rank it (via regexp match length) and
528 plonk it in the match candidates."
530 ((saved-fold-search case-fold-search
)
531 (case-fold-search (not *ectags-case-sensitive
*))
532 (match-rank (string-match *ectags-regexp
* tag
)))
535 ((full-match-rank (- (length tag
) (length *ectags-regexp
*))))
536 ;; (message (format "Found %s ranking %d " tag full-match-rank))
537 (add-to-list '*ectags-matches
*
542 (string-to-number lnumber
)
543 (ectags-trim info
)))))
544 (setq case-fold-search saved-fold-search
)))
546 (defun scan-ectag (fn tag-buffer
)
547 "Scan a tag table buffer for a match with a tag. Applies fn to all matches."
549 (set-buffer tag-buffer
)
550 (goto-char (point-min))
551 (while (re-search-forward (format "^\\([^ ]*%s[^ ]*\\) \\([^ ]+\\) \\([0-9]+\\);\"\\(.+\\)$" *ectags-regexp
*) nil t
)
552 (apply fn
(list (match-string-no-properties 1)
553 (match-string-no-properties 2)
554 (match-string-no-properties 3)
555 (match-string-no-properties 4))))))
557 (defun find-ectag (fn tag-buffer
)
558 "Scan a tag table buffer for an exact match with a tag"
560 (set-buffer tag-buffer
)
561 (goto-char (point-min))
562 (while (re-search-forward (format "^\\(%s\\) \\([^ ]+\\) \\([0-9]+\\);\"\\(.+\\)$" *ectags-regexp
*) nil t
)
563 (apply fn
(list (match-string-no-properties 1)
564 (match-string-no-properties 2)
565 (match-string-no-properties 3)
566 (match-string-no-properties 4))))))
568 (defun seek-ectag (regexp locate-fn
)
569 "Seek a match for the current regexp with the tags in the current tag table buffer"
570 (setq *ectags-matches
* nil
)
571 (setq *ectags-regexp
* regexp
)
572 (dolist (tags-buffer (ectags-table-list))
573 (funcall locate-fn
'match-ectags tags-buffer
)
574 (setq *ectags-matches
*
575 (sort *ectags-matches
* '(lambda (x y
)
576 (< (car x
) (car y
)))))))
579 ;; hiipe expand tag ----------------------------------------------------------
580 (defun he-ectag-beg ()
588 (defun try-expand-ectag (old)
590 (he-init-string (he-tag-beg) (point))
593 (all-completions he-search-string
*ectags-obarray
*) 'string-lessp
))
594 (while (and he-expand-list
595 (he-string-member (car he-expand-list
) he-tried-table
))
596 (setq he-expand-list
(cdr he-expand-list
))))
597 (if (null he-expand-list
)
599 (when old
(he-reset-string))
601 (he-substitute-string (car he-expand-list
))
602 (setq he-expand-list
(cdr he-expand-list
))
607 ;; ectags search ---------------------------------------------------------------------------------------------
610 (defun ectags-file-scan (file-list tag
)
611 "Scan the list of files for the tag and return a list of markers where it is found"
615 for file in file-list
617 (message "Scanning %s " file
)
620 (while (search-forward tag
(point-max) t
)
622 (message "Found in %s " file
)
623 (add-to-list 'result
(list file
(line-number-at-pos (point)))))
627 (defun reference-ectag (tag)
628 "Scan all currently tagged files for a tag and return a list of markers"
629 (let* ((file-list (make-ectags-file-list)))
630 (setq *ectags-scan-marks
* (ectags-file-scan file-list tag
))))
632 (defun next-ectag-reference ()
633 "Goto next ectag reference in current list, used as with tags-loop-continue"
635 (if (not (zerop (length *ectags-scan-marks
*)))
636 (let ((mark (car *ectags-scan-marks
*)))
637 (find-file (car mark
))
638 (forward-line (cadr mark
))
639 (setq *ectags-scan-marks
* (cdr *ectags-scan-marks
*)))
641 (or (find-tag-default)
642 (completing-read "Tag to reference " *ectags-obarray
*))))
643 (reference-ectag (tag))
644 (when (not (zerop (length *ectags-scan-marks
*)))
645 (next-ectag-reference)))))
648 (defun insert-ectag-references (tagname)
649 "Insert a refererence to a tag in an ectags-select buffer"
651 for index from
0 below
(length *ectags-scan-marks
*)
653 (let ((mark (nth index
*ectags-scan-marks
*)))
654 (insert "<" (int-to-string index
) ">:["
657 (int-to-string (cadr mark
)) "]\n"
660 (defun list-ectag-references (tag)
661 "List all references to the tag in a suitable buffer"
662 (setq *ectags-scan-marks
* nil
)
663 (setq *ectags-reference-source-buffer
* (buffer-name))
664 (get-buffer-create *ectags-reference-buffer-name
*)
665 (set-buffer *ectags-reference-buffer-name
*)
666 (setq buffer-read-only nil
)
668 (insert "Finding tag: " tagname
"\n")
669 (reference-ectag tag
)
670 (if (not (zerop (length *ectags-scan-marks
*)))
672 (insert-ectag-references tag
)
673 (set-buffer *ectags-reference-buffer-name
*)
674 (goto-char (point-min))
675 (set-buffer-modified-p nil
)
676 (setq buffer-read-only t
)
677 (setq *ectags-reference-opened-window
* (selected-window))
678 (unless (get-buffer-window *ectags-reference-buffer-name
*)
679 (select-window (split-window-vertically))
680 (switch-to-buffer *ectags-reference-buffer-name
*)
681 (ectags-select-mode))
682 (shrink-window-if-larger-than-buffer))
684 (message "Failed to find any references to tag %s " tagname
)
688 ;; ectags mode ------------------------------------------------------------------------------------------------
690 (defun ectags-select-case-fold-search ()
691 (when (boundp 'tags-case-fold-search
)
692 (if (memq tags-case-fold-search
'(nil t
))
693 tags-case-fold-search
696 (defun ectags-select-insert-matches (tagname)
697 (when *ectags-matches
*
698 (set-buffer *ectags-select-buffer-name
*)
699 (loop for index from
0 below
(min (length *ectags-matches
*) *ectags-max-candidates
*)
701 (let ((mtch (nth index
*ectags-matches
*)))
702 (insert "<" (int-to-string index
) ">:["
703 (ectags-match-tagname mtch
) " in "
704 (ectags-match-filename mtch
) "@"
705 (int-to-string (ectags-match-linenumber mtch
)) "]\n"
706 "*" (ectags-match-tag-info mtch
) "\n")))))
708 (defun ectags-select-find (tagname)
709 "Actually find a list of tags and push them into the tags select buffer"
710 (setq *ectags-select-source-buffer
* (buffer-name))
711 (get-buffer-create *ectags-select-buffer-name
*)
712 (set-buffer *ectags-select-buffer-name
*)
713 (setq buffer-read-only nil
)
715 (insert "Finding tag: " tagname
"\n")
716 (seek-ectag tagname
'scan-ectag
)
717 (if (> (length *ectags-matches
*) 0)
718 (progn (ectags-select-insert-matches tagname
)
719 (set-buffer *ectags-select-buffer-name
*)
720 (goto-char (point-min))
721 (ectags-select-next-tag)
722 (set-buffer-modified-p nil
)
723 (setq buffer-read-only t
)
724 (setq *ectags-select-opened-window
* (selected-window))
725 (unless (get-buffer-window *ectags-select-buffer-name
*)
726 (select-window (split-window-vertically))
727 (switch-to-buffer *ectags-select-buffer-name
*)
728 (ectags-select-mode))
729 (shrink-window-if-larger-than-buffer))
731 (message "Failed to find tag: %s " tagname
)
735 (defun ectags-select-goto-tag ()
736 "Goto the tag we currently have the point over in an ectags select mode window"
738 (let ((case-fold-search (not *ectags-case-sensitive
*)))
740 (goto-char (point-min))
741 (re-search-forward "Finding tag: \\(.*\\)$")
742 (setq tagname
(match-string-no-properties 1)))
744 (if (not (looking-at "<"))
745 (message "Please put the cursor on a line with a tag")
746 (setq tag-point
(point))
747 (setq overlay-arrow-position
(point-marker))
748 (re-search-forward "\\[\\([^ ]+\\) in \\([^@]+\\)@\\([0-9]+\\)")
749 (let ((tag (match-string-no-properties 1))
750 (fname (match-string-no-properties 2))
751 (lnno (match-string-no-properties 3)))
752 (ring-insert find-tag-marker-ring
(point-marker))
753 (find-file-other-window fname
)
754 (goto-char (point-min))
755 (forward-line (1- (string-to-int lnno
)))))))
758 (defun ectags-select-next-tag ()
759 "Move to next tag in buffer."
765 (defun ectags-select-previous-tag ()
766 "Move to previous tag in buffer."
772 (defun ectags-select-quit ()
773 "Quit ectags-select buffer."
778 (defun ectags-select-by-tag-number (first-digit)
779 (let ((tag-num (read-from-minibuffer "Tag number? " first-digit
))
780 (current-point (point)))
781 (goto-char (point-min))
782 (if (re-search-forward (concat "^<" tag-num
">") nil t
)
783 ;; TODO -- need to push tag and close window
784 (ectags-select-goto-tag)
785 (goto-char current-point
)
786 (message (concat "Couldn't find tag number " tag-num
))
790 ;; user commands ------------------------------------------------------------------------------------------------------
793 (defun ectags-select-find-tag-at-point ()
794 "Do a find-tag-at-point, and display all exact matches. If only one match is
795 found, see the `etags-select-no-select-for-one-match' variable to decide what
799 (or (find-tag-default)
800 (completing-read "Tag to find" *ectags-obarray
*))))
801 (ectags-select-find tag-to-find
)))
804 (defun ectags-select-reference-tag-at-point ()
805 "Do a search for tag in all files in tags tables and list all hits"
808 (or (find-tag-default)
809 (completing-read "Tag to find" *ectags-obarray
*))))
810 (list-ectag-references tag-to-find
)))
814 (defun ectags-select-find-tag ()
815 "Do a find-tag, and display all exact matches. If only one match is
816 found, see the `etags-select-no-select-for-one-match' variable to decide what
819 (let ((tagname (read-from-minibuffer
820 (format "Find tag (default %s): " (find-tag-default)) nil nil
821 nil
'find-tag-history
)))
822 (when (string= tagname
"")
823 (setq tagname
(find-tag-default)))
824 (ectags-select-find tagname
)))
827 (defun ectags-select-reference-tag ()
828 "Do a find-tag, and display all exact matches. If only one match is
829 found, see the `etags-select-no-select-for-one-match' variable to decide what
832 (let ((tagname (read-from-minibuffer
833 (format "Find tag (default %s): " (find-tag-default)) nil nil
834 nil
'find-tag-history
)))
835 (when (string= tagname
"")
836 (setq tagname
(find-tag-default)))
837 (list-ectag-references tagname
)))
839 ;; eldoc mode ------------------------------------------------------------------------------------------------
843 (defun c-eldoc-scope ()
844 "Try to figure out our scope"
847 (c-beginning-of-defun-1)
849 (c-syntactic-re-search-forward "::")
851 (when (c-on-identifier)
852 (let* ((id-end (point))
853 (id-start (progn (backward-char 1) (c-beginning-of-current-token) (point))))
854 (buffer-substring-no-properties id-start id-end
)))))
856 ;; finds the current function and position in argument list
858 (defun c-eldoc-function (&optional limit
)
859 (let* ((literal-limits (c-literal-limits))
860 (literal-type (c-literal-type literal-limits
)))
862 ;; if this is a string, move out to function domain
863 (when (eq literal-type
'string
)
864 (goto-char (car literal-limits
))
865 (setq literal-type nil
))
868 (when (c-on-identifier)
869 (let* ((id-on (point-marker))
871 (progn (c-beginning-of-current-token)
872 ;; are we looking at a double colon?
873 (if (and (= (char-before) ?
:)
874 (= (char-before (1- (point))) ?
:))
877 (c-beginning-of-current-token)
884 (c-end-of-current-token)
886 (buffer-substring-no-properties id-start id-end
)))))))
888 ;; non scoped verison for more conservative languages
890 (defun ectags-eldoc-print-current-symbol-info ()
891 "Print the ectags info associated with the current eldoc symbol"
892 (let* ((eldoc-sym (c-eldoc-function (- (point) 1000))))
893 (seek-ectag eldoc-sym
'find-ectag
)
894 (if (> (length *ectags-matches
*) 0)
895 (ectags-match-tag-info (car *ectags-matches
*))
896 (format "Unknown %s " eldoc-sym
))))
898 ;; scoped version for cpp and the like : tries to find symbol in current scope first
899 ;; scope format is a format string that concatenates the cureend scope and the symbol with the scope operator
900 ;; eg "%s::%s" for c++
902 (defun ectags-eldoc-print-current-scoped-symbol-info ()
903 "Try to find the meaning of the symbol in the current scope. Probably only useful for cpp mode"
904 (let* ((eldoc-scope (c-eldoc-scope))
905 (eldoc-sym (c-eldoc-function (- (point) 1000))))
907 (seek-ectag (format "%s::%s" eldoc-scope eldoc-sym
) 'find-ectag
)
908 (if (> (length *ectags-matches
*) 0)
909 (format "%s::%s %s" eldoc-scope eldoc-sym
(ectags-match-tag-info (car *ectags-matches
*)))
911 (seek-ectag eldoc-sym
'find-ectag
)
912 (if (> (length *ectags-matches
*) 0)
913 (format "%s %s" eldoc-sym
(ectags-match-tag-info (car *ectags-matches
*)))
915 (format "Scope %s " eldoc-scope
))
916 (format "Unknown %s " eldoc-sym
)))))))
919 (defun ectags-turn-on-eldoc-mode (&optional scope-format
)
922 (set (make-local-variable 'eldoc-documentation-function
)
923 'ectags-eldoc-print-current-scoped-symbol-info
)
924 (set (make-local-variable 'eldoc-documentation-function
)
925 'ectags-eldoc-print-current-symbol-info
)
926 (turn-on-eldoc-mode)))
929 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
933 (defvar ectags-select-mode-map nil
"'ectags-select-mode' keymap.")
934 (if (not ectags-select-mode-map
)
935 (let ((map (make-keymap)))
936 (define-key map
[(return)] 'ectags-select-goto-tag
)
937 (define-key map
[(down)] 'ectags-select-next-tag
)
938 (define-key map
[(up)] 'ectags-select-previous-tag
)
939 (define-key map
[(q)] 'ectags-select-quit
)
940 (define-key map
"0" (lambda () (interactive) (ectags-select-by-tag-number "0")))
941 (define-key map
"1" (lambda () (interactive) (ectags-select-by-tag-number "1")))
942 (define-key map
"2" (lambda () (interactive) (ectags-select-by-tag-number "2")))
943 (define-key map
"3" (lambda () (interactive) (ectags-select-by-tag-number "3")))
944 (define-key map
"4" (lambda () (interactive) (ectags-select-by-tag-number "4")))
945 (define-key map
"5" (lambda () (interactive) (ectags-select-by-tag-number "5")))
946 (define-key map
"6" (lambda () (interactive) (ectags-select-by-tag-number "6")))
947 (define-key map
"7" (lambda () (interactive) (ectags-select-by-tag-number "7")))
948 (define-key map
"8" (lambda () (interactive) (ectags-select-by-tag-number "8")))
949 (define-key map
"9" (lambda () (interactive) (ectags-select-by-tag-number "9")))
950 (setq ectags-select-mode-map map
)))
956 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
958 (defun ectags-select-mode ()
959 "ectags-select-mode is a mode for browsing through exuberant ctags.\n\n
960 \\{ectags-select-mode-map}"
962 (kill-all-local-variables)
963 (setq major-mode
'ectags-select-mode
)
964 (setq mode-name
"Ectags Select")
965 (set-syntax-table text-mode-syntax-table
)
966 (use-local-map ectags-select-mode-map
)
967 (make-local-variable 'font-lock-defaults
)
968 (setq ectags-select-mode-font-lock-keywords
970 (list "^<\\([0-9]+\\)>:\\[\\([^ ]+\\) in \\([^@]+\\)@\\([0-9]+)\\)\\]"
971 '(1 font-lock-warning-face
) '(2 font-lock-function-name-face
) '(3 font-lock-keyword-face
) '(4 font-lock-warning-face
))))
972 (setq font-lock-defaults
'(ectags-select-mode-font-lock-keywords))
973 (setq overlay-arrow-position nil
)
974 (run-hooks 'ectags-select-mode-hook
))