1 ;;; wesnoth-update.el --- Update known WML data via existing valid WML.
2 ;; Copyright (C) 2008 Chris Mann
4 ;; This file is part of wesnoth-mode.
6 ;; This program is free software; you can redistribute it and/or
7 ;; modify it under the terms of the GNU General Public License as
8 ;; published by the Free Software Foundation; either version 2 of the
9 ;; License, or (at your option) any later version.
11 ;; This program is distributed in the hope that it will be useful, but
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;; General Public License for more details.
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with this program; see the file COPYING. If not, write to the
18 ;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
22 ;; Update WML information using WML built-in to Wesnoth.
24 ;; The following should be added to your .emacs so that `wesnoth-update' can
25 ;; correctly generate WML data:
26 ;; (setq wesnoth-root-directory "/path/to/wesnoth/"
27 ;; wesnoth-update-output-directory "/path/to/wesnoth-mode/"
28 ;; wesnoth-addition-file "/path/to/wesnoth-mode/wesnoth-wml-additions.cfg")
29 ;; Specifying the appropriate path in each case.
31 ;; Although WML data is provided along with wesnoth-mode, you can generate
32 ;; update-to-date, version-specific WML reference data for `wesnoth-mode'
33 ;; using `wesnoth-update'. This requires Wesnoth to be install and its
34 ;; pathname set for this to behave correctly. for example:
35 ;; (setq wesnoth-root-directory "/usr/share/wesnoth/")
37 ;; Then set the output directory for `wesnoth-update's results:
38 ;; (setq wesnoth-update-output-directory "/path/to/wesnoth-mode/")
39 ;; This is recommended to be in the same directory as `wesnoth-mode' and
40 ;; must be in `load-path'.
42 ;; Once set, `wesnoth-update' will produce 'wesnoth-wml-data.el' in
43 ;; `wesnoth-update-output-directory' and the information will automatically
44 ;; be available in the future sessions.
46 ;; Although much data is retreived, it is unlikely to be completely
47 ;; comprehensive. wesnoth-mode can be taught about additional tags,
48 ;; attributes and macros using the current project, or a single file, using
49 ;; `wesnoth-update-wml-additions'.
51 ;; To teach wesnoth-mode about elements it may have missed, you can extend the
52 ;; sample additions included with wesnoth-mode; namely
53 ;; wesnoth-wml-additions.cfg (although any source of WML can be used). To
54 ;; enable this, do the following:
55 ;; Set `wesnoth-addition-file' appropriately, for example:
56 ;; (setq wesnoth-addition-file "/path/to/wesnoth-wml-additions.cfg")
58 ;; Once set correctly, running M-x wesnoth-update will update the WML data
59 ;; available to `wesnoth-mode'.
66 (defvar wesnoth-update-version
"0.1"
67 "Version of `wesnoth-update'.")
69 (defcustom wesnoth-root-directory nil
70 "Root directory of wesnoth."
74 (defcustom wesnoth-addition-file nil
75 "Filename to the file containing additional WML information."
79 (defcustom wesnoth-update-output-directory nil
80 "Directory to write discovered WML syntax information.
81 Ensure this directory is in your `load-path'."
85 (defconst wesnoth-macro-directory
"data/core/macros"
86 "Directory which built-in macros are stored.
87 This is relative to the wesnoth directory in `wesnoth-root-directory.'.")
89 (defvar wesnoth-found-cfgs
'()
90 "Temporary list of all .cfg files found.")
92 (defvar wesnoth-tag-data
'()
93 "All information regarding the relation of tags and attributes.")
95 (defvar wesnoth-macro-data
'()
96 "Information regarding built-in macros.")
98 (defvar wesnoth-local-macro-data
'()
99 "All macro definitions available in the current project.")
101 (defun wesnoth-file-cfg-p (file)
102 "Return non-nil if FILE has a '.cfg' extension."
103 (and (not (file-directory-p file
)) (string-match "\\.cfg$" file
)))
105 (defun wesnoth-fetch-all-dirs (dir)
106 "Retrieve a list of subdirectories to scan.
107 DIR is the directory to check."
108 (let ((dirs-to-scan (wesnoth-files-in-dir dir
)))
110 (setq dirs-to-scan
(append (wesnoth-files-in-dir (pop dirs-to-scan
))
113 (defun wesnoth-files-in-dir (dir)
114 "Add cfgs to `wesnoth-files-in-dir'.
115 Returns a list of sub-directories in DIR."
116 (let ((cfgs (wesnoth-cfg-files-in-dir dir
)))
118 (setq wesnoth-found-cfgs
(append cfgs wesnoth-found-cfgs
))))
120 (dolist (file (directory-files dir t
))
121 (unless (string-match "^\\..*" (file-name-nondirectory file
))
122 (cond ((file-directory-p file
)
123 (add-to-list 'dirs file
))
124 ((wesnoth-file-cfg-p file
)
125 (add-to-list 'wesnoth-found-cfgs file
)))))
128 (defun wesnoth-cfg-files-in-dir (dir)
129 "Return all cfg files in DIR."
131 (dolist (file (directory-files dir t
))
132 (and (wesnoth-file-cfg-p file
)
133 (add-to-list 'result file
)))
136 (defun wesnoth-determine-details (dir-or-file function
)
137 "Process .cfg files in DIR-OR-FILE using FUNCTION.
138 DIR-OR-FILE can be a file, a directory, or a list of files."
139 (cond ((listp dir-or-file
)
140 (dolist (file dir-or-file
)
141 (wesnoth-handle-file function file
)))
142 ((and (file-exists-p dir-or-file
)
143 (not (file-directory-p dir-or-file
)))
144 (wesnoth-handle-file function dir-or-file
))
146 (wesnoth-fetch-all-dirs dir-or-file
)
147 (while wesnoth-found-cfgs
148 (unless (string-match "^\\..+" (file-name-nondirectory
149 (car wesnoth-found-cfgs
)))
150 (wesnoth-handle-file function
(car wesnoth-found-cfgs
))
151 (setq wesnoth-found-cfgs
(cdr wesnoth-found-cfgs
)))))))
153 (defun wesnoth-handle-file (function file
)
154 "Perform FUNCTION on FILE."
156 (when (file-exists-p file
)
157 (insert-file-contents file
)
158 (funcall function
))))
160 (defun wesnoth-extract-tag-information ()
161 "Retrieve relevant tag and attribute information."
162 (let ((unmatched-tag-list '()))
163 (goto-char (point-min))
164 (while (search-forward-regexp
165 "^[\t ]*\\(\\[[+/]?\\(\\(\\w\\|_\\)+\\)\\]\\|\\(\\w\\|_\\)+=\\)"
169 ((and (save-excursion
170 (search-backward-regexp
171 "^[\t ]*\\(\\[[^/]]?\\|#define \\|#enddef \\)"
173 (string-match "#define " (match-string 1))
174 (looking-at "^[\t ]*\\[\\+?\\(\\(\\w\\|_\\)+\\)\\]"))
175 (wesnoth-append-tag-information (match-string-no-properties 1) nil nil
)
176 (setq unmatched-tag-list
(cons (match-string-no-properties 1)
177 unmatched-tag-list
)))
178 ((looking-at "^[\t ]*\\[\\+?\\(\\(\\w\\|_\\)+\\)\\]")
179 (wesnoth-append-tag-information (car unmatched-tag-list
)
180 (match-string-no-properties 1)
182 (wesnoth-append-tag-information (match-string-no-properties 1) nil nil
)
183 (setq unmatched-tag-list
(cons (match-string-no-properties 1)
184 unmatched-tag-list
)))
185 ((looking-at "^[\t ]*\\(\\(\\w\\|_\\)+\\)=")
186 (wesnoth-append-tag-information (car unmatched-tag-list
)
187 nil
(match-string-no-properties 1)))
188 ((looking-at "^[\t ]*\\[/\\(\\(\\w\\|_\\)+\\)\\]\\|")
189 (when (string= (match-string-no-properties 1)
190 (car unmatched-tag-list
))
191 (setq unmatched-tag-list
(cdr unmatched-tag-list
)))))
194 (defun wesnoth-append-tag-information (tag subtag attribute
)
195 "Add the information regarding TAG to the list.
196 SUBTAG and ATTRIBUTE are a children of TAG to be added."
197 (let ((match (assoc tag wesnoth-tag-data
)))
199 (add-to-list 'wesnoth-tag-data
(list tag
(and subtag
(list subtag
))
200 (and attribute
(list attribute
))))
202 (let ((tmp (nth 1 match
)))
203 (when (not (member subtag tmp
))
204 (add-to-list 'tmp subtag
)
205 (setq match
(list tag tmp
(car (last match
))))))
206 (when attribute
(let ((tmp (nth 2 match
)))
207 (when (not (member attribute tmp
))
208 (add-to-list 'tmp attribute
)
209 (setq match
(list tag
(nth 1 match
) tmp
))))))
210 (setq wesnoth-tag-data
211 (remove (assoc tag wesnoth-tag-data
)
213 (add-to-list 'wesnoth-tag-data match
))))
215 (defmacro wesnoth-determine-macro-information
(macro-list)
216 "Process the buffer, retrieving macro definition information.
217 MACRO-LIST is the variable to append macro information."
219 (goto-char (point-min))
220 (while (search-forward-regexp
221 "#define \\(\\(\\w\\|_\\)+\\)\\([\t ]+\\(\\w\\|_\\)+\\)?"
224 (add-to-list ,macro-list
(list (match-string 1)
225 (when (match-string 3) t
)))
228 (defun wesnoth-determine-macro-builtins ()
229 "Retrieve built-in macro definition information."
230 (wesnoth-determine-macro-information 'wesnoth-macro-data
))
232 (defun wesnoth-output-path ()
233 "Determine the path to output wml information via `wesnoth-update'."
234 (or wesnoth-update-output-directory
235 (and (boundp 'user-emacs-directory
)
236 user-emacs-directory
)
239 (defun wesnoth-update-wml-additions ()
240 "Update WML information contained in `wesnoth-addition-file'."
241 (wesnoth-determine-details wesnoth-addition-file
242 'wesnoth-extract-tag-information
)
243 (wesnoth-determine-details wesnoth-addition-file
244 'wesnoth-determine-macro-builtins
))
246 (defun wesnoth-update ()
247 "Update WML information.
248 Path to WML information included in wesnoth is set by
249 `wesnoth-root-directory.'."
251 (setq wesnoth-tag-data nil
252 wesnoth-macro-data nil
253 wesnoth-found-cfgs nil
)
254 (unless (and (stringp wesnoth-root-directory
)
255 (file-exists-p wesnoth-root-directory
))
256 ;; Update failed; restore data.
257 (load "wesnoth-wml-data")
258 (error "%s: directory does not exist"
259 wesnoth-root-directory
))
260 (message "Updating WML information...")
261 (wesnoth-determine-details wesnoth-root-directory
262 'wesnoth-extract-tag-information
)
263 (wesnoth-update-wml-additions)
264 (wesnoth-determine-details (concat wesnoth-root-directory
265 wesnoth-macro-directory
)
266 'wesnoth-determine-macro-builtins
)
268 (insert (format "(setq wesnoth-tag-data '%S)\n\n" wesnoth-tag-data
))
269 (insert (format "(setq wesnoth-macro-data '%S)\n\n" wesnoth-macro-data
))
270 (insert "(provide 'wesnoth-wml-data)\n")
271 (write-file (expand-file-name (format "wesnoth-wml-data.el")
272 (wesnoth-output-path)))
273 (load "wesnoth-wml-data"))
274 (message "Updating WML information...done"))
276 (defun wesnoth-update-project-information ()
277 "Update WML macro information for the current project."
279 (wesnoth-determine-details (wesnoth-cfg-files-in-dir default-directory
)
281 (wesnoth-determine-macro-information
282 'wesnoth-local-macro-data
))))
284 (defun wesnoth-update-teach-wesnoth-mode (file-or-dir)
285 "Update WML tag and attribute information for the current project.
286 If FILE-OR-DIR is provided, perform the update using only that location."
288 (wesnoth-determine-details
291 (wesnoth-determine-macro-information 'wesnoth-macro-data
)))
292 (wesnoth-determine-details file-or-dir
293 'wesnoth-extract-tag-information
))
295 (provide 'wesnoth-update
)
297 ;;; wesnoth-update.el ends here