find.el 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. ;;;; srecode/find.el --- Tools for finding templates in the database.
  2. ;; Copyright (C) 2007-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. ;; Various routines that search through various template tables
  18. ;; in search of the right template.
  19. (require 'srecode/ctxt)
  20. (require 'srecode/table)
  21. (require 'srecode/map)
  22. (declare-function srecode-compile-file "srecode/compile")
  23. ;;; Code:
  24. (defun srecode-table (&optional mode)
  25. "Return the currently active Semantic Recoder table for this buffer.
  26. Optional argument MODE specifies the mode table to use."
  27. (let* ((modeq (or mode major-mode))
  28. (table (srecode-get-mode-table modeq)))
  29. ;; If there isn't one, keep searching backwards for a table.
  30. (while (and (not table) (setq modeq (get-mode-local-parent modeq)))
  31. (setq table (srecode-get-mode-table modeq)))
  32. ;; Last ditch effort.
  33. (when (not table)
  34. (setq table (srecode-get-mode-table 'default)))
  35. table))
  36. ;;; TRACKER
  37. ;;
  38. ;; Template file tracker for between sessions.
  39. ;;
  40. (defun srecode-load-tables-for-mode (mmode &optional appname)
  41. "Load all the template files for MMODE.
  42. Templates are found in the SRecode Template Map.
  43. See `srecode-get-maps' for more.
  44. APPNAME is the name of an application. In this case,
  45. all template files for that application will be loaded."
  46. (require 'srecode/compile)
  47. (let ((files
  48. (if appname
  49. (apply 'append
  50. (mapcar
  51. (lambda (map)
  52. (srecode-map-entries-for-app-and-mode map appname mmode))
  53. (srecode-get-maps)))
  54. (apply 'append
  55. (mapcar
  56. (lambda (map)
  57. (srecode-map-entries-for-mode map mmode))
  58. (srecode-get-maps)))))
  59. )
  60. ;; Don't recurse if we are already the 'default state.
  61. (when (not (eq mmode 'default))
  62. ;; Are we a derived mode? If so, get the parent mode's
  63. ;; templates loaded too.
  64. (if (get-mode-local-parent mmode)
  65. (srecode-load-tables-for-mode (get-mode-local-parent mmode)
  66. appname)
  67. ;; No parent mode, all templates depend on the defaults being
  68. ;; loaded in, so get that in instead.
  69. (srecode-load-tables-for-mode 'default appname)))
  70. ;; Load in templates for our major mode.
  71. (dolist (f files)
  72. (let ((mt (srecode-get-mode-table mmode))
  73. )
  74. (when (or (not mt) (not (srecode-mode-table-find mt (car f))))
  75. (srecode-compile-file (car f)))
  76. ))
  77. ))
  78. ;;; PROJECT
  79. ;;
  80. ;; Find if a template table has a project set, and if so, is the
  81. ;; current buffer in that project.
  82. (defmethod srecode-template-table-in-project-p ((tab srecode-template-table))
  83. "Return non-nil if the table TAB can be used in the current project.
  84. If TAB has a :project set, check that the directories match.
  85. If TAB is nil, then always return t."
  86. (let ((proj (oref tab :project)))
  87. ;; Return t if the project wasn't set.
  88. (if (not proj) t
  89. ;; If the project directory was set, let's check it.
  90. (let ((dd (expand-file-name default-directory))
  91. (projexp (regexp-quote (directory-file-name proj))))
  92. (if (string-match (concat "^" projexp) dd)
  93. t nil)))))
  94. ;;; SEARCH
  95. ;;
  96. ;; Find a given template based on name, and features of the current
  97. ;; buffer.
  98. (defmethod srecode-template-get-table ((tab srecode-template-table)
  99. template-name &optional
  100. context application)
  101. "Find in the template in table TAB, the template with TEMPLATE-NAME.
  102. Optional argument CONTEXT specifies that the template should part
  103. of a particular context.
  104. The APPLICATION argument is unused."
  105. (when (srecode-template-table-in-project-p tab)
  106. (if context
  107. ;; If a context is specified, then look it up there.
  108. (let ((ctxth (gethash context (oref tab contexthash))))
  109. (when ctxth
  110. (gethash template-name ctxth)))
  111. ;; No context, perhaps a merged name?
  112. (gethash template-name (oref tab namehash)))))
  113. (defmethod srecode-template-get-table ((tab srecode-mode-table)
  114. template-name &optional
  115. context application)
  116. "Find in the template in mode table TAB, the template with TEMPLATE-NAME.
  117. Optional argument CONTEXT specifies a context a particular template
  118. would belong to.
  119. Optional argument APPLICATION restricts searches to only template tables
  120. belonging to a specific application. If APPLICATION is nil, then only
  121. tables that do not belong to an application will be searched."
  122. (let* ((mt tab)
  123. (tabs (oref mt :tables))
  124. (ans nil))
  125. (while (and (not ans) tabs)
  126. (let ((app (oref (car tabs) :application)))
  127. (when (or (and (not application) (null app))
  128. (and application (eq app application)))
  129. (setq ans (srecode-template-get-table (car tabs) template-name
  130. context)))
  131. (setq tabs (cdr tabs))))
  132. (or ans
  133. ;; Recurse to the default.
  134. (when (not (equal (oref tab :major-mode) 'default))
  135. (srecode-template-get-table (srecode-get-mode-table 'default)
  136. template-name context application)))))
  137. ;;
  138. ;; Find a given template based on a key binding.
  139. ;;
  140. (defmethod srecode-template-get-table-for-binding
  141. ((tab srecode-template-table) binding &optional context)
  142. "Find in the template name in table TAB, the template with BINDING.
  143. Optional argument CONTEXT specifies that the template should part
  144. of a particular context."
  145. (when (srecode-template-table-in-project-p tab)
  146. (let* ((keyout nil)
  147. (hashfcn (lambda (key value)
  148. (when (and (slot-boundp value 'binding)
  149. (oref value binding)
  150. (= (aref (oref value binding) 0) binding))
  151. (setq keyout key))))
  152. (contextstr (cond ((listp context)
  153. (car-safe context))
  154. ((stringp context)
  155. context)
  156. (t nil)))
  157. )
  158. (if context
  159. (let ((ctxth (gethash contextstr (oref tab contexthash))))
  160. (when ctxth
  161. ;; If a context is specified, then look it up there.
  162. (maphash hashfcn ctxth)
  163. ;; Context hashes EXCLUDE the context prefix which
  164. ;; we need to include, so concat it here
  165. (when keyout
  166. (setq keyout (concat contextstr ":" keyout)))
  167. )))
  168. (when (not keyout)
  169. ;; No context, or binding in context. Try full hash.
  170. (maphash hashfcn (oref tab namehash)))
  171. keyout)))
  172. (defmethod srecode-template-get-table-for-binding
  173. ((tab srecode-mode-table) binding &optional context application)
  174. "Find in the template name in mode table TAB, the template with BINDING.
  175. Optional argument CONTEXT specifies a context a particular template
  176. would belong to.
  177. Optional argument APPLICATION restricts searches to only template tables
  178. belonging to a specific application. If APPLICATION is nil, then only
  179. tables that do not belong to an application will be searched."
  180. (let* ((mt tab)
  181. (tabs (oref mt :tables))
  182. (ans nil))
  183. (while (and (not ans) tabs)
  184. (let ((app (oref (car tabs) :application)))
  185. (when (or (and (not application) (null app))
  186. (and application (eq app application)))
  187. (setq ans (srecode-template-get-table-for-binding
  188. (car tabs) binding context)))
  189. (setq tabs (cdr tabs))))
  190. (or ans
  191. ;; Recurse to the default.
  192. (when (not (equal (oref tab :major-mode) 'default))
  193. (srecode-template-get-table-for-binding
  194. (srecode-get-mode-table 'default) binding context)))))
  195. ;;; Interactive
  196. ;;
  197. ;; Interactive queries into the template data.
  198. ;;
  199. (defvar srecode-read-template-name-history nil
  200. "History for completing reads for template names.")
  201. (defun srecode-all-template-hash (&optional mode hash)
  202. "Create a hash table of all the currently available templates.
  203. Optional argument MODE is the major mode to look for.
  204. Optional argument HASH is the hash table to fill in."
  205. (let* ((mhash (or hash (make-hash-table :test 'equal)))
  206. (mmode (or mode major-mode))
  207. (mp (get-mode-local-parent mmode))
  208. )
  209. ;; Get the parent hash table filled into our current hash.
  210. (when (not (eq mode 'default))
  211. (if mp
  212. (srecode-all-template-hash mp mhash)
  213. (srecode-all-template-hash 'default mhash)))
  214. ;; Load up the hash table for our current mode.
  215. (let* ((mt (srecode-get-mode-table mmode))
  216. (tabs (when mt (oref mt :tables)))
  217. )
  218. (while tabs
  219. ;; Exclude templates for a particular application.
  220. (when (and (not (oref (car tabs) :application))
  221. (srecode-template-table-in-project-p (car tabs)))
  222. (maphash (lambda (key temp)
  223. (puthash key temp mhash)
  224. )
  225. (oref (car tabs) namehash)))
  226. (setq tabs (cdr tabs)))
  227. mhash)))
  228. (defun srecode-calculate-default-template-string (hash)
  229. "Calculate the name of the template to use as a DEFAULT.
  230. Templates are read from HASH.
  231. Context into which the template is inserted is calculated
  232. with `srecode-calculate-context'."
  233. (let* ((ctxt (srecode-calculate-context))
  234. (ans (concat (nth 0 ctxt) ":" (nth 1 ctxt))))
  235. (if (gethash ans hash)
  236. ans
  237. ;; No hash at the specifics, at least offer
  238. ;; the prefix for the completing read
  239. (concat (nth 0 ctxt) ":"))))
  240. (defun srecode-read-template-name (prompt &optional initial hist default)
  241. "Completing read for Semantic Recoder template names.
  242. PROMPT is used to query for the name of the template desired.
  243. INITIAL is the initial string to use.
  244. HIST is a history variable to use.
  245. DEFAULT is what to use if the user presses RET."
  246. (srecode-load-tables-for-mode major-mode)
  247. (let* ((hash (srecode-all-template-hash))
  248. (def (or initial
  249. (srecode-calculate-default-template-string hash))))
  250. (completing-read prompt hash
  251. nil t def
  252. (or hist
  253. 'srecode-read-template-name-history))))
  254. (provide 'srecode/find)
  255. ;;; srecode/find.el ends here