extract.el 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. ;;; srecode/extract.el --- Extract content from previously inserted macro.
  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. ;; Extract content from a previously inserted macro.
  18. ;;
  19. ;; The extraction routines can be handy if you want to extract users
  20. ;; added text from the middle of a template inserted block of text.
  21. ;; This code will not work for all templates. It will only work for
  22. ;; templates with unique static text between all the different insert
  23. ;; macros.
  24. ;;
  25. ;; That said, it will handle include and section templates, so complex
  26. ;; or deep template calls can be extracted.
  27. ;;
  28. ;; This code was specifically written for srecode-document, which
  29. ;; wants to extract user written text, and re-use it in a reformatted
  30. ;; comment.
  31. (require 'srecode)
  32. (require 'srecode/compile)
  33. (require 'srecode/insert)
  34. ;;; Code:
  35. (defclass srecode-extract-state ()
  36. ((anchor :initform nil
  37. :documentation
  38. "The last known plain-text end location.")
  39. (lastinserter :initform nil
  40. :documentation
  41. "The last inserter with 'later extraction type.")
  42. (lastdict :initform nil
  43. :documentation
  44. "The dictionary associated with lastinserter.")
  45. )
  46. "The current extraction state.")
  47. (defmethod srecode-extract-state-set ((st srecode-extract-state) ins dict)
  48. "Set onto the extract state ST a new inserter INS and dictionary DICT."
  49. (oset st lastinserter ins)
  50. (oset st lastdict dict))
  51. (defmethod srecode-extract-state-set-anchor ((st srecode-extract-state))
  52. "Reset the anchor point on extract state ST."
  53. (oset st anchor (point)))
  54. (defmethod srecode-extract-state-extract ((st srecode-extract-state)
  55. endpoint)
  56. "Perform an extraction on the extract state ST with ENDPOINT.
  57. If there was no waiting inserter, do nothing."
  58. (when (oref st lastinserter)
  59. (save-match-data
  60. (srecode-inserter-extract (oref st lastinserter)
  61. (oref st anchor)
  62. endpoint
  63. (oref st lastdict)
  64. st))
  65. ;; Clear state.
  66. (srecode-extract-state-set st nil nil)))
  67. ;;; Extraction
  68. ;l
  69. (defun srecode-extract (template start end)
  70. "Extract TEMPLATE from between START and END in the current buffer.
  71. Uses TEMPLATE's constant strings to break up the text and guess what
  72. the dictionary entries were for that block of text."
  73. (save-excursion
  74. (save-restriction
  75. (narrow-to-region start end)
  76. (let ((dict (srecode-create-dictionary t))
  77. (state (srecode-extract-state "state"))
  78. )
  79. (goto-char start)
  80. (srecode-extract-method template dict state)
  81. dict))))
  82. (defmethod srecode-extract-method ((st srecode-template) dictionary
  83. state)
  84. "Extract template ST and store extracted text in DICTIONARY.
  85. Optional STARTRETURN is a symbol in which the start of the first
  86. plain-text match occurred."
  87. (srecode-extract-code-stream (oref st code) dictionary state))
  88. (defun srecode-extract-code-stream (code dictionary state)
  89. "Extract CODE from buffer text into DICTIONARY.
  90. Uses string constants in CODE to split up the buffer.
  91. Uses STATE to maintain the current extraction state."
  92. (while code
  93. (cond
  94. ;; constant strings need mark the end of old inserters that
  95. ;; need to extract values, or are just there.
  96. ((stringp (car code))
  97. (srecode-extract-state-set-anchor state)
  98. ;; When we have a string, find it in the collection, then extract
  99. ;; that start point as the end point of the inserter
  100. (unless (re-search-forward (regexp-quote (car code))
  101. (point-max) t)
  102. (error "Unable to extract all dictionary entries"))
  103. (srecode-extract-state-extract state (match-beginning 0))
  104. (goto-char (match-end 0))
  105. )
  106. ;; Some inserters are simple, and need to be extracted after
  107. ;; we find our next block of static text.
  108. ((eq (srecode-inserter-do-extract-p (car code)) 'later)
  109. (srecode-extract-state-set state (car code) dictionary)
  110. )
  111. ;; Some inserter want to start extraction now, such as sections.
  112. ;; We can't predict the end point till we parse out the middle.
  113. ((eq (srecode-inserter-do-extract-p (car code)) 'now)
  114. (srecode-extract-state-set-anchor state)
  115. (srecode-inserter-extract (car code) (point) nil dictionary state))
  116. )
  117. (setq code (cdr code))
  118. ))
  119. ;;; Inserter Base Extractors
  120. ;;
  121. (defmethod srecode-inserter-do-extract-p ((ins srecode-template-inserter))
  122. "Return non-nil if this inserter can extract values."
  123. nil)
  124. (defmethod srecode-inserter-extract ((ins srecode-template-inserter)
  125. start end dict state)
  126. "Extract text from START/END and store in DICT.
  127. Return nil as this inserter will extract nothing."
  128. nil)
  129. ;;; Variable extractor is simple and can extract later.
  130. ;;
  131. (defmethod srecode-inserter-do-extract-p ((ins srecode-template-inserter-variable))
  132. "Return non-nil if this inserter can extract values."
  133. 'later)
  134. (defmethod srecode-inserter-extract ((ins srecode-template-inserter-variable)
  135. start end vdict state)
  136. "Extract text from START/END and store in VDICT.
  137. Return t if something was extracted.
  138. Return nil if this inserter doesn't need to extract anything."
  139. (srecode-dictionary-set-value vdict
  140. (oref ins :object-name)
  141. (buffer-substring-no-properties
  142. start end)
  143. )
  144. t)
  145. ;;; Section Inserter
  146. ;;
  147. (defmethod srecode-inserter-do-extract-p ((ins srecode-template-inserter-section-start))
  148. "Return non-nil if this inserter can extract values."
  149. 'now)
  150. (defmethod srecode-inserter-extract ((ins srecode-template-inserter-section-start)
  151. start end indict state)
  152. "Extract text from START/END and store in INDICT.
  153. Return the starting location of the first plain-text match.
  154. Return nil if nothing was extracted."
  155. (let ((name (oref ins :object-name))
  156. (subdict (srecode-create-dictionary indict))
  157. (allsubdict nil)
  158. )
  159. ;; Keep extracting till we can extract no more.
  160. (while (condition-case nil
  161. (progn
  162. (srecode-extract-method
  163. (oref ins template) subdict state)
  164. t)
  165. (error nil))
  166. ;; Success means keep this subdict, and also make a new one for
  167. ;; the next iteration.
  168. (setq allsubdict (cons subdict allsubdict))
  169. (setq subdict (srecode-create-dictionary indict))
  170. )
  171. (srecode-dictionary-set-value indict name (nreverse allsubdict))
  172. nil))
  173. ;;; Include Extractor must extract now.
  174. ;;
  175. (defmethod srecode-inserter-do-extract-p ((ins srecode-template-inserter-include))
  176. "Return non-nil if this inserter can extract values."
  177. 'now)
  178. (defmethod srecode-inserter-extract ((ins srecode-template-inserter-include)
  179. start end dict state)
  180. "Extract text from START/END and store in DICT.
  181. Return the starting location of the first plain-text match.
  182. Return nil if nothing was extracted."
  183. (goto-char start)
  184. (srecode-insert-include-lookup ins dict)
  185. ;; There are two modes for includes. One is with no dict,
  186. ;; so it is inserted straight. If the dict has a name, then
  187. ;; we need to run once per dictionary occurrence.
  188. (if (not (string= (oref ins :object-name) ""))
  189. ;; With a name, do the insertion.
  190. (let ((subdict (srecode-dictionary-add-section-dictionary
  191. dict (oref ins :object-name))))
  192. (error "Need to implement include w/ name extractor")
  193. ;; Recurse into the new template while no errors.
  194. (while (condition-case nil
  195. (progn
  196. (srecode-extract-method
  197. (oref ins includedtemplate) subdict
  198. state)
  199. t)
  200. (error nil))))
  201. ;; No stream, do the extraction into the current dictionary.
  202. (srecode-extract-method (oref ins includedtemplate) dict
  203. state))
  204. )
  205. (provide 'srecode/extract)
  206. ;;; srecode/extract.el ends here