diff lisp/cedet/semantic/texi.el @ 104419:b1ac14799f78

cedet/semantic/analyze.el, cedet/semantic/complete.el, cedet/semantic/edit.el, cedet/semantic/html.el, cedet/semantic/idle.el, cedet/semantic/texi.el: New files. cedet/semantic/lex.el: Move defsubsts to front of file to avoid compiler error.
author Chong Yidong <cyd@stupidchicken.com>
date Sat, 29 Aug 2009 19:00:35 +0000
parents
children da5b2513c225
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/cedet/semantic/texi.el	Sat Aug 29 19:00:35 2009 +0000
@@ -0,0 +1,677 @@
+;;; texi.el --- Semantic details for Texinfo files
+
+;;; Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009
+;;; Free Software Foundation, Inc.
+
+;; Author: Eric M. Ludlam <zappo@gnu.org>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Parse Texinfo buffers using regular expressions.  The core parser
+;; engine is the function `semantic-texi-parse-headings'.  The
+;; parser plug-in is the function `semantic-texi-parse-region' that
+;; overrides `semantic-parse-region'.
+
+(require 'semantic)
+(require 'semantic/format)
+(require 'texinfo)
+
+(eval-when-compile
+  (require 'semantic/db)
+  (require 'semantic/db-find)
+  (require 'semantic/ctxt)
+  (require 'semantic/imenu)
+  (require 'semantic/doc)
+  (require 'senator))
+
+(defvar semantic-texi-super-regex
+  "^@\\(top\\|chapter\\|\\(sub\\)*section\\|unnumbered\\(\\(sub\\)*sec\\)?\\|\
+\\(chap\\|\\(sub\\)+\\|major\\)?heading\\|appendix\\(\\(sub\\)*sec\\)?\\|\
+centerchap\\|def\\(var\\|un\\|fn\\|opt\\)x?\\)"
+  "Regular expression used to find special sections in a Texinfo file.")
+
+(defvar semantic-texi-name-field-list
+  '( ("defvar" . 1)
+     ("defvarx" . 1)
+     ("defun" . 1)
+     ("defunx" . 1)
+     ("defopt" . 1)
+     ("deffn" . 2)
+     ("deffnx" . 2)
+     )
+  "List of definition commands, and the field position.
+The field position is the field number (based at 1) where the
+name of this section is.")
+
+;;; Code:
+(defun semantic-texi-parse-region (&rest ignore)
+  "Parse the current texinfo buffer for semantic tags.
+IGNORE any arguments, always parse the whole buffer.
+Each tag returned is of the form:
+ (\"NAME\" section (:members CHILDREN))
+or
+ (\"NAME\" def)
+
+It is an override of 'parse-region and must be installed by the
+function `semantic-install-function-overrides'."
+  (mapcar 'semantic-texi-expand-tag
+          (semantic-texi-parse-headings)))
+
+(defun semantic-texi-parse-changes ()
+  "Parse changes in the current texinfo buffer."
+  ;; NOTE: For now, just schedule a full reparse.
+  ;;       To be implemented later.
+  (semantic-parse-tree-set-needs-rebuild))
+
+(defun semantic-texi-expand-tag (tag)
+  "Expand the texinfo tag TAG."
+  (let ((chil (semantic-tag-components tag)))
+    (if chil
+        (semantic-tag-put-attribute
+         tag :members (mapcar 'semantic-texi-expand-tag chil)))
+    (car (semantic--tag-expand tag))))
+
+(defun semantic-texi-parse-headings ()
+  "Parse the current texinfo buffer for all semantic tags now."
+  (let ((pass1 nil))
+    ;; First search and snarf.
+    (save-excursion
+      (goto-char (point-min))
+      (let ((semantic--progress-reporter
+	     (make-progress-reporter
+	      (format "Parsing %s..."
+		      (file-name-nondirectory buffer-file-name))
+	      (point-min) (point-max))))
+	(while (re-search-forward semantic-texi-super-regex nil t)
+	  (setq pass1 (cons (match-beginning 0) pass1))
+	  (progress-reporter-update semantic--progress-reporter (point)))
+	(progress-reporter-done semantic--progress-reporter)))
+    (setq pass1 (nreverse pass1))
+    ;; Now, make some tags while creating a set of children.
+    (car (semantic-texi-recursive-combobulate-list pass1 0))
+    ))
+
+(defsubst semantic-texi-new-section-tag (name members start end)
+  "Create a semantic tag of class section.
+NAME is the name of this section.
+MEMBERS is a list of semantic tags representing the elements that make
+up this section.
+START and END define the location of data described by the tag."
+  (append (semantic-tag name 'section :members members)
+          (list start end)))
+
+(defsubst semantic-texi-new-def-tag (name start end)
+  "Create a semantic tag of class def.
+NAME is the name of this definition.
+START and END define the location of data described by the tag."
+  (append (semantic-tag name 'def)
+          (list start end)))
+
+(defun semantic-texi-set-endpoint (metataglist pnt)
+  "Set the end point of the first section tag in METATAGLIST to PNT.
+METATAGLIST is a list of tags in the intermediate tag format used by the
+texinfo parser.  PNT is the new point to set."
+  (let ((metatag nil))
+    (while (and metataglist
+		(not (eq (semantic-tag-class (car metataglist)) 'section)))
+      (setq metataglist (cdr metataglist)))
+    (setq metatag (car metataglist))
+    (when metatag
+      (setcar (nthcdr (1- (length metatag)) metatag) pnt)
+      metatag)))
+
+(defun semantic-texi-recursive-combobulate-list (sectionlist level)
+  "Rearrange SECTIONLIST to be a hierarchical tag list starting at LEVEL.
+Return the rearranged new list, with all remaining tags from
+SECTIONLIST starting at ELT 2.  Sections not are not dealt with as soon as a
+tag with greater section value than LEVEL is found."
+  (let ((newl nil)
+	(oldl sectionlist)
+        tag
+	)
+    (save-excursion
+      (catch 'level-jump
+	(while oldl
+	  (goto-char (car oldl))
+	  (if (looking-at "@\\(\\w+\\)")
+	      (let* ((word (match-string 1))
+		     (levelmatch (assoc word texinfo-section-list))
+		     text begin tmp
+		     )
+		;; Set begin to the right location
+		(setq begin (point))
+		;; Get out of here if there if we made it that far.
+		(if (and levelmatch (<= (car (cdr levelmatch)) level))
+		    (progn
+		      (when newl
+			(semantic-texi-set-endpoint newl begin))
+		      (throw 'level-jump t)))
+		;; Recombobulate
+		(if levelmatch
+		    (let ((end (match-end 1)))
+		      ;; Levels sometimes have a @node just in front.
+		      ;; That node statement should be included in the space
+		      ;; for this entry.
+		      (save-excursion
+			(skip-chars-backward "\n \t")
+			(beginning-of-line)
+			(when (looking-at "@node\\>")
+			  (setq begin (point))))
+		      ;; When there is a match, the descriptive text
+		      ;; consists of the rest of the line.
+		      (goto-char end)
+		      (skip-chars-forward " \t")
+		      (setq text (buffer-substring-no-properties
+				  (point)
+				  (progn (end-of-line) (point))))
+		      ;; Next, recurse into the body to find the end.
+		      (setq tmp (semantic-texi-recursive-combobulate-list
+				 (cdr oldl) (car (cdr levelmatch))))
+		      ;; Build a tag
+                      (setq tag (semantic-texi-new-section-tag
+                                 text (car tmp) begin (point)))
+		      ;; Before appending the newtag, update the previous tag
+		      ;; if it is a section tag.
+		      (when newl
+			(semantic-texi-set-endpoint newl begin))
+		      ;; Append new tag to our master list.
+		      (setq newl (cons tag newl))
+		      ;; continue
+		      (setq oldl (cdr tmp))
+		      )
+		  ;; No match means we have a def*, so get the name from
+		  ;; it based on the type of thingy we found.
+		  (setq levelmatch (assoc word semantic-texi-name-field-list)
+			tmp (or (cdr levelmatch) 1))
+		  (forward-sexp tmp)
+		  (skip-chars-forward " \t")
+		  (setq text (buffer-substring-no-properties
+			      (point)
+			      (progn (forward-sexp 1) (point))))
+		  ;; Seek the end of this definition
+		  (goto-char begin)
+		  (semantic-texi-forward-deffn)
+                  (setq tag (semantic-texi-new-def-tag text begin (point))
+                        newl (cons tag newl))
+		  ;; continue
+		  (setq oldl (cdr oldl)))
+		)
+	    (error "Problem finding section in semantic/texi parser"))
+	  ;; (setq oldl (cdr oldl))
+	  )
+	;; When oldl runs out, force a new endpoint as point-max
+	(when (not oldl)
+	  (semantic-texi-set-endpoint newl (point-max)))
+	))
+    (cons (nreverse newl) oldl)))
+
+(defun semantic-texi-forward-deffn ()
+  "Move forward over one deffn type definition.
+The cursor should be on the @ sign."
+  (when (looking-at "@\\(\\w+\\)")
+    (let* ((type (match-string 1))
+	   (seek (concat "^@end\\s-+" (regexp-quote type))))
+      (re-search-forward seek nil t))))
+
+(define-mode-local-override semantic-tag-components
+  texinfo-mode (tag)
+  "Return components belonging to TAG."
+  (semantic-tag-get-attribute tag :members))
+
+
+;;; Overrides: Context Parsing
+;;
+;; How to treat texi as a language?
+;;
+(defvar semantic-texi-environment-regexp
+  (if (string-match texinfo-environment-regexp "@menu")
+      ;; Make sure our Emacs has menus in it.
+      texinfo-environment-regexp
+    ;; If no menus, then merge in the menu concept.
+    (when (string-match "cartouche" texinfo-environment-regexp)
+      (concat (substring texinfo-environment-regexp
+			 0 (match-beginning 0))
+	      "menu\\|"
+	       (substring texinfo-environment-regexp
+			 (match-beginning 0)))))
+  "Regular expression for matching texinfo enviroments.
+uses `texinfo-environment-regexp', but makes sure that it
+can handle the @menu environment.")
+
+(define-mode-local-override semantic-up-context texinfo-mode ()
+  "Handle texinfo constructs which do not use parenthetical nesting."
+  (let ((done nil))
+    (save-excursion
+      (let ((parenthetical (semantic-up-context-default))
+	    )
+	(when (not parenthetical)
+	  ;; We are in parenthises.  Are they the types of parens
+	  ;; belonging to a texinfo construct?
+	  (forward-word -1)
+	  (when (looking-at "@\\w+{")
+	    (setq done (point))))))
+    ;; If we are not in a parenthetical node, then find a block instead.
+    ;; Use the texinfo support to find block start/end constructs.
+    (save-excursion
+      (while (and (not done)
+		  (re-search-backward  semantic-texi-environment-regexp nil t))
+	;; For any hit, if we find an @end foo, then jump to the
+	;; matching @foo.  If it is not an end, then we win!
+	(if (not (looking-at "@end\\s-+\\(\\w+\\)"))
+	    (setq done (point))
+	  ;; Skip over this block
+	  (let ((env (match-string 1)))
+	    (re-search-backward (concat "@" env))))
+	))
+    ;; All over, post what we find.
+    (if done
+	;; We found something, so use it.
+	(progn (goto-char done)
+	       nil)
+      t)))
+
+(define-mode-local-override semantic-beginning-of-context texinfo-mode (&optional point)
+  "Move to the beginning of the context surrounding POINT."
+  (if (semantic-up-context point)
+      ;; If we can't go up, we can't do this either.
+      t
+    ;; We moved, so now we need to skip into whatever this thing is.
+    (forward-word 1) ;; skip the command
+    (if (looking-at "\\s-*{")
+	;; In a short command.  Go in.
+	(down-list 1)
+      ;; An environment.  Go to the next line.
+      (end-of-line)
+      (forward-char 1))
+    nil))
+
+(define-mode-local-override semantic-ctxt-current-class-list
+  texinfo-mode (&optional point)
+  "Determine the class of tags that can be used at POINT.
+For texinfo, there two possibilities returned.
+1) 'function - for a call to a texinfo function
+2) 'word     - indicates an english word.
+It would be nice to know function arguments too, but not today."
+  (let ((sym (semantic-ctxt-current-symbol)))
+    (if (and sym (= (aref (car sym) 0) ?@))
+	'(function)
+      '(word))))
+
+
+;;; Overrides : Formatting
+;;
+;; Various override to better format texi tags.
+;;
+
+(define-mode-local-override semantic-format-tag-abbreviate
+  texinfo-mode  (tag &optional parent color)
+  "Texinfo tags abbreviation."
+  (let ((class (semantic-tag-class tag))
+	(name (semantic-format-tag-name tag parent color))
+	)
+    (cond ((eq class 'function)
+	   (concat name "{ }"))
+	  (t (semantic-format-tag-abbreviate-default tag parent color)))
+    ))
+
+(define-mode-local-override semantic-format-tag-prototype
+  texinfo-mode  (tag &optional parent color)
+  "Texinfo tags abbreviation."
+  (semantic-format-tag-abbreviate tag parent color))
+
+
+;;; Texi Unique Features
+;;
+(defun semantic-tag-texi-section-text-bounds (tag)
+  "Get the bounds to the text of TAG.
+The text bounds is the text belonging to this node excluding
+the text of any child nodes, but including any defuns."
+  (let ((memb (semantic-tag-components tag)))
+    ;; Members.. if one is a section, check it out.
+    (while (and memb (not (semantic-tag-of-class-p (car memb) 'section)))
+      (setq memb (cdr memb)))
+    ;; No members? ... then a simple problem!
+    (if (not memb)
+	(semantic-tag-bounds tag)
+      ;; Our end is their beginning...
+      (list (semantic-tag-start tag) (semantic-tag-start (car memb))))))
+
+(defun semantic-texi-current-environment (&optional point)
+  "Return as a string the type of the current environment.
+Optional argument POINT is where to look for the environment."
+  (save-excursion
+    (when point (goto-char (point)))
+    (while (and (or (not (looking-at  semantic-texi-environment-regexp))
+		    (looking-at "@end"))
+		(not (semantic-up-context)))
+      )
+    (when (looking-at  semantic-texi-environment-regexp)
+      (match-string 1))))
+
+
+;;; Analyzer
+;;
+(eval-when-compile
+  (require 'semantic/analyze))
+
+(define-mode-local-override semantic-analyze-current-context
+  texinfo-mode (point)
+  "Analysis context makes no sense for texinfo.  Return nil."
+  (let* ((prefixandbounds (semantic-ctxt-current-symbol-and-bounds (point)))
+	 (prefix (car prefixandbounds))
+	 (bounds (nth 2 prefixandbounds))
+	 (prefixclass (semantic-ctxt-current-class-list))
+	 )
+    (when prefix
+      (require 'semantic-analyze)
+      (semantic-analyze-context
+       "Context-for-texinfo"
+       :buffer (current-buffer)
+       :scope nil
+       :bounds bounds
+       :prefix prefix
+       :prefixtypes nil
+       :prefixclass prefixclass)
+      )
+    ))
+
+(defvar semantic-texi-command-completion-list
+  (append (mapcar (lambda (a) (car a)) texinfo-section-list)
+	  (condition-case nil
+	      texinfo-environments
+	    (error
+	     ;; XEmacs doesn't use the above.  Split up its regexp
+	     (split-string texinfo-environment-regexp "\\\\|\\|\\^@\\\\(\\|\\\\)")
+	     ))
+	  ;; Is there a better list somewhere?  Here are few
+	  ;; of the top of my head.
+	  "anchor" "asis"
+	  "bullet"
+	  "code" "copyright"
+	  "defun" "deffn" "defoption" "defvar" "dfn"
+	  "emph" "end"
+	  "ifinfo" "iftex" "inforef" "item" "itemx"
+	  "kdb"
+	  "node"
+	  "ref"
+	  "set" "setfilename" "settitle"
+	  "value" "var"
+	  "xref"
+	  )
+  "List of commands that we might bother completing.")
+
+(define-mode-local-override semantic-analyze-possible-completions
+  texinfo-mode (context)
+  "List smart completions at point.
+Since texinfo is not a programming language the default version is not
+useful.  Insted, look at the current symbol.  If it is a command
+do primitive texinfo built ins.  If not, use ispell to lookup words
+that start with that symbol."
+  (let ((prefix (car (oref context :prefix)))
+	)
+    (cond ((member 'function (oref context :prefixclass))
+	   ;; Do completion for texinfo commands
+	   (let* ((cmd (substring prefix 1))
+		  (lst (all-completions
+			cmd semantic-texi-command-completion-list)))
+	     (mapcar (lambda (f) (semantic-tag (concat "@" f) 'function))
+		     lst))
+	   )
+	  ((member 'word (oref context :prefixclass))
+	   ;; Do completion for words via ispell.
+	   (require 'ispell)
+	   (let ((word-list (lookup-words prefix)))
+	     (mapcar (lambda (f) (semantic-tag f 'word)) word-list))
+	   )
+	  (t nil))
+    ))
+
+
+;;; Parser Setup
+;;
+(defun semantic-default-texi-setup ()
+  "Set up a buffer for parsing of Texinfo files."
+  ;; This will use our parser.
+  (semantic-install-function-overrides
+   '((parse-region . semantic-texi-parse-region)
+     (parse-changes . semantic-texi-parse-changes)))
+  (setq semantic-parser-name "TEXI"
+        ;; Setup a dummy parser table to enable parsing!
+        semantic--parse-table t
+        imenu-create-index-function 'semantic-create-imenu-index
+	semantic-command-separation-character "@"
+	semantic-type-relation-separator-character '(":")
+	semantic-symbol->name-assoc-list '((section . "Section")
+					   (def . "Definition")
+					   )
+	semantic-imenu-expandable-tag-classes '(section)
+	semantic-imenu-bucketize-file nil
+	semantic-imenu-bucketize-type-members nil
+	senator-step-at-start-end-tag-classes '(section)
+	semantic-stickyfunc-sticky-classes '(section)
+	)
+  (local-set-key [(f9)] 'semantic-texi-update-doc-from-texi)
+  )
+
+(add-hook 'texinfo-mode-hook 'semantic-default-texi-setup)
+
+
+;;; Special features of Texinfo tag streams
+;;
+;; This section provides specialized access into texinfo files.
+;; Because texinfo files often directly refer to functions and programs
+;; it is useful to access the texinfo file from the C code for document
+;; maintainance.
+(defun semantic-texi-associated-files (&optional buffer)
+  "Find texinfo files associated with BUFFER."
+  (save-excursion
+    (if buffer (set-buffer buffer))
+    (cond ((and (fboundp 'ede-documentation-files)
+                ede-minor-mode (ede-current-project))
+	   ;; When EDE is active, ask it.
+	   (ede-documentation-files)
+	   )
+	  ((and (featurep 'semanticdb) (semanticdb-minor-mode-p))
+	   ;; See what texinfo files we have loaded in the database
+	   (let ((tabs (semanticdb-get-database-tables
+			semanticdb-current-database))
+		 (r nil))
+	     (while tabs
+	       (if (eq (oref (car tabs) major-mode) 'texinfo-mode)
+		   (setq r (cons (oref (car tabs) file) r)))
+	       (setq tabs (cdr tabs)))
+	     r))
+	  (t
+	   (directory-files default-directory nil "\\.texi$"))
+	  )))
+
+;; Turns out this might not be useful.
+;; Delete later if that is true.
+(defun semantic-texi-find-documentation (name &optional type)
+  "Find the function or variable NAME of TYPE in the texinfo source.
+NAME is a string representing some functional symbol.
+TYPE is a string, such as \"variable\" or \"Command\" used to find
+the correct definition in case NAME qualifies as several things.
+When this function exists, POINT is at the definition.
+If the doc was not found, an error is thrown.
+Note: TYPE not yet implemented."
+  (let ((f (semantic-texi-associated-files))
+	stream match)
+    (while (and f (not match))
+      (unless stream
+	(with-current-buffer (find-file-noselect (car f))
+	  (setq stream (semantic-fetch-tags))))
+      (setq match (semantic-find-first-tag-by-name name stream))
+      (when match
+	(set-buffer (semantic-tag-buffer match))
+	(goto-char (semantic-tag-start match)))
+      (setq f (cdr f)))))
+
+(defun semantic-texi-update-doc-from-texi (&optional tag)
+  "Update the documentation in the texinfo deffn class tag TAG.
+The current buffer must be a texinfo file containing TAG.
+If TAG is nil, determine a tag based on the current position."
+  (interactive)
+  (unless (or (featurep 'semanticdb) (semanticdb-minor-mode-p))
+    (error "Texinfo updating only works when `semanticdb' is being used"))
+  (semantic-fetch-tags)
+  (unless tag
+    (beginning-of-line)
+    (setq tag (semantic-current-tag)))
+  (unless (semantic-tag-of-class-p tag 'def)
+    (error "Only deffns (or defun or defvar) can be updated"))
+  (let* ((name (semantic-tag-name tag))
+	 (tags (semanticdb-strip-find-results
+		(semanticdb-with-match-any-mode
+		  (semanticdb-brute-deep-find-tags-by-name name))
+		'name))
+	 (docstring nil)
+	 (docstringproto nil)
+	 (docstringvar nil)
+	 (doctag nil)
+	 (doctagproto nil)
+	 (doctagvar nil)
+	 )
+    (save-excursion
+      (while (and tags (not docstring))
+	(let ((sourcetag (car tags)))
+	  ;; There could be more than one!  Come up with a better
+	  ;; solution someday.
+	  (when (semantic-tag-buffer sourcetag)
+	    (set-buffer (semantic-tag-buffer sourcetag))
+	    (unless (eq major-mode 'texinfo-mode)
+	    (cond ((semantic-tag-get-attribute sourcetag :prototype-flag)
+		   ;; If we found a match with doc that is a prototype, then store
+		   ;; that, but don't exit till we find the real deal.
+		   (setq docstringproto (semantic-documentation-for-tag sourcetag)
+			 doctagproto sourcetag))
+		  ((eq (semantic-tag-class sourcetag) 'variable)
+		   (setq docstringvar (semantic-documentation-for-tag sourcetag)
+			 doctagvar sourcetag))
+		  ((semantic-tag-get-attribute sourcetag :override-function-flag)
+		   nil)
+		  (t
+		   (setq docstring (semantic-documentation-for-tag sourcetag))))
+	    (setq doctag (if docstring sourcetag nil))))
+	  (setq tags (cdr tags)))))
+    ;; If we found a prototype of the function that has some doc, but not the
+    ;; actual function, lets make due with that.
+    (if (not docstring)
+	(cond ((stringp docstringvar)
+	       (setq docstring docstringvar
+		     doctag doctagvar))
+	      ((stringp docstringproto)
+	       (setq docstring docstringproto
+		     doctag doctagproto))))
+    ;; Test for doc string
+    (unless docstring
+      (error "Could not find documentation for %s" (semantic-tag-name tag)))
+    ;; If we have a string, do the replacement.
+    (delete-region (semantic-tag-start tag)
+		   (semantic-tag-end tag))
+    ;; Use useful functions from the docaument library.
+    (require 'document)
+    (document-insert-texinfo doctag (semantic-tag-buffer doctag))
+    ))
+
+(defun semantic-texi-update-doc-from-source (&optional tag)
+  "Update the documentation for the source TAG.
+The current buffer must be a non-texinfo source file containing TAG.
+If TAG is nil, determine the tag based on the current position.
+The current buffer must include TAG."
+  (interactive)
+  (when (eq major-mode 'texinfo-mode)
+    (error "Not a source file"))
+  (semantic-fetch-tags)
+  (unless tag
+    (setq tag (semantic-current-tag)))
+  (unless (semantic-documentation-for-tag tag)
+    (error "Cannot find interesting documentation to use for %s"
+	   (semantic-tag-name tag)))
+  (let* ((name (semantic-tag-name tag))
+	 (texi (semantic-texi-associated-files))
+	 (doctag nil)
+	 (docbuff nil))
+    (while (and texi (not doctag))
+      (set-buffer (find-file-noselect (car texi)))
+      (setq doctag (car (semantic-deep-find-tags-by-name
+			 name (semantic-fetch-tags)))
+	    docbuff (if doctag (current-buffer) nil))
+      (setq texi (cdr texi)))
+    (unless doctag
+      (error "Tag %s is not yet documented.  Use the `document' command"
+             name))
+    ;; Ok, we should have everything we need.  Do the deed.
+    (if (get-buffer-window docbuff)
+	(set-buffer docbuff)
+      (switch-to-buffer docbuff))
+    (goto-char (semantic-tag-start doctag))
+    (delete-region (semantic-tag-start doctag)
+		   (semantic-tag-end doctag))
+    ;; Use useful functions from the document library.
+    (require 'document)
+    (document-insert-texinfo tag (semantic-tag-buffer tag))
+    ))
+
+(defun semantic-texi-update-doc (&optional tag)
+  "Update the documentation for TAG.
+If the current buffer is a texinfo file, then find the source doc, and
+update it.  If the current buffer is a source file, then get the
+documentation for this item, find the existing doc in the associated
+manual, and update that."
+  (interactive)
+  (cond ((eq major-mode 'texinfo-mode)
+	 (semantic-texi-update-doc-from-texi tag))
+	(t
+	 (semantic-texi-update-doc-from-source tag))))
+
+(defun semantic-texi-goto-source (&optional tag)
+  "Jump to the source for the definition in the texinfo file TAG.
+If TAG is nil, it is derived from the deffn under POINT."
+  (interactive)
+  (unless (or (featurep 'semanticdb) (semanticdb-minor-mode-p))
+    (error "Texinfo updating only works when `semanticdb' is being used"))
+  (semantic-fetch-tags)
+  (unless tag
+    (beginning-of-line)
+    (setq tag (semantic-current-tag)))
+  (unless (semantic-tag-of-class-p tag 'def)
+    (error "Only deffns (or defun or defvar) can be updated"))
+  (let* ((name (semantic-tag-name tag))
+	 (tags (semanticdb-fast-strip-find-results
+		(semanticdb-with-match-any-mode
+		  (semanticdb-brute-deep-find-tags-by-name name nil 'name))
+		))
+
+	 (done nil)
+	 )
+    (save-excursion
+      (while (and tags (not done))
+	(set-buffer (semantic-tag-buffer (car tags)))
+	(unless (eq major-mode 'texinfo-mode)
+	  (switch-to-buffer (semantic-tag-buffer (car tags)))
+	  (goto-char (semantic-tag-start (car tags)))
+	  (setq done t))
+	(setq tags (cdr tags)))
+      (if (not done)
+	  (error "Could not find tag for %s" (semantic-tag-name tag)))
+      )))
+
+(provide 'semantic/texi)
+
+;;; semantic-texi.el ends here