Added better comments.
[ectags.git] / ectags.el
blob0db15e117b7c57765b9d56aae8d585e5d264b669
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>
8 ;;
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)
14 ;; any later version.
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.
26 ;;; Commentary:
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
42 ;; code.
45 ;; An up to date version of this code lives on repo.or.cz
46 ;; at git://repo.or.cz/ectags.git
48 ;;; Code:
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
65 (require 'cl)
66 (require 'custom)
67 (require 'easymenu)
69 ;; Asm *.asm *.ASM *.s *.S *.A51 *.29[kK] *.[68][68][kKsSxX] *.[xX][68][68]
70 ;; Asp *.asp *.asa
71 ;; Awk *.awk *.gawk *.mawk
72 ;; BETA *.bet
73 ;; C *.c
74 ;; C++ *.c++ *.cc *.cp *.cpp *.cxx *.h *.h++ *.hh *.hp *.hpp *.hxx *.C *.H
75 ;; C# *.cs
76 ;; Cobol *.cbl *.cob *.CBL *.COB
77 ;; Eiffel *.e
78 ;; Erlang *.erl *.ERL *.hrl *.HRL
79 ;; Fortran *.f *.for *.ftn *.f77 *.f90 *.f95 *.F *.FOR *.FTN *.F77 *.F90 *.F95
80 ;; HTML *.htm *.html
81 ;; Java *.java
82 ;; JavaScript *.js
83 ;; Lisp *.cl *.clisp *.el *.l *.lisp *.lsp *.ml
84 ;; Lua *.lua
85 ;; Make *.mak *.mk [Mm]akefile
86 ;; Pascal *.p *.pas
87 ;; Perl *.pl *.pm *.plx *.perl
88 ;; PHP *.php *.php3 *.phtml
89 ;; Python *.py *.python
90 ;; REXX *.cmd *.rexx *.rx
91 ;; Ruby *.rb *.ruby
92 ;; Scheme *.SCM *.SM *.sch *.scheme *.scm *.sm
93 ;; Sh *.sh *.SH *.bsh *.bash *.ksh *.zsh
94 ;; SLang *.sl
95 ;; SML *.sml *.sig
96 ;; SQL *.sql
97 ;; Tcl *.tcl *.tk *.wish *.itcl
98 ;; Vera *.vr *.vri *.vrh
99 ;; Verilog *.v
100 ;; Vim *.vim
101 ;; YACC *.y
103 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
104 ;;; Custom stuff
107 ;;;###autoload
108 (defgroup ectags nil
109 "Exuberant Ctags Support for Emacs"
110 :version "22.1.1"
111 :group 'tools)
113 ;;;###autoload
114 (defcustom ectags-command "ectags"
115 "Name of the exuberant ctags executable on your system"
116 :type 'string
117 :group 'ectags)
119 ;;;###autoload
120 (defcustom ectags-config-file "~/.ectags"
121 "Name of the exuberant-ctags configuration file."
122 :type 'string
123 :group 'ectags)
125 ;;;###autoload
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"))
131 ( "c#" . ( "*.cs" ))
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"
138 :type 'alist
139 :group 'ectags)
141 ;;;###autoload
142 (defcustom ectags-system-tag-table-list nil
143 "List of tags tables that include system headers"
144 :type 'list
145 :group 'ectags)
147 ;;;###autoload
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"
153 :type 'alist
154 :group 'ectags)
157 ;;;###autoload
158 (defcustom ectags-select-mode-hook nil
159 "*List of functions to call on entry to ectags-select-mode mode."
160 :group 'ectags
161 :type 'hook)
163 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
164 ;;; Variables
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
218 ;;; Functions
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)))
229 (car split-result))
230 "")))
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)
248 (mapcar (lambda (l)
249 (mapcar (lambda (s)
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"
263 (let*
264 ((suffix-clauses
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
272 (car suffix-clauses)
273 (apply 'concat
274 (mapcar (lambda (s)
275 (concat " -o" s))
276 (cdr suffix-clauses)))
277 shell-command-suffix)))
281 ;;;###autoload
282 (defun ectags-tag-directory ()
283 "Prompt for a directory and a langage and create a tag file."
284 (interactive)
285 ;; prompt for directory
286 (let ((tag-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"
298 (save-excursion
299 (when tag-buffer
300 (set-buffer tag-buffer))
301 (goto-char (point-min))
302 (forward-line 5)
303 ;; now point is at first tag
304 (while (/= (point) (point-max))
305 (forward-line)
306 (beginning-of-line)
307 (let* ((start (point-marker))
308 (end (progn (forward-word) (point-marker))))
309 (intern-soft (buffer-substring-no-properties start end) obarray)
310 (end-of-line))))
311 obarray)
313 (defun extract-ectags-files (&optional tag-buffer)
314 "Extract a list of tags from a tag-buffer"
315 (let ((result nil))
316 (save-excursion
317 (when tag-buffer
318 (set-buffer tag-buffer))
319 (goto-char (point-min))
320 (forward-line 5)
321 ;; now point is at first tag
322 (while (search-forward "kind:file" (point-max) t)
323 (beginning-of-line)
324 (when (search-forward " " (point-max) t)
325 (let* ((start
326 (point-marker))
327 (end (progn
328 (search-forward " " (point-max) t)
329 (backward-char)
330 (point-marker))))
331 (add-to-list 'result (buffer-substring-no-properties start end)))
332 (end-of-line))))
333 result))
336 (defun make-ectags-obarray ()
337 (let ((result (make-vector 65535 0)))
338 (mapcar (lambda (b)
339 (when (bufferp b)
340 (save-excursion
341 (extract-ectags b result))))
342 (ectags-table-list))
343 (setq *ectags-obarray* result)))
345 (defun flatten-file-list (l)
346 (let (result stack)
347 (while (or stack l)
348 (if l
349 (if (consp l)
350 (setq stack (cons (cdr l)
351 stack)
352 l (car l))
353 (setq result (cons l result)
354 l nil))
355 (setq l (car stack)
356 stack (cdr stack))))
357 result))
359 (defun make-ectags-file-list ()
360 "Create a list of all files in the tags"
361 (let ((result nil))
362 (setq result
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)))
374 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)
403 ectags-syntax-table)
404 "Punctuation free table")
407 ;;;###autoload
408 (defun ectags-table-mode ()
409 "Major mode for exuberant ctags table file buffers."
410 (interactive)
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"
423 (interactive)
424 (mapcar
425 (lambda (x)
426 (when (bufferp x) (progn (bury-buffer x) (kill-buffer x))))
427 (ectags-table-list))
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)
438 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."
449 (save-excursion
450 (message "Validating tags table %s " file)
451 (if (get-file-buffer file)
452 (progn
453 (set-buffer (get-file-buffer file))
454 (unless (ectags-verify-tags-table)
455 (fundamental-mode)))
456 (when (file-exists-p file)
457 (progn
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))))
463 ;;;###autoload
464 (defun ectags-visit-tags-table (name)
465 "Visit an exuberant ctags file and add it to the current list of tags tables."
466 (interactive
467 (list (read-file-name "Visit tags table (default tags): "
468 default-directory
469 (expand-file-name "tags"
470 default-directory)
471 t)))
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?:")
492 (save-excursion
493 (with-temp-buffer
494 (insert
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))
500 "\;;")
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?:")
506 (load fname))
509 ;; actually finding tags and so forth ------------------------------------------------------------------------------------------------
511 (defun ectags-match-tagname (tag-match)
512 (nth 1 tag-match))
514 (defun ectags-match-filename (tag-match)
515 (nth 2 tag-match))
517 (defun ectags-match-linenumber (tag-match)
518 (nth 3 tag-match))
520 (defun ectags-match-tag-info (tag-match)
521 (nth 4 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."
529 (let*
530 ((saved-fold-search case-fold-search)
531 (case-fold-search (not *ectags-case-sensitive*))
532 (match-rank (string-match *ectags-regexp* tag)))
533 (when match-rank
534 (let
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*
538 (list
539 full-match-rank
540 tag
541 fname
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."
548 (save-excursion
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"
559 (save-excursion
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 ()
581 (let ((p
582 (save-excursion
583 (backward-word 1)
584 (point))))
587 ;;;###autoload
588 (defun try-expand-ectag (old)
589 (unless old
590 (he-init-string (he-tag-beg) (point))
591 (setq he-expand-list
592 (sort
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)
598 (progn
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"
612 (let ((result)
613 (found))
614 (loop
615 for file in file-list
616 do (save-excursion
617 (message "Scanning %s " file)
618 (find-file file)
619 (setq found nil)
620 (while (search-forward tag (point-max) t)
621 (setq found t)
622 (message "Found in %s " file)
623 (add-to-list 'result (list file (line-number-at-pos (point)))))
624 (kill-buffer nil)))
625 result))
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"
634 (interactive)
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*)))
640 (let ((ref-tag
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"
650 (loop
651 for index from 0 below (length *ectags-scan-marks*)
653 (let ((mark (nth index *ectags-scan-marks*)))
654 (insert "<" (int-to-string index) ">:["
655 tagname " in "
656 (car mark) "@"
657 (int-to-string (cadr mark)) "]\n"
658 "*" "\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)
667 (erase-buffer)
668 (insert "Finding tag: " tagname "\n")
669 (reference-ectag tag)
670 (if (not (zerop (length *ectags-scan-marks*)))
671 (progn
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))
683 (progn
684 (message "Failed to find any references to tag %s " tagname)
685 (ding))))
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
694 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)
714 (erase-buffer)
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))
730 (progn
731 (message "Failed to find tag: %s " tagname)
732 (ding))))
735 (defun ectags-select-goto-tag ()
736 "Goto the tag we currently have the point over in an ectags select mode window"
737 (interactive)
738 (let ((case-fold-search (not *ectags-case-sensitive*)))
739 (save-excursion
740 (goto-char (point-min))
741 (re-search-forward "Finding tag: \\(.*\\)$")
742 (setq tagname (match-string-no-properties 1)))
743 (beginning-of-line)
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."
760 (interactive)
761 (beginning-of-line)
762 (forward-line))
765 (defun ectags-select-previous-tag ()
766 "Move to previous tag in buffer."
767 (interactive)
768 (beginning-of-line)
769 (forward-line -1))
772 (defun ectags-select-quit ()
773 "Quit ectags-select buffer."
774 (interactive)
775 (kill-buffer nil)
776 (delete-window))
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))
787 (ding))))
790 ;; user commands ------------------------------------------------------------------------------------------------------
792 ;;;###autoload
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
796 to do."
797 (interactive)
798 (let ((tag-to-find
799 (or (find-tag-default)
800 (completing-read "Tag to find" *ectags-obarray*))))
801 (ectags-select-find tag-to-find)))
803 ;;;###autoload
804 (defun ectags-select-reference-tag-at-point ()
805 "Do a search for tag in all files in tags tables and list all hits"
806 (interactive)
807 (let ((tag-to-find
808 (or (find-tag-default)
809 (completing-read "Tag to find" *ectags-obarray*))))
810 (list-ectag-references tag-to-find)))
813 ;;;###autoload
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
817 to do."
818 (interactive)
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)))
826 ;;;###autoload
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
830 to do."
831 (interactive)
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 ------------------------------------------------------------------------------------------------
842 ;;;###autoload
843 (defun c-eldoc-scope ()
844 "Try to figure out our scope"
845 (save-excursion
846 (c-end-of-defun)
847 (c-beginning-of-defun-1)
848 (forward-line -1)
849 (c-syntactic-re-search-forward "::")
850 (backward-char 2)
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
857 ;;;###autoload
858 (defun c-eldoc-function (&optional limit)
859 (let* ((literal-limits (c-literal-limits))
860 (literal-type (c-literal-type literal-limits)))
861 (save-excursion
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))
866 (if literal-type
868 (when (c-on-identifier)
869 (let* ((id-on (point-marker))
870 (id-start
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))) ?:))
875 (progn
876 (backward-char 3)
877 (c-beginning-of-current-token)
878 (point-marker))
879 (point-marker))))
880 (id-end
881 (progn
882 (goto-char id-on)
883 (forward-char)
884 (c-end-of-current-token)
885 (point-marker))))
886 (buffer-substring-no-properties id-start id-end)))))))
888 ;; non scoped verison for more conservative languages
889 ;;;###autoload
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++
901 ;;;###autoload
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))))
906 (when eldoc-sym
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*)))
910 (progn
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*)))
914 (if eldoc-scope
915 (format "Scope %s " eldoc-scope))
916 (format "Unknown %s " eldoc-sym)))))))
918 ;;;###autoload
919 (defun ectags-turn-on-eldoc-mode (&optional scope-format)
920 (interactive)
921 (if 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
930 ;;; Keymap
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
957 ;;; Mode startup
958 (defun ectags-select-mode ()
959 "ectags-select-mode is a mode for browsing through exuberant ctags.\n\n
960 \\{ectags-select-mode-map}"
961 (interactive)
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
969 (list
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))
977 (provide 'ectags)