Minor bugfixes.
[ectags.git] / ectags.el
blob6b18c06a74834d764b3f43963d0b25a25d952936
1 ; This buffer is for notes you don't want to save, and for Lisp evaluation.
2 ;; If you want to create a file, visit that file with C-x C-f,
3 ;; then enter the text in that file's own buffer.
5 ;; TO DO : tag completion DONE
6 ;; TO DO : tags search DONE
7 ;; TO DO : eldoc integration DONE
8 ;; TO DO : more than one lang per dir DONE
9 ;; TO DO : compile window for ectags DONE
10 ;; TO DO : do sthing about ginormous tag files
11 ;; TO DO : .. use abbreviated info
12 ;; TO DO : .. use gzipped tag files
13 ;; TO DO : use search for locating tag rather than line #
14 ;; TO DO : some kind of higlighting for select buffer
15 ;; TO DO : include line matched when searching for references
17 (require 'cl)
18 (require 'custom)
19 (require 'easymenu)
21 ;; Asm *.asm *.ASM *.s *.S *.A51 *.29[kK] *.[68][68][kKsSxX] *.[xX][68][68]
22 ;; Asp *.asp *.asa
23 ;; Awk *.awk *.gawk *.mawk
24 ;; BETA *.bet
25 ;; C *.c
26 ;; C++ *.c++ *.cc *.cp *.cpp *.cxx *.h *.h++ *.hh *.hp *.hpp *.hxx *.C *.H
27 ;; C# *.cs
28 ;; Cobol *.cbl *.cob *.CBL *.COB
29 ;; Eiffel *.e
30 ;; Erlang *.erl *.ERL *.hrl *.HRL
31 ;; Fortran *.f *.for *.ftn *.f77 *.f90 *.f95 *.F *.FOR *.FTN *.F77 *.F90 *.F95
32 ;; HTML *.htm *.html
33 ;; Java *.java
34 ;; JavaScript *.js
35 ;; Lisp *.cl *.clisp *.el *.l *.lisp *.lsp *.ml
36 ;; Lua *.lua
37 ;; Make *.mak *.mk [Mm]akefile
38 ;; Pascal *.p *.pas
39 ;; Perl *.pl *.pm *.plx *.perl
40 ;; PHP *.php *.php3 *.phtml
41 ;; Python *.py *.python
42 ;; REXX *.cmd *.rexx *.rx
43 ;; Ruby *.rb *.ruby
44 ;; Scheme *.SCM *.SM *.sch *.scheme *.scm *.sm
45 ;; Sh *.sh *.SH *.bsh *.bash *.ksh *.zsh
46 ;; SLang *.sl
47 ;; SML *.sml *.sig
48 ;; SQL *.sql
49 ;; Tcl *.tcl *.tk *.wish *.itcl
50 ;; Vera *.vr *.vri *.vrh
51 ;; Verilog *.v
52 ;; Vim *.vim
53 ;; YACC *.y
55 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
56 ;;; Custom stuff
59 ;;;###autoload
60 (defgroup ectags nil
61 "Exuberant Ctags Support for Emacs"
62 :version "22.1.1"
63 :group 'tools)
65 ;;;###autoload
66 (defcustom ectags-command "ectags"
67 "Name of the exuberant ctags executable on your system"
68 :type 'string
69 :group 'ectags)
71 ;;;###autoload
72 (defcustom ectags-config-file "~/.ectags"
73 "Name of the exuberant-ctags configuration file."
74 :type 'string
75 :group 'ectags)
77 ;;;###autoload
78 (defcustom ectags-language-file-suffix-alist
79 '(( "asp" . ( "*.asp" "*.asa" ))
80 ( "awk" . ( "*.awk" "*.gawk" "*.mawk"))
81 ( "c" . ( "*.c" "*.h" ))
82 ( "c++" . ( "*.c++" "*.cc" "*.cp" "*.cpp" "*.cxx" "*.h" "*.h++" "*.hh" "*.hp" "*.hpp" "*.hxx" "*.c" "*.C" "*.h" "*.H"))
83 ( "c#" . ( "*.cs" ))
84 ( "java" . ( "*.java " ))
85 ( "lisp" . ( "*.cl" "*.clisp" "*.el" "*.l" "*.lisp" "*.lsp" "*.ml"))
86 ( "python" . ( "*.py" "*.python" ))
87 ( "SQL" . ( "*.sql" ))
88 ( "Tcl" . ( "*.tcl" "*.tk" "*.wish" "*.itcl" )))
89 "Association list defining file masks for languages"
90 :type 'alist
91 :group 'ectags)
93 ;;;###autoload
94 (defcustom ectags-system-tag-table-list nil
95 "List of tags tables that include system headers"
96 :type 'list
97 :group 'ectags)
99 ;;;###autoload
100 (defcustom ectags-api-files
101 '(( "wx" . "/usr/local/include/wx" )
102 ( "gtk" . "/usr/include/gtk-2.0" )
103 ( "glib" . "/usr/include/glib-2.0" ))
104 "Association list mapping apis to directories"
105 :type 'alist
106 :group 'ectags)
109 ;;;###autoload
110 (defcustom ectags-select-mode-hook nil
111 "*List of functions to call on entry to ectags-select-mode mode."
112 :group 'ectags
113 :type 'hook)
115 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
116 ;;; Variables
118 (defvar *ectags-matches* nil
119 "List of candiate tag matches")
121 (defvar *ectags-regexp* nil
122 "Holds regexp currently being sought in tags")
124 (defvar *ectags-max-candidates* 7
125 "How many candidates to select between")
127 (defvar *ectags-case-sensitive* t
128 "Is the tag matching case sensitive?")
130 (defvar *ectags-autopop-tag* t
131 "If non-nil, automatically pop the tag off the tag stack when jumped to")
133 (defvar *ectags-tag-stack* nil
134 "Stack of tag positions for browsing.")
136 (defvar *ectags-obarray* nil
137 "Obarray used for ectags completions.")
139 (defvar *ectags-select-buffer-name* "*ectags-select*"
140 "ectags-select buffer name.")
142 (defvar *ectags-reference-buffer-name* "*ectag References*"
143 "ectags-reference buffer-name")
146 (defvar ectags-select-mode-font-lock-keywords nil
147 "ectags-select font-lock-keywords.")
149 (defvar *ectags-select-source-buffer* nil
150 "ectags-select source buffer tag was found from.")
152 (defvar *ectags-reference-source-buffer* nil
153 "ectags-reference source buffer tag was found from.")
155 (defvar *ectags-select-opened-window* nil
156 "ectags-select opened a select window.")
158 (defvar *ectags-reference-opened-window* nil
159 "ectags-referecnce opened a reference window.")
162 (defvar *ectags-scan-marks* nil
163 "Holds markers where matches found.")
165 (defconst ectags-select-non-tag-regexp "\\(\\s-*$\\|In:\\|Finding tag:\\)"
166 "ectags-select non-tag regex.")
169 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
170 ;;; Functions
172 ;; Klaus Berndl <klaus.berndl@sdm.de>: we have to take account that GNU Emacs
173 ;; > 21.3 has changed its split-string function! For the new split-string is
174 ;; (cdr (split-string ...)) not nil (at least in our context below), for GNU
175 ;; Emacs <= 21.3 nil!
176 (defun ectags-left-trim (str)
177 "Return a string stripped of all leading whitespaces of STR."
178 (let ((split-result (split-string str "^[\n\t ]*")))
179 (or (or (and (cdr split-result) ;; GNU Emacs > 21.3
180 (car (cdr split-result)))
181 (car split-result))
182 "")))
184 (defun ectags-right-trim (str)
185 "Return a string stripped of all trailing whitespaces of STR."
186 (or (car (split-string str "[\n\t ]*$")) ""))
188 (defun ectags-trim (str)
189 "Applies `ectags-right-trim' and `ectags-left-trim' to STR."
190 (ectags-left-trim (ectags-right-trim str)))
192 (defun ectags-full-trim (str)
193 "Applies `ectags-trim' and `ectags-middle-trim' to STR."
194 (ectags-excessive-trim (ectags-trim str)))
196 ;; creating tag files ------------------------------------------------------------------------------------
199 (defun make-suffix-clauses (languages)
200 (mapcar (lambda (l)
201 (mapcar (lambda (s)
202 (concat " -iname \"" s "\""))
203 (cdr (assoc-string l ectags-language-file-suffix-alist))))
204 (split-string languages)))
206 (defun make-shell-command-prefix (directory)
207 (concat "find " (expand-file-name directory)))
209 (defun make-tag-file-name (directory)
210 (expand-file-name (concat directory (string directory-sep-char) "tags")))
212 (defun ectag-directory-command (directory languages)
213 "Produce a command needed to scan the given directory for files
214 of the given language and produce tags"
215 (let*
216 ((suffix-clauses
217 (car (make-suffix-clauses languages)))
218 (shell-command-prefix
219 (make-shell-command-prefix directory))
220 (shell-command-suffix
221 (concat " | " ectags-command " -o " (make-tag-file-name directory) " --options="
222 (expand-file-name ectags-config-file) " --verbose --excmd=n --extra=+fq --fields=+afiKlmnsSzt --file-scope=no -L -")))
223 (concat shell-command-prefix
224 (car suffix-clauses)
225 (apply 'concat
226 (mapcar (lambda (s)
227 (concat " -o" s))
228 (cdr suffix-clauses)))
229 shell-command-suffix)))
233 ;;;###autoload
234 (defun ectags-tag-directory ()
235 "Prompt for a directory and a langage and create a tag file."
236 (interactive)
237 ;; prompt for directory
238 (let ((tag-directory
239 (read-directory-name "Directory to tag? " default-directory))
240 (tag-languages (completing-read "Languages to tag? " ectags-language-file-suffix-alist nil nil)))
241 (add-to-list 'compilation-error-regexp-alist
242 '("^\\([^:]+\\) confusing argument declarations beginning at line \\([0-9]+\\))" 1 2))
243 (compile (ectag-directory-command tag-directory tag-languages) t)))
246 ;; building tag completion obarray --------------------------------------------------
248 (defun extract-ectags (&optional tag-buffer obarray)
249 "Extract a list of tags from a buffer"
250 (save-excursion
251 (when tag-buffer
252 (set-buffer tag-buffer))
253 (goto-char (point-min))
254 (forward-line 5)
255 ;; now point is at first tag
256 (while (/= (point) (point-max))
257 (forward-line)
258 (beginning-of-line)
259 (let* ((start (point-marker))
260 (end (progn (forward-word) (point-marker))))
261 (intern-soft (buffer-substring-no-properties start end) obarray)
262 (end-of-line))))
263 obarray)
265 (defun extract-ectags-files (&optional tag-buffer)
266 "Extract a list of tags from a tag-buffer"
267 (let ((result nil))
268 (save-excursion
269 (when tag-buffer
270 (set-buffer tag-buffer))
271 (goto-char (point-min))
272 (forward-line 5)
273 ;; now point is at first tag
274 (while (search-forward "kind:file" (point-max) t)
275 (beginning-of-line)
276 (when (search-forward " " (point-max) t)
277 (let* ((start
278 (point-marker))
279 (end (progn
280 (search-forward " " (point-max) t)
281 (backward-char)
282 (point-marker))))
283 (add-to-list 'result (buffer-substring-no-properties start end)))
284 (end-of-line))))
285 result))
288 (defun make-ectags-obarray ()
289 (let ((result (make-vector 65535 0)))
290 (mapcar (lambda (b)
291 (when (bufferp b)
292 (save-excursion
293 (extract-ectags b result))))
294 (ectags-table-list))
295 (setq *ectags-obarray* result)))
297 (defun flatten-file-list (l)
298 (let (result stack)
299 (while (or stack l)
300 (if l
301 (if (consp l)
302 (setq stack (cons (cdr l)
303 stack)
304 l (car l))
305 (setq result (cons l result)
306 l nil))
307 (setq l (car stack)
308 stack (cdr stack))))
309 result))
311 (defun make-ectags-file-list ()
312 "Create a list of all files in the tags"
313 (let ((result nil))
314 (setq result
315 (mapcar (lambda (b) (extract-ectags-files b)) (ectags-table-list)))
316 (flatten-file-list result)))
318 ;; tags table mode ------------------------------------------------------------------------------------
320 (defun ectags-table-list ()
321 "Return a list of available tag tables."
322 (let (tags-table-list)
323 (dolist (buffer (buffer-list) tags-table-list)
324 (when (assoc 'is-ectag-table (buffer-local-variables buffer))
325 (push buffer tags-table-list)))
326 tags-table-list))
329 (defvar ectags-table-mode-syntax-table
330 (let ((ectags-syntax-table text-mode-syntax-table))
331 (modify-syntax-entry ?_ "w" ectags-syntax-table)
332 (modify-syntax-entry ?- "w" ectags-syntax-table)
333 (modify-syntax-entry ?# "w" ectags-syntax-table)
334 (modify-syntax-entry ?! "w" ectags-syntax-table)
335 (modify-syntax-entry ?\" "w" ectags-syntax-table)
336 (modify-syntax-entry ?& "w" ectags-syntax-table)
337 (modify-syntax-entry ?< "w" ectags-syntax-table)
338 (modify-syntax-entry ?\( "w" ectags-syntax-table)
339 (modify-syntax-entry ?\) "w" ectags-syntax-table)
340 (modify-syntax-entry ?: "w" ectags-syntax-table)
341 (modify-syntax-entry ?\; "w" ectags-syntax-table)
342 (modify-syntax-entry ?? "w" ectags-syntax-table)
343 (modify-syntax-entry ?@ "w" ectags-syntax-table)
344 (modify-syntax-entry ?\ "w" ectags-syntax-table)
345 (modify-syntax-entry ?\[ "w" ectags-syntax-table)
346 (modify-syntax-entry ?\] "w" ectags-syntax-table)
347 (modify-syntax-entry ?\{ "w" ectags-syntax-table)
348 (modify-syntax-entry ?\} "w" ectags-syntax-table)
349 (modify-syntax-entry ?| "w" ectags-syntax-table)
350 (modify-syntax-entry ?\' "w" ectags-syntax-table)
351 (modify-syntax-entry ?^ "w" ectags-syntax-table)
352 (modify-syntax-entry ?, "w" ectags-syntax-table)
353 (modify-syntax-entry ?` "w" ectags-syntax-table)
354 (modify-syntax-entry ?~ "w" ectags-syntax-table)
355 ectags-syntax-table)
356 "Punctuation free table")
359 ;;;###autoload
360 (defun ectags-table-mode ()
361 "Major mode for exuberant ctags table file buffers."
362 (interactive)
363 (kill-all-local-variables)
364 (setq major-mode 'ectags-table-mode)
365 (set-syntax-table ectags-table-mode-syntax-table)
366 (setq mode-name "ECTags Tags Table")
367 (set (make-local-variable 'is-ectag-table) t))
371 ;; removing tags tables ------------------------------------------------------------------------------------
373 (defun ectags-wipe-tag-tables ()
374 "Wipe out all ectags tables"
375 (interactive)
376 (mapcar
377 (lambda (x)
378 (when (bufferp x) (progn (bury-buffer x) (kill-buffer x))))
379 (ectags-table-list))
380 (setq *ectags-obarray* nil))
382 ;; -- adding tags tables ---------------------------------------------------------------------------------
385 ;; Expand tags table name FILE into a complete file name.
386 (defun ectags-expand-table-name (file)
387 (setq file (expand-file-name file))
388 (if (file-directory-p file)
389 (expand-file-name "tags" file)
390 file))
392 ;; Return non-nil iff the current buffer is a valid etags TAGS file.
393 (defun ectags-verify-tags-table ()
394 "Is current buffer actually an ectags table."
395 ;; Use eq instead of = in case char-after returns nil.
396 (goto-char (point-min))
397 (looking-at "!_TAG_FILE_FORMAT"))
399 (defun ectags-verify-table (file)
400 "Given a file, read it in to a buffer and validate it as a tags table."
401 (save-excursion
402 (message "Validating tags table %s " file)
403 (if (get-file-buffer file)
404 (progn
405 (set-buffer (get-file-buffer file))
406 (unless (ectags-verify-tags-table)
407 (fundamental-mode)))
408 (when (file-exists-p file)
409 (progn
410 (set-buffer (find-file-noselect file))
411 (when (ectags-verify-tags-table)
412 (ectags-table-mode)))))
413 (assoc 'is-ectag-table (buffer-local-variables))))
415 ;;;###autoload
416 (defun ectags-visit-tags-table (name)
417 "Visit an exuberant ctags file and add it to the current list of tags tables."
418 (interactive
419 (list (read-file-name "Visit tags table (default tags): "
420 default-directory
421 (expand-file-name "tags"
422 default-directory)
423 t)))
424 (let ((curbuf (current-buffer))
425 (local-tags-filename (ectags-expand-table-name name)))
426 (if (ectags-verify-table local-tags-filename)
427 ;; We have a valid tags table.
428 (progn (message "Valid tags table")
429 (setq *ectags-obarray* (make-ectags-obarray)))
430 ;; The buffer was not valid. Don't use it again.
431 (progn (error "Not a valid tags table")))))
434 ;; -- saving and reloading sets of tag tables ------------------------------------------------
437 (defun ectags-output (tag-buffer)
438 "Output a line needed to restore this table to the tags buffer list"
439 (insert "(add-working-ectags-table " (buffer-file-name tag-buffer) ")\n"))
441 (defun save-working-ectags-tables (fname)
442 "Save the current working list of ectags tables in a file"
443 (interactive "fFile to save tags tables in?:")
444 (save-excursion
445 (with-temp-buffer
446 (insert
447 ";; -*- mode: fundamental; coding: emacs-mule; -*-\n"
448 ";; Created " (current-time-string) "\n"
449 ";; Emacs version " emacs-version "\n\n"
450 (dolist (tagbuff (ectags-table-list))
451 (ectags-output tagbuff))
452 "\;;")
453 (write-region (point min) (point-max) fname nil 'nomessage))))
455 (defun read-working-ectags-tables (fname)
456 "Read the current working list of ectags tables in a file"
457 (interactive "fFile to read tags tables from?:")
458 (load fname))
461 ;; actually finding tags and so forth ------------------------------------------------------------------------------------------------
463 (defun ectags-match-tagname (tag-match)
464 (nth 1 tag-match))
466 (defun ectags-match-filename (tag-match)
467 (nth 2 tag-match))
469 (defun ectags-match-linenumber (tag-match)
470 (nth 3 tag-match))
472 (defun ectags-match-tag-info (tag-match)
473 (nth 4 tag-match))
475 (defun ectags-fname (tag-match))
478 (defun match-ectags (tag fname lnumber info)
479 "Given a tags match, rank it (via regexp match length) and
480 plonk it in the match candidates."
481 (let*
482 ((saved-fold-search case-fold-search)
483 (case-fold-search (not *ectags-case-sensitive*))
484 (match-rank (string-match *ectags-regexp* tag)))
485 (when match-rank
486 (let
487 ((full-match-rank (- (length tag) (length *ectags-regexp*))))
488 ;; (message (format "Found %s ranking %d " tag full-match-rank))
489 (add-to-list '*ectags-matches*
490 (list
491 full-match-rank
492 tag
493 fname
494 (string-to-number lnumber)
495 (ectags-trim info)))))
496 (setq case-fold-search saved-fold-search)))
498 (defun scan-ectag (fn tag-buffer)
499 "Scan a tag table buffer for a match with a tag. Applies fn to all matches."
500 (save-excursion
501 (set-buffer tag-buffer)
502 (goto-char (point-min))
503 (while (re-search-forward (format "^\\([^ ]*%s[^ ]*\\) \\([^ ]+\\) \\([0-9]+\\);\"\\(.+\\)$" *ectags-regexp*) nil t)
504 (apply fn (list (match-string-no-properties 1)
505 (match-string-no-properties 2)
506 (match-string-no-properties 3)
507 (match-string-no-properties 4))))))
509 (defun find-ectag (fn tag-buffer)
510 "Scan a tag table buffer for an exact match with a tag"
511 (save-excursion
512 (set-buffer tag-buffer)
513 (goto-char (point-min))
514 (while (re-search-forward (format "^\\(%s\\) \\([^ ]+\\) \\([0-9]+\\);\"\\(.+\\)$" *ectags-regexp*) nil t)
515 (apply fn (list (match-string-no-properties 1)
516 (match-string-no-properties 2)
517 (match-string-no-properties 3)
518 (match-string-no-properties 4))))))
520 (defun seek-ectag (regexp locate-fn)
521 "Seek a match for the current regexp with the tags in the current tag table buffer"
522 (setq *ectags-matches* nil)
523 (setq *ectags-regexp* regexp)
524 (dolist (tags-buffer (ectags-table-list))
525 (funcall locate-fn 'match-ectags tags-buffer)
526 (setq *ectags-matches*
527 (sort *ectags-matches* '(lambda (x y)
528 (< (car x) (car y)))))))
531 ;; hiipe expand tag ----------------------------------------------------------
532 (defun he-ectag-beg ()
533 (let ((p
534 (save-excursion
535 (backward-word 1)
536 (point))))
539 ;;;###autoload
540 (defun try-expand-ectag (old)
541 (unless old
542 (he-init-string (he-tag-beg) (point))
543 (setq he-expand-list
544 (sort
545 (all-completions he-search-string *ectags-obarray*) 'string-lessp))
546 (while (and he-expand-list
547 (he-string-member (car he-expand-list) he-tried-table))
548 (setq he-expand-list (cdr he-expand-list))))
549 (if (null he-expand-list)
550 (progn
551 (when old (he-reset-string))
553 (he-substitute-string (car he-expand-list))
554 (setq he-expand-list (cdr he-expand-list))
559 ;; ectags search ---------------------------------------------------------------------------------------------
562 (defun ectags-file-scan (file-list tag)
563 "Scan the list of files for the tag and return a list of markers where it is found"
564 (let ((result)
565 (found))
566 (loop
567 for file in file-list
568 do (save-excursion
569 (message "Scanning %s " file)
570 (find-file file)
571 (setq found nil)
572 (while (search-forward tag (point-max) t)
573 (setq found t)
574 (message "Found in %s " file)
575 (add-to-list 'result (list file (line-number-at-pos (point)))))
576 (kill-buffer nil)))
577 result))
579 (defun reference-ectag (tag)
580 "Scan all currently tagged files for a tag and return a list of markers"
581 (let* ((file-list (make-ectags-file-list)))
582 (setq *ectags-scan-marks* (ectags-file-scan file-list tag))))
584 (defun next-ectag-reference ()
585 "Goto next ectag reference in current list, used as with tags-loop-continue"
586 (interactive)
587 (if (not (zerop (length *ectags-scan-marks*)))
588 (let ((mark (car *ectags-scan-marks*)))
589 (find-file (car mark))
590 (forward-line (cadr mark))
591 (setq *ectags-scan-marks* (cdr *ectags-scan-marks*)))
592 (let ((ref-tag
593 (or (find-tag-default)
594 (completing-read "Tag to reference " *ectags-obarray*))))
595 (reference-ectag (tag))
596 (when (not (zerop (length *ectags-scan-marks*)))
597 (next-ectag-reference)))))
600 (defun insert-ectag-references (tagname)
601 "Insert a refererence to a tag in an ectags-select buffer"
602 (loop
603 for index from 0 below (length *ectags-scan-marks*)
605 (let ((mark (nth index *ectags-scan-marks*)))
606 (insert "<" (int-to-string index) ">:["
607 tagname " in "
608 (car mark) "@"
609 (int-to-string (cadr mark)) "]\n"
610 "*" "\n"))))
612 (defun list-ectag-references (tag)
613 "List all references to the tag in a suitable buffer"
614 (setq *ectags-scan-marks* nil)
615 (setq *ectags-reference-source-buffer* (buffer-name))
616 (get-buffer-create *ectags-reference-buffer-name*)
617 (set-buffer *ectags-reference-buffer-name*)
618 (setq buffer-read-only nil)
619 (erase-buffer)
620 (insert "Finding tag: " tagname "\n")
621 (reference-ectag tag)
622 (if (not (zerop (length *ectags-scan-marks*)))
623 (progn
624 (insert-ectag-references tag)
625 (set-buffer *ectags-reference-buffer-name*)
626 (goto-char (point-min))
627 (set-buffer-modified-p nil)
628 (setq buffer-read-only t)
629 (setq *ectags-reference-opened-window* (selected-window))
630 (unless (get-buffer-window *ectags-reference-buffer-name*)
631 (select-window (split-window-vertically))
632 (switch-to-buffer *ectags-reference-buffer-name*)
633 (ectags-select-mode))
634 (shrink-window-if-larger-than-buffer))
635 (progn
636 (message "Failed to find any references to tag %s " tagname)
637 (ding))))
640 ;; ectags mode ------------------------------------------------------------------------------------------------
642 (defun ectags-select-case-fold-search ()
643 (when (boundp 'tags-case-fold-search)
644 (if (memq tags-case-fold-search '(nil t))
645 tags-case-fold-search
646 case-fold-search)))
648 (defun ectags-select-insert-matches (tagname)
649 (when *ectags-matches*
650 (set-buffer *ectags-select-buffer-name*)
651 (loop for index from 0 below (min (length *ectags-matches*) *ectags-max-candidates*)
653 (let ((mtch (nth index *ectags-matches*)))
654 (insert "<" (int-to-string index) ">:["
655 (ectags-match-tagname mtch) " in "
656 (ectags-match-filename mtch) "@"
657 (int-to-string (ectags-match-linenumber mtch)) "]\n"
658 "*" (ectags-match-tag-info mtch) "\n")))))
660 (defun ectags-select-find (tagname)
661 "Actually find a list of tags and push them into the tags select buffer"
662 (setq *ectags-select-source-buffer* (buffer-name))
663 (get-buffer-create *ectags-select-buffer-name*)
664 (set-buffer *ectags-select-buffer-name*)
665 (setq buffer-read-only nil)
666 (erase-buffer)
667 (insert "Finding tag: " tagname "\n")
668 (seek-ectag tagname 'scan-ectag)
669 (if (> (length *ectags-matches*) 0)
670 (progn (ectags-select-insert-matches tagname)
671 (set-buffer *ectags-select-buffer-name*)
672 (goto-char (point-min))
673 (ectags-select-next-tag)
674 (set-buffer-modified-p nil)
675 (setq buffer-read-only t)
676 (setq *ectags-select-opened-window* (selected-window))
677 (unless (get-buffer-window *ectags-select-buffer-name*)
678 (select-window (split-window-vertically))
679 (switch-to-buffer *ectags-select-buffer-name*)
680 (ectags-select-mode))
681 (shrink-window-if-larger-than-buffer))
682 (progn
683 (message "Failed to find tag: %s " tagname)
684 (ding))))
687 (defun ectags-select-goto-tag ()
688 "Goto the tag we currently have the point over in an ectags select mode window"
689 (interactive)
690 (let ((case-fold-search (not *ectags-case-sensitive*)))
691 (save-excursion
692 (goto-char (point-min))
693 (re-search-forward "Finding tag: \\(.*\\)$")
694 (setq tagname (match-string-no-properties 1)))
695 (beginning-of-line)
696 (if (not (looking-at "<"))
697 (message "Please put the cursor on a line with a tag")
698 (setq tag-point (point))
699 (setq overlay-arrow-position (point-marker))
700 (re-search-forward "\\[\\([^ ]+\\) in \\([^@]+\\)@\\([0-9]+\\)")
701 (let ((tag (match-string-no-properties 1))
702 (fname (match-string-no-properties 2))
703 (lnno (match-string-no-properties 3)))
704 (ring-insert find-tag-marker-ring (point-marker))
705 (find-file-other-window fname)
706 (goto-char (point-min))
707 (forward-line (1- (string-to-int lnno)))))))
710 (defun ectags-select-next-tag ()
711 "Move to next tag in buffer."
712 (interactive)
713 (beginning-of-line)
714 (forward-line))
717 (defun ectags-select-previous-tag ()
718 "Move to previous tag in buffer."
719 (interactive)
720 (beginning-of-line)
721 (forward-line -1))
724 (defun ectags-select-quit ()
725 "Quit ectags-select buffer."
726 (interactive)
727 (kill-buffer nil)
728 (delete-window))
730 (defun ectags-select-by-tag-number (first-digit)
731 (let ((tag-num (read-from-minibuffer "Tag number? " first-digit))
732 (current-point (point)))
733 (goto-char (point-min))
734 (if (re-search-forward (concat "^<" tag-num ">") nil t)
735 ;; TODO -- need to push tag and close window
736 (ectags-select-goto-tag)
737 (goto-char current-point)
738 (message (concat "Couldn't find tag number " tag-num))
739 (ding))))
742 ;; user commands ------------------------------------------------------------------------------------------------------
744 ;;;###autoload
745 (defun ectags-select-find-tag-at-point ()
746 "Do a find-tag-at-point, and display all exact matches. If only one match is
747 found, see the `etags-select-no-select-for-one-match' variable to decide what
748 to do."
749 (interactive)
750 (let ((tag-to-find
751 (or (find-tag-default)
752 (completing-read "Tag to find" *ectags-obarray*))))
753 (ectags-select-find tag-to-find)))
755 ;;;###autoload
756 (defun ectags-select-reference-tag-at-point ()
757 "Do a search for tag in all files in tags tables and list all hits"
758 (interactive)
759 (let ((tag-to-find
760 (or (find-tag-default)
761 (completing-read "Tag to find" *ectags-obarray*))))
762 (list-ectag-references tag-to-find)))
765 ;;;###autoload
766 (defun ectags-select-find-tag ()
767 "Do a find-tag, and display all exact matches. If only one match is
768 found, see the `etags-select-no-select-for-one-match' variable to decide what
769 to do."
770 (interactive)
771 (let ((tagname (read-from-minibuffer
772 (format "Find tag (default %s): " (find-tag-default)) nil nil
773 nil 'find-tag-history)))
774 (when (string= tagname "")
775 (setq tagname (find-tag-default)))
776 (ectags-select-find tagname)))
778 ;;;###autoload
779 (defun ectags-select-reference-tag ()
780 "Do a find-tag, and display all exact matches. If only one match is
781 found, see the `etags-select-no-select-for-one-match' variable to decide what
782 to do."
783 (interactive)
784 (let ((tagname (read-from-minibuffer
785 (format "Find tag (default %s): " (find-tag-default)) nil nil
786 nil 'find-tag-history)))
787 (when (string= tagname "")
788 (setq tagname (find-tag-default)))
789 (list-ectag-references tagname)))
791 ;; eldoc mode ------------------------------------------------------------------------------------------------
794 ;;;###autoload
795 (defun c-eldoc-scope ()
796 "Try to figure out our scope"
797 (save-excursion
798 (c-end-of-defun)
799 (c-beginning-of-defun-1)
800 (forward-line -1)
801 (c-syntactic-re-search-forward "::")
802 (backward-char 2)
803 (when (c-on-identifier)
804 (let* ((id-end (point))
805 (id-start (progn (backward-char 1) (c-beginning-of-current-token) (point))))
806 (buffer-substring-no-properties id-start id-end)))))
808 ;; finds the current function and position in argument list
809 ;;;###autoload
810 (defun c-eldoc-function (&optional limit)
811 (let* ((literal-limits (c-literal-limits))
812 (literal-type (c-literal-type literal-limits)))
813 (save-excursion
814 ;; if this is a string, move out to function domain
815 (when (eq literal-type 'string)
816 (goto-char (car literal-limits))
817 (setq literal-type nil))
818 (if literal-type
820 (when (c-on-identifier)
821 (let* ((id-on (point-marker))
822 (id-start
823 (progn (c-beginning-of-current-token)
824 ;; are we looking at a double colon?
825 (if (and (= (char-before) ?:)
826 (= (char-before (1- (point))) ?:))
827 (progn
828 (backward-char 3)
829 (c-beginning-of-current-token)
830 (point-marker))
831 (point-marker))))
832 (id-end
833 (progn
834 (goto-char id-on)
835 (forward-char)
836 (c-end-of-current-token)
837 (point-marker))))
838 (buffer-substring-no-properties id-start id-end)))))))
840 ;; non scoped verison for more conservative languages
841 ;;;###autoload
842 (defun ectags-eldoc-print-current-symbol-info ()
843 "Print the ectags info associated with the current eldoc symbol"
844 (let* ((eldoc-sym (c-eldoc-function (- (point) 1000))))
845 (seek-ectag eldoc-sym 'find-ectag)
846 (if (> (length *ectags-matches*) 0)
847 (ectags-match-tag-info (car *ectags-matches*))
848 (format "Unknown %s " eldoc-sym))))
850 ;; scoped version for cpp and the like : tries to find symbol in current scope first
851 ;; scope format is a format string that concatenates the cureend scope and the symbol with the scope operator
852 ;; eg "%s::%s" for c++
853 ;;;###autoload
854 (defun ectags-eldoc-print-current-scoped-symbol-info ()
855 "Try to find the meaning of the symbol in the current scope. Probably only useful for cpp mode"
856 (let* ((eldoc-scope (c-eldoc-scope))
857 (eldoc-sym (c-eldoc-function (- (point) 1000))))
858 (when eldoc-sym
859 (seek-ectag (format "%s::%s" eldoc-scope eldoc-sym) 'find-ectag)
860 (if (> (length *ectags-matches*) 0)
861 (format "%s::%s %s" eldoc-scope eldoc-sym (ectags-match-tag-info (car *ectags-matches*)))
862 (progn
863 (seek-ectag eldoc-sym 'find-ectag)
864 (if (> (length *ectags-matches*) 0)
865 (format "%s %s" eldoc-sym (ectags-match-tag-info (car *ectags-matches*)))
866 (if eldoc-scope
867 (format "Scope %s " eldoc-scope))
868 (format "Unknown %s " eldoc-sym)))))))
870 ;;;###autoload
871 (defun ectags-turn-on-eldoc-mode (&optional scope-format)
872 (interactive)
873 (if scope-format
874 (set (make-local-variable 'eldoc-documentation-function)
875 'ectags-eldoc-print-current-scoped-symbol-info)
876 (set (make-local-variable 'eldoc-documentation-function)
877 'ectags-eldoc-print-current-symbol-info)
878 (turn-on-eldoc-mode)))
881 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
882 ;;; Keymap
885 (defvar ectags-select-mode-map nil "'ectags-select-mode' keymap.")
886 (if (not ectags-select-mode-map)
887 (let ((map (make-keymap)))
888 (define-key map [(return)] 'ectags-select-goto-tag)
889 (define-key map [(down)] 'ectags-select-next-tag)
890 (define-key map [(up)] 'ectags-select-previous-tag)
891 (define-key map [(q)] 'ectags-select-quit)
892 (define-key map "0" (lambda () (interactive) (ectags-select-by-tag-number "0")))
893 (define-key map "1" (lambda () (interactive) (ectags-select-by-tag-number "1")))
894 (define-key map "2" (lambda () (interactive) (ectags-select-by-tag-number "2")))
895 (define-key map "3" (lambda () (interactive) (ectags-select-by-tag-number "3")))
896 (define-key map "4" (lambda () (interactive) (ectags-select-by-tag-number "4")))
897 (define-key map "5" (lambda () (interactive) (ectags-select-by-tag-number "5")))
898 (define-key map "6" (lambda () (interactive) (ectags-select-by-tag-number "6")))
899 (define-key map "7" (lambda () (interactive) (ectags-select-by-tag-number "7")))
900 (define-key map "8" (lambda () (interactive) (ectags-select-by-tag-number "8")))
901 (define-key map "9" (lambda () (interactive) (ectags-select-by-tag-number "9")))
902 (setq ectags-select-mode-map map)))
908 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
909 ;;; Mode startup
910 (defun ectags-select-mode ()
911 "ectags-select-mode is a mode for browsing through exuberant ctags.\n\n
912 \\{ectags-select-mode-map}"
913 (interactive)
914 (kill-all-local-variables)
915 (setq major-mode 'ectags-select-mode)
916 (setq mode-name "Ectags Select")
917 (set-syntax-table text-mode-syntax-table)
918 (use-local-map ectags-select-mode-map)
919 (make-local-variable 'font-lock-defaults)
920 (setq ectags-select-mode-font-lock-keywords
921 (list
922 (list "^<\\([0-9]+\\)>:\\[\\([^ ]+\\) in \\([^@]+\\)@\\([0-9]+)\\)\\]"
923 '(1 font-lock-warning-face) '(2 font-lock-function-name-face) '(3 font-lock-keyword-face) '(4 font-lock-warning-face))))
924 (setq font-lock-defaults '(ectags-select-mode-font-lock-keywords))
925 (setq overlay-arrow-position nil)
926 (run-hooks 'ectags-select-mode-hook))
929 (provide 'ectags)