* wesnoth-update.el: Clean up variables.
[wesnoth-mode.git] / wesnoth-update.el
blob6ed6f96ce51546f953ecc7c90c595e647167b597
1 ;;; wesnoth-update.el --- Update WML data via a wesnoth installation.
2 ;; Copyright (C) 2008 Chris Mann
4 ;; This program is free software; you can redistribute it and/or
5 ;; modify it under the terms of the GNU General Public License as
6 ;; published by the Free Software Foundation; either version 2 of the
7 ;; License, or (at your option) any later version.
9 ;; This program is distributed in the hope that it will be useful, but
10 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ;; General Public License for more details.
14 ;; You should have received a copy of the GNU General Public License
15 ;; along with this program; see the file COPYING. If not, write to the
16 ;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
17 ;; MA 02139, USA.
19 ;; This file is part of wesnoth-mode.
21 ;;; Commentary:
22 ;; Update WML information using WML built-in to Wesnoth.
23 ;; The pathname of wesnoth must be set correctly for this to behave correctly,
24 ;; for example:
25 ;; (setq wesnoth-root-directory "/usr/share/wesnoth/")
26 ;; Ensuring the pathname ends in a slash, then running `wesnoth-update'.
27 ;; `wesnoth-update' will produce 'wesnoth-wml-data.el' in `default-directory',
28 ;; ensure this file is in `load-path' to use the information retrieved in the
29 ;; future sessions.
31 ;; Although much data is retreived, it is unlikely to be completely
32 ;; comprehensive. wesnoth-mode can be taught about additional tags,
33 ;; attributes and macros using the current project, or a single file, using
34 ;; `wesnoth-update-teach-wesnoth-mode'.
36 ;; To teach wesnoth-mode about elements it may have missed, you can create a
37 ;; formatted WML file, ensuring the nesting and all elements are correct
38 ;; (preferably by running wmllint) and evaluate:
39 ;; (wesnoth-update-teach-wesnoth-mode "/path/to/structured/wml.cfg")
41 ;; It may be preferable to perform this each time wesnoth-mode is used. For
42 ;; this, you can add the following to your .emacs:
43 ;; (add-hook 'wesnoth-mode-hook
44 ;; '(lambda ()
45 ;; (wesnoth-update-teach-wesnoth-mode "/path/to/structured/wml.cfg")))
48 ;;; History:
49 ;; 0.1
50 ;; * Initial version
52 ;;; Code:
53 (require 'cl)
55 (defvar wesnoth-update-version "0.1"
56 "Version of `wesnoth-update'.")
57 (defcustom wesnoth-root-directory nil
58 "Path to the root directory of wesnoth.
59 Path must end in trailing slash."
60 :type 'directory
61 :group 'wesnoth-mode)
63 (defvar wesnoth-macro-directory (concat wesnoth-root-directory "data/core/macros")
64 "Directory which built-in macros are stored.
65 This is relative to `wesnoth-root-directory'.")
67 (defvar wesnoth-found-cfgs '()
68 "Temporary list of all .cfg files found.")
70 (defvar wesnoth-tag-data '()
71 "All information regarding the relation of tags and attributes.")
73 (defvar wesnoth-macro-data '()
74 "Information regarding built-in macros.")
76 (defvar wesnoth-local-macro-data '()
77 "All macro definitions available in the current project.")
79 (defun wesnoth-file-cfg-p (file)
80 "Return non-nil if FILE has a '.cfg' extension."
81 (not (and (not (file-directory-p file)) (string-match "\\.cfg$" file))))
83 (defun wesnoth-fetch-all-dirs (dir)
84 "Retrieve a list of subdirectories to scan.
85 DIR is the directory to check."
86 (let ((dirs-to-scan (wesnoth-files-in-dir dir)))
87 (while dirs-to-scan
88 (setq dirs-to-scan (append (wesnoth-files-in-dir (pop dirs-to-scan))
89 dirs-to-scan)))))
91 (defun wesnoth-files-in-dir (dir)
92 "Return a list of cfg files from DIR."
93 (let ((cfgs (remove-if-not 'wesnoth-file-cfg-p
94 (file-expand-wildcards
95 (concat dir "/*") t))))
96 (when cfgs
97 (setq wesnoth-found-cfgs (append cfgs wesnoth-found-cfgs))))
98 (remove-if
99 (lambda (file)
100 (or (not (file-directory-p file))
101 (string-match "^\\..+" (file-name-nondirectory file))))
102 (file-expand-wildcards (concat dir "/*") t)))
104 (defun wesnoth-determine-details (dir-or-file function)
105 "Process .cfg files in DIR-OR-FILE using FUNCTION."
106 (if (and (file-exists-p dir-or-file)
107 (not (file-directory-p dir-or-file)))
108 (wesnoth-handle-file function dir-or-file)
109 (wesnoth-fetch-all-dirs dir-or-file)
110 (while wesnoth-found-cfgs
111 (unless (string-match "^\\..+" (file-name-nondirectory
112 (car wesnoth-found-cfgs)))
113 (wesnoth-handle-file function (car wesnoth-found-cfgs))
114 (setq wesnoth-found-cfgs (cdr wesnoth-found-cfgs))))))
116 (defun wesnoth-handle-file (function file)
117 "Perform FUNCTION on FILE."
118 (with-temp-buffer
119 (insert-file-contents file)
120 (funcall function)))
122 (defun wesnoth-extract-tag-information ()
123 "Retrieve relevant tag and attribute information."
124 (let ((unmatched-tag-list '()))
125 (goto-char (point-min))
126 (while (search-forward-regexp
127 "^[\t ]*\\(\\[[+/]?\\(\\(\\w\\|_\\)+\\)\\]\\|\\(\\w\\|_\\)+=\\)"
128 (point-max) t)
129 (beginning-of-line)
130 (cond ((looking-at "^[\t ]*\\[\\+?\\(\\(\\w\\|_\\)+\\)\\]")
131 (wesnoth-append-tag-information (car unmatched-tag-list)
132 (match-string-no-properties 1)
133 nil)
134 (setq unmatched-tag-list (cons (match-string-no-properties 1)
135 unmatched-tag-list)))
136 ((looking-at "^[\t ]*\\(\\(\\w\\|_\\)+\\)=")
137 (wesnoth-append-tag-information (car unmatched-tag-list)
138 nil (match-string-no-properties 1)))
139 ((looking-at "^[\t ]*\\[/\\(\\(\\w\\|_\\)+\\)\\]\\|")
140 (when (string= (match-string-no-properties 1)
141 (car unmatched-tag-list))
142 (setq unmatched-tag-list (cdr unmatched-tag-list)))))
143 (end-of-line))))
145 (defun wesnoth-append-tag-information (tag subtag attribute)
146 "Add the information regarding TAG to the list.
147 SUBTAG and ATTRIBUTE are a children of TAG to be added."
148 (let ((match (find tag wesnoth-tag-data :key 'car :test 'string=)))
149 (if (not match)
150 (add-to-list 'wesnoth-tag-data (list tag nil nil))
151 (if subtag
152 (let ((tmp (nth 1 match)))
153 (when (not (member subtag tmp))
154 (add-to-list 'tmp subtag)
155 (setq match (list tag tmp (car (last match))))))
156 (when attribute (let ((tmp (nth 2 match)))
157 (when (not (member attribute tmp))
158 (add-to-list 'tmp attribute)
159 (setq match (list tag (nth 1 match) tmp))))))
160 (setq wesnoth-tag-data
161 (remove (find tag wesnoth-tag-data :key 'car :test 'string=)
162 wesnoth-tag-data))
163 (add-to-list 'wesnoth-tag-data match))))
165 (defmacro wesnoth-determine-macro-information (macro-list)
166 "Process the buffer, retrieving macro definition information.
167 MACRO-LIST is the variable to append macro information."
168 `(progn
169 (goto-char (point-min))
170 (while (search-forward-regexp
171 "#define \\(\\(\\w\\|_\\)+\\)\\([\t ]+\\(\\w\\|_\\)+\\)?"
172 (point-max) t)
173 (beginning-of-line)
174 (add-to-list ,macro-list (list (match-string 1)
175 (when (match-string 3) t)))
176 (end-of-line))))
178 (defun wesnoth-determine-macro-builtins ()
179 "Retrieve built-in macro definition information."
180 (wesnoth-determine-macro-information 'wesnoth-macro-data))
182 (defun wesnoth-update ()
183 "Update WML information."
184 (interactive)
185 (setq wesnoth-tag-data nil
186 wesnoth-macro-data nil)
187 (unless wesnoth-root-directory
188 (error "`wesnoth-root-directory' not a suitable pathname"))
189 (wesnoth-determine-details wesnoth-root-directory 'wesnoth-extract-tag-information)
190 (wesnoth-determine-details wesnoth-macro-directory 'wesnoth-determine-macro-builtins)
191 (with-temp-buffer
192 (insert (format "(defvar wesnoth-tag-data '%S)\n\n" wesnoth-tag-data))
193 (insert (format "(defvar wesnoth-macro-data '%S)\n\n" wesnoth-macro-data))
194 (insert (format "(provide 'wesnoth-wml-data)\n"))
195 (write-file "wesnoth-wml-data.el")))
197 (defun wesnoth-update-project-information ()
198 "Update WML macro information for the current project."
199 (interactive)
200 (wesnoth-determine-details default-directory
201 (lambda ()
202 (wesnoth-determine-macro-information 'wesnoth-local-macro-data))))
204 (defun wesnoth-update-teach-wesnoth-mode (&optional file-or-dir)
205 "Update WML tag and attribute information for the current project.
206 If FILE-OR-DIR is provided, perform the update using only that location."
207 (interactive)
208 (wesnoth-determine-details
209 file-or-dir
210 (lambda ()
211 (wesnoth-determine-macro-information 'wesnoth-macro-data)))
212 (wesnoth-determine-details (or file-or-dir default-directory)
213 'wesnoth-extract-tag-information))
215 (provide 'wesnoth-update)
217 ;;; wesnoth-update.el ends here