[net.emacs] tags.ml and ctags

hansen@pegasus.UUCP (11/07/84)

< Apparently our 1981 version of 'tags.ml' is not compatible with the current
< version of 'ctags', as the format of the tags file is different.  For example,
< tags.ml expacts to see ^A and ^B as delimiters.
< 	Is there a more recent version of tags.ml around?  Any help would be
< appreciated.

The ctags program does not produce a file which can be used directly by the
tags package. The tags file that tags.ml creates is what produces the ^A and
^B characters; the ctags format is totally different.  Enclosed below is our
most recent version of the tags library plus a macro package to take a tags
file created by the ctags program and convert it into a format usable by the
tags package.

					Tony Hansen
					pegasus!hansen

#!/bin/sh
# This is a shar archive.
# The rest of this file is a shell script which will extract:
# tags-pkg.ml ctags.ml
# Archive created: Wed Nov 7 13:25:47 EST 1984
echo x - tags-pkg.ml
sed 's/^X//' > tags-pkg.ml << '~FUNKY STUFF~'
; Edit 7
; Fri Jul 16 18:05:28 1982

; twenex-like tags package		J. Gosling, November 81
;
; A tag file is a sequence of lines of the following forms:

; ^_filename
; ^Atagline^Bposition

; A tagline/position pair refers to the preceeding file

(declare-global last-search-tag)

(defun
    (to-tag-buffer
	(temp-use-buffer "*TAG*")
	(if (& (= (buffer-size) 0) (= (current-file-name) ""))
	    (progn
		  (if (error-occured (read-file ".tags"))
		      (progn
			    (write-named-file ".tags")
			    (message "New tag file")))
		  (beginning-of-file)))
    ))

(defun
    (visit-tag-table tagfn
	(setq tagfn (arg 1 ": visit-tag-table "))
	(save-excursion
	    (temp-use-buffer "*TAG*")
	    (read-file tagfn))
    ))

(defun
    (goto-tag fn str pos restart
	(setq restart 0)
	(if (! prefix-argument-provided)
	    (progn
		  (setq last-search-tag
			(concat "\^A[^\^B]*" (quote (arg 1 ": goto-tag "))))
		  (setq restart 1)))
	(save-excursion
	    (to-tag-buffer)
	    (if restart (beginning-of-file))
	    (re-search-forward last-search-tag)
	    (beginning-of-line)
	    (re-search-forward "\^A\\([^\^B]*\\)\^B\\(.*\\)")
	    (region-around-match 1)
	    (setq str (region-to-string))
	    (region-around-match 2)
	    (setq pos (- (region-to-string) 300))
	    (save-excursion
		(re-search-reverse "\^_\\(.*\\)")
		(region-around-match 1)
		(setq fn (region-to-string)))
	)
	(visit-file fn)
	(goto-character pos)
	(if (error-occured (search-forward str))
	    (search-reverse ""))
	(beginning-of-line)
	(line-to-top-of-window)
    ))

(defun
    (find-pos-str
	(beginning-of-line)
	(setq pos (+ (dot) 0))
	(set-mark)
	(end-of-line)
	(setq str (region-to-string))))

(defun
    (store-pos-str
	(insert-character '^A')
	(insert-string str)
	(insert-character '^B')
	(insert-string pos)
	(newline)))

(defun
    (add-tag
	    (save-excursion pos str fn
		(find-pop-str)
		(setq fn (current-file-name))
		(to-tag-buffer)
		(beginning-of-file)
		(if (error-occured (re-search-forward
				       (concat "\^_" fn "[^\^_]*")))
		    (progn
			  (beginning-of-file)
			  (insert-character '^_')
			  (insert-string fn)
			  (newline)))
		(store-pos-str)
		(beginning-of-file))))

(defun
    (add-tag* pos str
	(find-pos-str)
	(save-excursion
	    (temp-use-buffer "*TAG*")
	    (store-pos-str))))

(defun
    (add-all-tags pattern fn
	(setq pattern (arg 1 ": add-all-tags (pattern) "))
	(setq fn (current-file-name))
	(save-excursion
	    (to-tag-buffer)
	    (if (error-occured (search-forward (concat "\^_" fn "\n")))
		(progn
		      (beginning-of-file)
		      (insert-character '^_')
		      (insert-string fn)
		      (newline))
		(progn
		      (set-mark)
		      (while (= (following-char) '^A')
			     (next-line))
		      (erase-region))
	    )
	)
	(save-excursion
	    (error-occured
		(beginning-of-file)
		(while 1
		       (re-search-forward pattern)
		       (add-tag*))))
	(novalue)
    )
)

(defun
    (add-typed-tags ext pattern
	(setq ext (substr (current-file-name) -2 2))
	(add-all-tags
	    (if
	       (= ext ".l") "^(def"
	       ;(= ext ".c") "^[A-z].*(.*)"		; TLH
	       (| (= ext ".c")
		  (= ext ".h")
		  (= ext ".C")
		  (= ext ".H"))
	       "^[A-z].*(.*)\\|^#[ \t]*define[ \t][ \t]*[A-Za-z_][A-Za-z_]*("
	       (= ext "ml") "^(defun[ \t\n]*("
	       (= ext "ss") "@section\\|@chapter\\|@subsection"
	       (= ext ".p") "function\\|procedure"
	       (error (concat "Can't tag " (current-file-name)))
	    )
	)
    ))

(defun
    (tag-file fn cur-file		; TLH added cur-file
	(if (is-bound current-file)
	    (setq cur-file current-file)
	    (setq cur-file ""))
	(setq fn (arg 1 ": tag-file (filename) "))
	(message (concat "Tagging " fn))
	(save-window-excursion
	    (error-occured
		(visit-file fn)
		(add-typed-tags)
		(if (& (!= cur-file fn)	; TLH
		       (= buffer-is-modified 0)); TLH
		    (delete-buffer fn))		; TLH
		))))

(defun
    (recompute-all-tags current-file	; TLH added current-file
	(setq current-file (current-file-name)); TLH
	(save-window-excursion
	    (to-tag-buffer)
	    (beginning-of-file)
	    (error-occured
		(while 1
		       (re-search-forward "\^_\\(.*\\)")
		       (region-around-match 1)
		       (tag-file (region-to-string)))
	    )
	    (write-named-file ".tags"))))

(defun
    (make-tag-table fns current-file	; TLH added current-file
	(setq fns (arg 1 ": make-tag-table (from filenames) "))
	(setq current-file (current-file-name)); TLH
	(save-window-excursion
	    (temp-use-buffer "*TEMP*")
	    (erase-buffer)
	    (set-mark)
	    (filter-region (concat "ls " fns))
	    (beginning-of-file)
	    (while (! (eobp))
		   (set-mark)
		   (end-of-line)
		   (tag-file (region-to-string))
		   (next-line)
		   (beginning-of-line))
	    (delete-buffer "*TEMP*")
	    (temp-use-buffer "*TAG*")
	    (write-named-file ".tags"))
	(novalue)
    ))

(defun
    (visit-function func
	(save-window-excursion
	    (forward-character)
	    (backward-word)
	    (set-mark)
	    (forward-word)
	    (setq func (region-to-string))
	    (goto-tag func)
	    (message "Type ^C to go back")
	    (recursive-edit)
	)
    )
)
~FUNKY STUFF~
ls -l tags-pkg.ml
echo x - ctags.ml
sed 's/^X//' > ctags.ml << '~FUNKY STUFF~'
; ctags-to-tags
; - take a file created by the BSD ctags(1) program and change it into the
;   format usable by the emacs tags facility.
;
; Tony Hansen, 1983

; ctags files are made of lines of the form:
;	function-name\tfilename\t?regular-expression?
; 
; emacs tag files are of the form
;	^_filename
;	^Aregular-expression^Bposition
;

(defun
    (ctags-to-tags tag-file new-tag-file
	(setq tag-file (arg 1 "Name of ctags file? [tags is default] "))
	(if (= tag-file "")
	    (setq tag-file "tags"))
	(setq new-tag-file (arg 2 "New tag file name [.tags by default] "))
	(if (= new-tag-file "")
	    (setq new-tag-file ".tags"))
	(message "Working....")
	(sit-for 0)

	(save-window-excursion
	    (switch-to-buffer "*temp-ctag*")
	    (setq needs-checkpointing 0)
	    (erase-buffer)
	    (insert-file tag-file)

	    ; change over-all format from
	    ;	function-name\tfilename\t?regular-expression?
	    ;	...
	    ; into
	    ;	^_filename
	    ;	^A?regular-expression?^Bfunction-name
	    ;	^_filename
	    ;	^A?regular-expression?^Bfunction-name
	    ;	...
	    ; sorted by filenames.
	    (progn
		  (beginning-of-file)
		  (re-replace-string
		      "^\\([^\t]*\\)\t\\([^\t]*\\)\t\\(.*\\)$"
		      "\^_\\2\^A\\3\^B\\1")
		  (set-mark)
		  (end-of-file)
		  (filter-region "sort")
		  (beginning-of-file)
		  (re-replace-string "\\(\^_[^\^A]*\\)\^A" "\\1\n\^A"))

	    ; Get rid of extra file names.
	    ; from
	    ;	^_filename
	    ;	^A?regular-expression?^Bfunction-name
	    ;	^_filename
	    ;	^A?regular-expression?^Bfunction-name
	    ;	...
	    ; into
	    ;	^_filename
	    ;	^A?regular-expression?^Bfunction-name
	    ;	^A?regular-expression?^Bfunction-name
	    ;	^A?regular-expression?^Bfunction-name
	    ;	...
	    (progn last-file current-file
		   (beginning-of-file)
		   (setq last-file "")
		   (while (! (error-occured (search-forward "\^_")))
			  (set-mark)
			  (end-of-line)
			  (setq current-file (region-to-string))
			  (if (= current-file last-file)
			      (progn
				    (beginning-of-line)
				    (kill-to-end-of-line)
				    (delete-next-character))
			      (setq last-file current-file))))

	    ; Change relative filename references into absolute ones.
	    (progn
		  (beginning-of-file)
		  (re-replace-string
		      "\^_\\([^/]\\)"
		      (concat "\^_" (working-directory) "\\1")
		  ))

	    ; change ctags ?regular-expression? lines into .tags format
	    ; and change function-name into absolute position reference
	    ;
	    ; from
	    ;	^_filename
	    ;	^A?regular-expression?^Bfunction-name
	    ;	^A?regular-expression?^Bfunction-name
	    ;	^A?regular-expression?^Bfunction-name
	    ;	...
	    ; into
	    ;	^_filename
	    ;	^Aregular-expression^Blocation
	    ;	^Aregular-expression^Blocation
	    ;	^Aregular-expression^Blocation
	    ;	...
	    (progn current-file last-file
		   (setq current-file "")
		   (beginning-of-file)
		   (if (! (looking-at "\^_"))
		       (error-message "Missing file name!"))
		   (while (! (eobp))
			  ; looking at a file name?
			  (if (looking-at "\^_\\(.*\\)")
			      (progn
				    (if (!= current-file "")
					(save-window-excursion
					    (visit-file current-file)
					    (if (! buffer-is-modified)
						(delete-buffer (current-buffer-name)))))
				    (region-around-match 1)
				    (setq current-file (region-to-string)))

			      ; looking at a function name?
			      (looking-at "\^A\\([^\^B]*\\)\^B\\(.*\\)")
			      (progn pattern function-name location
				     (region-around-match 1)
				     (setq pattern (region-to-string))
				     (region-around-match 2)
				     (setq function-name (region-to-string))
				     ; strip '?^' from beginning and
				     ; strip '$?' from end.
				     (if (= "?" (substr pattern 1 1))
					 (setq pattern (substr pattern 2 -1)))
				     (if (= "^" (substr pattern 1 1))
					 (setq pattern (substr pattern 2 -1)))
				     (if (= "?" (substr pattern -1 1))
					 (setq pattern (substr pattern 1 -1)))
				     (if (= "$" (substr pattern -1 1))
					 (setq pattern (substr pattern 1 -1)))
				     (save-excursion
					 (visit-file current-file)
					 (if (error-occured (re-search-forward pattern))
					     (if (error-occured (re-search-reverse pattern))
						(if (error-occured (re-search-forward function-name))
						   (if (error-occured (re-search-reverse function-name))
						      (error-message "Cannot find " function-name " in " current-file)))))
					 (beginning-of-line)
					 (setq location (+ (dot) 0)))
				     (beginning-of-line)
				     (kill-to-end-of-line)
				     (insert-string (concat "\^A" pattern
							    "\^B" location))
				     (end-of-line))
			      (error-message "Mal-formed tags line")
			  )
			  (error-occured (forward-character))))

	    (write-named-file new-tag-file))
	(delete-buffer "*temp-ctag*")
	(message "Done!")
	(novalue)
    )
)
~FUNKY STUFF~
ls -l ctags.ml
# The following exit is to ensure that extra garbage 
# after the end of the shar file will be ignored.
exit 0

mann@CSL-Vax.ARPA (Tim Mann) (11/08/84)

> Apparently our 1981 version of 'tags.ml' is not compatible with the current
> version of 'ctags', as the format of the tags file is different.  For example,
> tags.ml expacts to see ^A and ^B as delimiters.
> 	Is there a more recent version of tags.ml around?  Any help would be
> appreciated.

Here is a version of tags.ml I hacked up to work with ctags output.  (The
"ved" mentioned in the comment is an editor that runs on the V-System here
at Stanford.)  Hope no one minds the length of this posting to net.emacs.

; twenex-like tags package		J. Gosling, November 81
; modified to use ctags files		Tim Mann, 9-4-84
;
; As a hack, I've modified the Emacs tags package to use the "tags" files
; created by the ctags program instead of building its own.  This is a win for
; several reasons, the primary one being that ved (and also vi) uses this kind
; of tags file, so you only need one.  Another reason is that ctags builds a
; tags file much faster than Emacs can.
;
; To use the modified tags package, add the following to your .emacs_pro file:
;
; (autoload "goto-tag" "ctags.ml")
; (autoload "goto-tag-in-buffer" "ctags.ml")
; (bind-to-key "goto-tag" "\^Xt")
; (bind-to-key "goto-tag-in-buffer" "\^X\^T")
;
; This binds "goto-tag" to ^Xt and "goto-tag-in-buffer" to ^X^T.  Goto-tag
; prompts you for the name of a function or typedef, then visits the file
; containing the function or type definition at the first line of the
; definition.  If there is more than one object of the same name listed in 
; the tags file, ^Xt will find the first one, and repeatedly typing ^U^Xt 
; will step through the others.  Goto-tag-in-buffer is used when the cursor 
; is positioned on or just after the name of a function or type; it finds the 
; name in the buffer, then visits the file as in goto-tag.
;
; The first time you use one of these functions, it will look for the tags
; file in the current directory, and if none is found, in your home directory.
; From then on the same tags file will be used until you either exit or call
; the function "visit-tag-table" with a new file name.
;
; When you run ctags, you will probably want to use the switches -tw.  This
; causes tags to be generated for typedefs as well as functions, and suppresses
; (useless) warning messages.  Both emacs and ved (as of now) understand tags
; on both functions and typedefs.  (Macros too, I think.)
;

(declare-global last-search-tag)

(defun (to-tag-buffer
	   (temp-use-buffer "*TAG*")
	   (if (& (= (buffer-size) 0) (= (current-file-name) ""))
	       (progn
		   (if (error-occured (read-file "tags"))
		       (if (error-occured (read-file "~/tags"))
			   (message "Can't read tags file")
		       )
		   )
		   (beginning-of-file)
		   (error-occured (replace-string "*" "\\*"))
		   (setq buffer-is-modified 0)
	       )
	   )
       )
)

(defun (visit-tag-table tagfn
	   (setq tagfn (arg 1 ": visit-tag-table "))
	   (save-excursion
	       (temp-use-buffer "*TAG*")
	       (read-file tagfn)
	       (error-occured (replace-string "*" "\\*"))
	       (setq buffer-is-modified 0)
	   )
       )
)

(defun (goto-tag fn pat restart pos
	   (setq restart 0)
	   (if (! prefix-argument-provided)
	       (progn
		   (setq last-search-tag
		      (concat "^" (quote (arg 1 ": goto-tag ")) "[^\^I]*\^I"))
		   (setq restart 1)))
	   (setq pos -2)
	   (save-excursion
	       (to-tag-buffer)
	       (if restart (beginning-of-file))
	       (re-search-forward last-search-tag)
	       (set-mark)
	       (search-forward "\^I")
	       (backward-character)
	       (setq fn (region-to-string))
	       (forward-character)
	       (if (looking-at "/")
		   (progn 
			  (forward-character)
			  (set-mark)
			  (end-of-line)
			  (backward-character)
			  (setq pat (region-to-string))
		   )
		   (progn 
			  (set-mark)
			  (end-of-line)
			  (setq pos (- (region-to-string) 1))
		   )
	       )
	   )
	   
	   (visit-file fn)
	   (if (= pos -2)
	       (if (error-occured (re-search-forward pat))
	           (re-search-reverse "")
	       )
	       (progn 
		      (beginning-of-file)
	              (provide-prefix-argument pos (next-line))
	       )
	   )
	   (beginning-of-line)
	   (line-to-top-of-window)
       ))


; goto-tag-in-buffer
; Similar to describe-word-in-buffer
(defun
    (goto-tag-in-buffer
	(error-occured (forward-character))
	(backward-word)
	(set-mark)
	(forward-word)
	(goto-tag (region-to-string))
    )
)

grayson@uiucuxc.UUCP (11/08/84)

Thanks - I've learned that ctags is not part of the tags package, 
and that tags has a routine to make the tags table.

robert@gitpyr.UUCP (Robert Viduya) (11/09/84)

> Apparently our 1981 version of 'tags.ml' is not compatible with the current
> version of 'ctags', as the format of the tags file is different.  For example,
> tags.ml expacts to see ^A and ^B as delimiters.
> 	Is there a more recent version of tags.ml around?  Any help would be
> appreciated.

For some strange reason, I thought ctags was written for vi, not emacs.
-- 
Robert Viduya
Office of Computing Services
Georgia Institute of Technology, Atlanta GA 30332
Phone:  (404) 894-4669

...!{akgua,allegra,amd,hplabs,ihnp4,masscomp,ut-ngp}!gatech!gitpyr!robert
...!{rlgvax,sb1,uf-cgrl,unmvax,ut-sally}!gatech!gitpyr!robert

grayson@uiucuxc.UUCP (11/13/84)

Apparently our 1981 version of 'tags.ml' is not compatible with the current
version of 'ctags', as the format of the tags file is different.  For example,
tags.ml expacts to see ^A and ^B as delimiters.
	Is there a more recent version of tags.ml around?  Any help would be
appreciated.