texi.el 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. ;;; srecode/texi.el --- Srecode texinfo support.
  2. ;; Copyright (C) 2008-2012 Free Software Foundation, Inc.
  3. ;; Author: Eric M. Ludlam <eric@siege-engine.com>
  4. ;; This file is part of GNU Emacs.
  5. ;; GNU Emacs is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; GNU Emacs is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;
  17. ;; Texinfo semantic recoder support.
  18. ;;
  19. ;; Contains some handlers, and a few simple texinfo srecoder applications.
  20. (require 'semantic)
  21. (require 'semantic/texi)
  22. (require 'srecode/semantic)
  23. ;;; Code:
  24. (defun srecode-texi-add-menu (newnode)
  25. "Add an item into the current menu. Add @node statements as well.
  26. Argument NEWNODE is the name of the new node."
  27. (interactive "sName of new node: ")
  28. (srecode-load-tables-for-mode major-mode)
  29. (semantic-fetch-tags)
  30. (let ((currnode (reverse (semantic-find-tag-by-overlay)))
  31. (nodebounds nil))
  32. (when (not currnode)
  33. (error "Cannot find node to put menu item into"))
  34. (setq currnode (car currnode))
  35. (setq nodebounds (semantic-tag-texi-section-text-bounds currnode))
  36. ;; Step 1:
  37. ;; Limit search within this node.
  38. ;; Step 2:
  39. ;; Find the menu. If there isn't one, add one to the end.
  40. ;; Step 3:
  41. ;; Add new item to end of menu list.
  42. ;; Step 4:
  43. ;; Find correct node new item should show up after, and stick
  44. ;; the new node there.
  45. (if (string= (semantic-texi-current-environment) "menu")
  46. ;; We are already in a menu, so insert the new item right here.
  47. (beginning-of-line)
  48. ;; Else, try to find a menu item to append to.
  49. (goto-char (car nodebounds))
  50. (if (not (re-search-forward "^@menu" (car (cdr nodebounds)) t))
  51. (progn
  52. (goto-char (car (cdr nodebounds)))
  53. (if (not (y-or-n-p "Add menu here? "))
  54. (error "Abort"))
  55. (srecode-insert "declaration:menu"))
  56. ;; Else, find the end
  57. (re-search-forward "@end menu")
  58. (beginning-of-line)))
  59. ;; At this point, we are in a menu... or not.
  60. ;; If we are, do stuff, else error.
  61. (when (string= (semantic-texi-current-environment) "menu")
  62. (let ((menuname newnode)
  63. (returnpoint nil))
  64. (srecode-insert "declaration:menuitem" "NAME" menuname)
  65. (set-mark (point))
  66. (setq returnpoint (make-marker))
  67. ;; Update the bound since we added text
  68. (setq nodebounds (semantic-tag-texi-section-text-bounds currnode))
  69. (beginning-of-line)
  70. (forward-char -1)
  71. (beginning-of-line)
  72. (let ((end nil))
  73. (if (not (looking-at "\\* \\([^:]+\\):"))
  74. (setq end (car (cdr nodebounds)))
  75. (let* ((nname (match-string 1))
  76. (tag
  77. (semantic-deep-find-tags-by-name nname (current-buffer))))
  78. (when tag
  79. (setq end (semantic-tag-end (car tag))))
  80. ))
  81. (when (not end)
  82. (goto-char returnpoint)
  83. (error "Could not find location for new node" ))
  84. (when end
  85. (goto-char end)
  86. (when (bolp) (forward-char -1))
  87. (insert "\n")
  88. (if (eq (semantic-current-tag) currnode)
  89. (srecode-insert "declaration:subnode" "NAME" menuname)
  90. (srecode-insert "declaration:node" "NAME" menuname))
  91. )
  92. )))
  93. ))
  94. ;;;###autoload
  95. (defun srecode-semantic-handle-:texi (dict)
  96. "Add macros into the dictionary DICT based on the current texinfo file.
  97. Adds the following:
  98. LEVEL - chapter, section, subsection, etc
  99. NEXTLEVEL - One below level"
  100. ;; LEVEL and NEXTLEVEL calculation
  101. (semantic-fetch-tags)
  102. (let ((tags (reverse (semantic-find-tag-by-overlay)))
  103. (level nil))
  104. (while (and tags (not (semantic-tag-of-class-p (car tags) 'section)))
  105. (setq tags (cdr tags)))
  106. (when tags
  107. (save-excursion
  108. (goto-char (semantic-tag-start (car tags)))
  109. (when (looking-at "@node")
  110. (forward-line 1)
  111. (beginning-of-line))
  112. (when (looking-at "@\\(\\w+\\)")
  113. (setq level (match-string 1))
  114. )))
  115. (srecode-dictionary-set-value dict "LEVEL" (or level "chapter"))
  116. (let ((nl (assoc level '( ( nil . "top" )
  117. ("top" . "chapter")
  118. ("chapter" . "section")
  119. ("section" . "subsection")
  120. ("subsection" . "subsubsection")
  121. ("subsubsection" . "subsubsection")
  122. ))))
  123. (srecode-dictionary-set-value dict "NEXTLEVEL" (cdr nl))))
  124. )
  125. ;;;###autoload
  126. (defun srecode-semantic-handle-:texitag (dict)
  127. "Add macros into the dictionary DICT based on the current :tag file.
  128. Adds the following:
  129. TAGDOC - Texinfo formatted doc string for :tag."
  130. ;; If we also have a TAG, what is the doc?
  131. (let ((tag (srecode-dictionary-lookup-name dict "TAG"))
  132. (doc nil)
  133. )
  134. ;; If the user didn't apply :tag, then do so now.
  135. (when (not tag)
  136. (srecode-semantic-handle-:tag dict))
  137. (setq tag (srecode-dictionary-lookup-name dict "TAG"))
  138. (when (not tag)
  139. (error "No tag to insert for :texitag template argument"))
  140. ;; Extract the tag out of the compound object.
  141. (setq tag (oref tag :prime))
  142. ;; Extract the doc string
  143. (setq doc (semantic-documentation-for-tag tag))
  144. (when doc
  145. (srecode-dictionary-set-value dict "TAGDOC"
  146. (srecode-texi-massage-to-texinfo
  147. tag (semantic-tag-buffer tag)
  148. doc)))
  149. ))
  150. ;;; OVERRIDES
  151. ;;
  152. ;; Override some semantic and srecode features with texi specific
  153. ;; versions.
  154. (define-mode-local-override semantic-insert-foreign-tag
  155. texinfo-mode (foreign-tag)
  156. "Insert FOREIGN-TAG from a foreign buffer in TAGFILE.
  157. Assume TAGFILE is a source buffer, and create a documentation
  158. thingy from it using the `document' tool."
  159. (srecode-texi-insert-tag-as-doc foreign-tag))
  160. (defun srecode-texi-insert-tag-as-doc (tag)
  161. "Insert TAG into the current buffer with SRecode."
  162. (when (not (eq major-mode 'texinfo-mode))
  163. (error "Can only insert tags into texinfo in texinfo mode"))
  164. (let ((srecode-semantic-selected-tag tag))
  165. (srecode-load-tables-for-mode major-mode)
  166. ;; @todo - choose of the many types of tags to insert,
  167. ;; or put all that logic into srecode.
  168. (srecode-insert "declaration:function")))
  169. ;;; Texinfo mangling.
  170. (define-overloadable-function srecode-texi-texify-docstring
  171. (docstring)
  172. "Texify the doc string DOCSTRING.
  173. Takes plain text formatting that may exist, and converts it to
  174. using TeXinfo formatting.")
  175. (defun srecode-texi-texify-docstring-default (docstring)
  176. "Texify the doc string DOCSTRING.
  177. Takes a few very generic guesses as to what the formatting is."
  178. (let ((case-fold-search nil)
  179. (start 0))
  180. (while (string-match
  181. "\\(^\\|[^{]\\)\\<\\([A-Z0-9_-]+\\)\\>\\($\\|[^}]\\)"
  182. docstring start)
  183. (let ((ms (match-string 2 docstring)))
  184. ;(when (eq mode 'emacs-lisp-mode)
  185. ; (setq ms (downcase ms)))
  186. (when (not (or (string= ms "A")
  187. (string= ms "a")
  188. ))
  189. (setq docstring (concat (substring docstring 0 (match-beginning 2))
  190. "@var{"
  191. ms
  192. "}"
  193. (substring docstring (match-end 2))))))
  194. (setq start (match-end 2)))
  195. ;; Return our modified doc string.
  196. docstring))
  197. (defun srecode-texi-massage-to-texinfo (tag buffer string)
  198. "Massage TAG's documentation from BUFFER as STRING.
  199. This is to take advantage of TeXinfo's markup symbols."
  200. (save-excursion
  201. (if buffer
  202. (progn (set-buffer buffer)
  203. (srecode-texi-texify-docstring string))
  204. ;; Else, no buffer, so let's do something else
  205. (with-mode-local texinfo-mode
  206. (srecode-texi-texify-docstring string)))))
  207. (define-mode-local-override srecode-texi-texify-docstring emacs-lisp-mode
  208. (string)
  209. "Take STRING, (a normal doc string), and convert it into a texinfo string.
  210. For instances where CLASS is the class being referenced, do not Xref
  211. that class.
  212. `function' => @dfn{function}
  213. `variable' => @code{variable}
  214. `class' => @code{class} @xref{class}
  215. `unknown' => @code{unknown}
  216. \"text\" => ``text''
  217. 'quoteme => @code{quoteme}
  218. non-nil => non-@code{nil}
  219. t => @code{t}
  220. :tag => @code{:tag}
  221. [ stuff ] => @code{[ stuff ]}
  222. Key => @kbd{Key} (key is C\\-h, M\\-h, SPC, RET, TAB and the like)
  223. ... => @dots{}"
  224. (while (string-match "`\\([-a-zA-Z0-9<>.]+\\)'" string)
  225. (let* ((vs (substring string (match-beginning 1) (match-end 1)))
  226. (v (intern-soft vs)))
  227. (setq string
  228. (concat
  229. (replace-match (concat
  230. (if (fboundp v)
  231. "@dfn{" "@code{")
  232. vs "}")
  233. nil t string)))))
  234. (while (string-match "\\( \\|^\\)\\(nil\\|t\\|'[-a-zA-Z0-9]+\\|:[-a-zA-Z0-9]+\\)\\([. ,]\\|$\\)" string)
  235. (setq string (replace-match "@code{\\2}" t nil string 2)))
  236. (while (string-match "\\( \\|^\\)\\(\\(non-\\)\\(nil\\)\\)\\([. ,]\\|$\\)" string)
  237. (setq string (replace-match "\\3@code{\\4}" t nil string 2)))
  238. (while (string-match "\\( \\|^\\)\\(\\[[^]]+\\]\\)\\( \\|$\\)" string)
  239. (setq string (replace-match "@code{\\2}" t nil string 2)))
  240. (while (string-match "\\( \\|^\\)\\(\\(\\(C-\\|M-\\|S-\\)+\\([^ \t\n]\\|RET\\|SPC\\|TAB\\)\\)\\|\\(RET\\|SPC\\|TAB\\)\\)\\( \\|\\s.\\|$\\)" string)
  241. (setq string (replace-match "@kbd{\\2}" t nil string 2)))
  242. (while (string-match "\"\\(.+\\)\"" string)
  243. (setq string (replace-match "``\\1''" t nil string 0)))
  244. (while (string-match "\\.\\.\\." string)
  245. (setq string (replace-match "@dots{}" t nil string 0)))
  246. ;; Also do base docstring type.
  247. (srecode-texi-texify-docstring-default string))
  248. (provide 'srecode/texi)
  249. ;; Local variables:
  250. ;; generated-autoload-file: "loaddefs.el"
  251. ;; generated-autoload-load-name: "srecode/texi"
  252. ;; End:
  253. ;;; srecode/texi.el ends here