1 ;; wesnoth-mode.el - A major mode for editing WML.
2 ;; Copyright (C) 2006, 2007 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,
20 ;; wesnoth-mode is a major mode for Emacs which assists in the editing
21 ;; of Wesnoth Markup Language (WML) files. Currently, this mode
22 ;; features syntax highlighting support, automatic indentation,
23 ;; tag-completion and preliminary support for syntax checking.
26 ;; Add the following to your .emacs:
27 ;; (add-to-list 'load-path "path/to/wesnoth-mode")
28 ;; (require 'wesnoth-mode)
30 ;; (add-to-list 'auto-mode-alist '("\\.cfg\\'" . wesnoth-mode))
31 ;; to automatically load wesnoth-mode for all files ending in '.cfg'.
33 (defconst wesnoth-mode-version
"1.1.2.1"
34 "The current version of wesnoth-mode.")
36 (defgroup wesnoth-mode nil
"Wesnoth-mode access"
40 (defcustom wesnoth-indent
2
41 "The number of columns to indent WML."
45 (defcustom wesnoth-indentation-function
'wesnoth-indent-line-default
46 "Use the specified function when indenting WML.
47 You can specify either `wesnoth-indent-line-default' or
48 `wesnoth-indent-line-savefile' as the indentation style or a
49 customised function for indentation."
53 (defcustom wesnoth-auto-indent-flag t
54 "Whether to attempt tag indentation when a newline is created.
55 If nil, no indentation will be attempted. Otherwise, attempt to
60 (defconst wesnoth-preprocessor-regexp
61 "#\\(?:define \\|e\\(?:lse\\|nd\\(?:\\(?:de\\|i\\)f\\)\\)\\|\\(?:if\\|un\\)def \\)"
62 "Regular expression to match all preprocessor statements.")
64 (defconst wesnoth-preprocessor-opening-regexp
65 "#\\(?:define \\|else\\|ifdef \\)"
66 "Regular expression to match \"opening\" preprocessor statements.")
68 (defconst wesnoth-preprocessor-closing-regexp
69 "#e\\(?:lse\\|nd\\(?:\\(?:de\\|i\\)f\\)\\)"
70 "Regular expression to match \"closing\" preprocessor statements.")
72 (defvar wesnoth-wml-structure
73 '(("multiplayer" ("id" "allow_new_game" "next_scenario" "description" "objectives" "name" "map_data" "turns" "turn_at" "random_start_time" "music" "victory_when_enemies_defeated" "experience_modifier" "theme" "map_generation") ("music" "story" "time" "illuminated_time" "time_area" "side" "event" "generator"))
74 ("test" ("id" "allow_new_game" "next_scenario" "description" "objectives" "name" "map_data" "turns" "turn_at" "random_start_time" "music" "victory_when_enemies_defeated" "experience_modifier" "theme" "map_generation") ("music" "story" "time" "illuminated_time" "time_area" "side" "event" "generator"))
75 ("tutorial" ("id" "allow_new_game" "next_scenario" "description" "objectives" "name" "map_data" "turns" "turn_at" "random_start_time" "music" "victory_when_enemies_defeated" "experience_modifier" "theme" "map_generation") ("music" "story" "time" "illuminated_time" "time_area" "side" "event" "generator"))
76 ("scenario" ("id" "allow_new_game" "next_scenario" "description" "objectives" "name" "map_data" "turns" "turn_at" "random_start_time" "music" "victory_when_enemies_defeated" "experience_modifier" "theme" "map_generation") ("music" "story" "time" "illuminated_time" "time_area" "side" "event" "generator"))
78 ("team" ("damage_inflicted" "damage_taken" "recall_cost" "recruit_cost") ("recruits" "cecalls" "advances" "deaths" "killed" "attacks" "defends"))
79 ("abilities" ("name" "name_inactive" "description" "description_inactive" "affect_self" "affect_allies" "affect_enemies" "cumulative" "id") ("heals" "regenerate" "resistance" "leadership" "skirmisher" "illuminates" "teleport" "hides" "adjacent_description" "filter" "filter_adjactent" "filter_adjacent_location" "affect_adjacent" "filter_self" "filter_base_value"))
80 ("heals" ("value" "poison") ())
81 ("regenerate" ("value" "poison") ())
82 ("resistance" ("value" "max_value" "add" "multiply" "apply_to" "active_on") ())
83 ("leadership" ("value") ())
84 ("illuminates" ("value" "max_value"))
86 ("specials" () ("damage" "attacks" "chance_to_hit" "slow" "poison" "stones" "berserk" "firststrike" "drains" "plague"))
87 ("damage" ("name" "name_inactive" "value" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
88 ("attacks" ("name" "name_inactive" "value" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
89 ("chance_to_hit" ("name" "value" "name_inactive" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
90 ("slow" ("name" "name_inactive" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
91 ("poison" ("name" "name_inactive" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
92 ("stones" ("name" "name_inactive" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
93 ("berserk" ("name" "values" "name_inactive" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
94 ("firststrike" ("name" "name_inactive" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
95 ("drains" ("name" "name_inactive" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
96 ("plague" ("name" "type" "name_inactive" "description" "description_inactive" "active_on" "multiply" "cumulative" "id" "active_on" "apply_to") ("filter_adjacent" "filter_adjactent_location" "filter_self" "filter_opponent" "filter_attacker" "filter_defender" "filter_base_value"))
97 ("unit" ("advancefrom" "advanceto" "alignment" "cost" "experience" "gender" "hide_help" "hitpoints" "id" "level" "movement" "movetype" "name" "num_traits" "profile" "race" "unit_description" "undead_variation" "usage" "zoc") ("advancement" "base_unit" "attack" "defend" "death" "teleport" "extra_anim" "event" "variation" "male" "female" "abilities"))
98 ("about" ("title" "artists" "units" "balancing" "text") ("entry"))
99 ("ai" ("time_of_day" "turns" "ai_algorithm" "python_script" "recruitment_pattern" "aggression" "caution" "village_value" "leader_value" "villages_per_scout" "recruitment_ignore_bad_movement" "recruitment_ignore_bad_combat" "passive_leader" "attack_depth" "simple_targetting" "scout_village_targetting" "grouping" "protect_leader") ("protect_location" "protect_unit" "target" "avoid" "leader_goal"))))
101 (defvar wesnoth-mode-hook nil
)
103 (defvar wesnoth-mode-map
()
104 "Keymap used in wesnoth mode.")
105 (unless wesnoth-mode-map
106 (setq wesnoth-mode-map
(make-sparse-keymap))
107 (define-key wesnoth-mode-map
"\C-\M-a" 'wesnoth-jump-backward
)
108 (define-key wesnoth-mode-map
"\C-\M-e" 'wesnoth-jump-forward
)
109 (define-key wesnoth-mode-map
"\C-c\C-m" 'wesnoth-jump-to-matching
)
110 (define-key wesnoth-mode-map
"\C-cm" 'wesnoth-jump-to-matching
)
111 (define-key wesnoth-mode-map
"\C-m" 'wesnoth-newline
)
112 (define-key wesnoth-mode-map
"\C-j" 'wesnoth-newline-and-indent
)
113 (define-key wesnoth-mode-map
"\C-c\C-c" 'wesnoth-check-structure
)
114 (define-key wesnoth-mode-map
"\C-cc" 'wesnoth-check-structure
)
115 (define-key wesnoth-mode-map
"\C-c\C-n" 'wesnoth-check-tag-names
)
116 (define-key wesnoth-mode-map
"\C-cf" 'wesnoth-fix-structure
)
117 (define-key wesnoth-mode-map
"\C-c\C-f" 'wesnoth-fix-structure
)
118 (define-key wesnoth-mode-map
"\C-cn" 'wesnoth-check-tag-names
)
119 (define-key wesnoth-mode-map
"\C-c\C-e" 'wesnoth-insert-tag
)
120 (define-key wesnoth-mode-map
"\C-ce" 'wesnoth-insert-tag
))
122 (defvar wesnoth-syntax-table
123 (let ((wesnoth-syntax-table (make-syntax-table)))
124 (modify-syntax-entry ?
= "." wesnoth-syntax-table
)
125 (modify-syntax-entry ?\_
"w" wesnoth-syntax-table
)
126 (modify-syntax-entry ?-
"_" wesnoth-syntax-table
)
127 (modify-syntax-entry ?.
"_" wesnoth-syntax-table
)
128 (modify-syntax-entry ?
\n ">" wesnoth-syntax-table
)
129 (modify-syntax-entry ?
\r ">" wesnoth-syntax-table
)
130 wesnoth-syntax-table
)
131 "Syntax table for wesnoth-mode.")
133 ;; Prevents automatic syntax-highlighting of elements which might be
134 ;; pre-processor statements.
135 (defvar wesnoth-syntactic-keywords
137 '("^[\t ]*\\(#\\(?:define \\|e\\(?:lse\\|nd\\(?:\\(?:de\\|i\\)f\\)\\)\\|\\(?:if\\|un\\)def \\)\\)" 1 "w")
138 '("\\(#[\t ]*.*$\\)" 1 "<"))
139 "Highlighting syntactic keywords within wesnoth-mode.")
141 (defvar wesnoth-font-lock-keywords
143 '("\\(#\\(?:define\\|\\(?:if\\|un\\)def\\)\\)"
144 1 font-lock-preprocessor-face
)
145 '("\\(#\\(?:define\\|\\(?:if\\|un\\)def\\)\\)[\t ]+\\(\\w+\\)"
146 2 font-lock-function-name-face
)
147 '("\\(#e\\(?:lse\\|nd\\(?:\\(?:de\\|i\\)f\\)\\)\\)" . font-lock-preprocessor-face
)
148 '("[\t ]*\\({[[:word:]/\.]+\\|{[@~][[:word:]/\.]+\\).*\\(}\\)"
149 (1 font-lock-function-name-face
)
150 (2 font-lock-function-name-face
))
151 '("\\[[^]]+\\]" . font-lock-type-face
)
152 '("\\$\\w+" . font-lock-variable-name-face
)
153 '("\\(\\w+\\(\\,[\t ]*\\w+\\)*\\)="
154 1 font-lock-variable-name-face
))
155 "Syntax highlighting for wesnoth-mode.")
157 (defvar wesnoth-tags-list
159 "abilities" "about" "advances" "advancefrom" "ai" "allow_recruit"
160 "and" "animation" "array" "attack" "attacks" "avoid" "binary_path"
161 "bold" "campaign" "capture_village" "choose""clear_variable"
162 "colour_adjust" "command" "deaths" "defend" "defends" "defense"
163 "delay" "destination" "disallow_recruit" "do" "effect" "else"
164 "end_turn" "endlevel" "era" "event" "expenses" "filter"
165 "filter_radius" "filter_second" "format" "frame" "game_config"
166 "generator" "gold" "have_unit" "header" "hide_unit" "if"
167 "illuminated_time" "image" "img" "income" "italic" "item" "jump"
168 "kill" "killed" "label" "language" "leader_goal" "main_map" "menu"
169 "message" "mini_map" "missile_frame" "modifications" "modify_side"
170 "modify_turns" "move" "move_unit_fake" "movement_costs" "movetype"
171 "multiplayer" "multiplayer_side" "music" "not" "num_units" "object"
172 "objectives" "objective" "observers" "option" "or" "panel" "part"
173 "place_shroud" "position" "print" "protect_location" "protect_unit"
174 "race" "random" "recall" "recalls" "recruit" "recruits" "redraw"
175 "ref" "remove_shroud" "remove_unit_overlay" "removeitem" "replay"
176 "replay_start" "resistance" "resolution" "results" "role" "save"
177 "scenario" "scroll" "scroll_to" "scroll_to_unit" "section"
178 "set_recruit" "set_variable" "side" "side_playing" "snapshot"
179 "sound" "source" "specials" "statistics" "status" "stone"
180 "store_gold" "store_locations" "store_starting_location"
181 "store_unit" "story" "target" "team" "teleport" "teleport_anim"
182 "terrain" "terrain_graphics" "test" "theme" "then" "tile" "time"
183 "time_area" "time_of_day" "topic" "toplevel" "trait" "turn"
184 "tutorial" "unhide_unit" "unit" "unit_abilities"
185 "unit_alignment" "unit_description" "unit_hp" "unit_image"
186 "unit_level" "unit_moves" "unit_overlay" "unit_profile"
187 "unit_status" "unit_traits" "unit_type" "unit_weapons" "unit_xp"
188 "units" "unstone" "unstore_unit" "upkeep" "variable" "variables"
189 "village" "villages" "while")
190 "A list containing all tags which are available for use in WML.")
192 (defun wesnoth-insert-tag ()
193 "Inserts the specified opening tag and it's matching closing tag.
194 Both the opening and closing tags will be placed on their own
195 lines with point positioned between them. Completion of tags at
196 the prompt uses `wesnoth-tags-list'."
205 (unless (looking-at "^[\t ]*$")
209 (insert "[" tagname
"]")
210 (wesnoth-indent-line)
212 (wesnoth-indent-line)
214 (insert "\n[/" tagname
"]")
215 (wesnoth-indent-line)
216 (forward-line -
1)))))
218 (defun wesnoth-jump-forward (repeat)
219 "Move point to the end of the next tag.
220 REPEAT is an optional numeric argument. If REPEAT is non-nil,
221 jump forward the specified number of tags."
223 (or repeat
(setq repeat
1))
224 (and (< repeat
0) (wesnoth-jump-backward (abs repeat
)))
225 (let ((iterations 0))
226 (while (< iterations repeat
)
228 (search-forward-regexp
229 (concat "^[\t ]*\\(\\[\\w+\\]\\|"
230 wesnoth-preprocessor-opening-regexp
"\\)")
232 (setq iterations
(1+ iterations
)))))
234 (defun wesnoth-jump-backward (repeat)
235 "Move point to the beginning of the previous tag.
236 REPEAT is an optional numeric argument. If REPEAT is non-nil,
237 jump backward the specified number of tags."
239 (or repeat
(setq repeat
1))
240 (and (< repeat
0) (wesnoth-jump-forward (abs repeat
)))
241 (let ((iterations 0))
242 (while (< iterations repeat
)
244 (search-backward-regexp
245 (concat "^[\t ]*\\(\\[\\w+\\]\\|"
246 wesnoth-preprocessor-opening-regexp
"\\)")
249 (search-forward-regexp "[^[:blank:]]")
251 (setq iterations
(1+ iterations
)))))
253 (defun wesnoth-jump-to-matching ()
254 "Jump point to the matching opening/closing tag.
255 A tag must be on the same line as point for jumping to occur. If
256 the tag structure is not correct this may have unexpected
262 (search-backward nil
))
266 (concat "^[\t ]*\\(\\[\\|"
267 wesnoth-preprocessor-regexp
"\\)"))
269 (concat "^[\t ]*\\(\\[/\\|#\\(?:endif\\|enddef\\)\\)"))
270 (setq search-backward t
))
271 (if (wesnoth-wml-start-pos)
272 (if (> (point) (wesnoth-wml-start-pos))
273 (search-backward-regexp
274 (concat "^[\t ]*\\(\\[\\|"
275 wesnoth-preprocessor-regexp
"\\)")
277 (goto-char (point-min))
278 (search-forward-regexp
279 (concat "^[\t ]*\\(\\[\\|"
280 wesnoth-preprocessor-regexp
"\\)"))
282 (error "Unable to locate tag to jump from")))
287 (or (< open-tags
0) (not search-started
))
288 (search-backward-regexp
289 (concat "^[\t ]*\\(\\[\\|"
290 wesnoth-preprocessor-regexp
"\\)")
292 (setq search-started t
)
294 "^[\t ]*\\(\\[\\w+\\]\\|#\\(?:define\\|ifdef\\) \\)")
295 (setq open-tags
(1+ open-tags
))
297 (concat "^[\t ]*\\(\\[/\\w+\\]\\|#\\(?:endif\\|enddef\\)\\)"))
298 (setq open-tags
(1- open-tags
))))))
300 (or (> open-tags
0) (not search-started
))
301 (search-forward-regexp
302 (concat "^[\t ]*\\(\\[\\|"
303 wesnoth-preprocessor-regexp
"\\)")
306 (setq search-started t
)
308 "^[\t ]*\\(\\[\\w+\\]\\|#\\(?:define\\|ifdef\\) \\)")
309 (setq open-tags
(1+ open-tags
))
311 (concat "^[\t ]*\\(\\[/\\w+\\]\\|#\\(?:endif\\|enddef\\)\\)"))
312 (setq open-tags
(1- open-tags
))))
314 (setq tag-position
(point)))
316 (goto-char tag-position
)
319 (search-backward-regexp "\\[\\|#"))
321 (defun wesnoth-wml-start-pos ()
322 "Determine the position of `point' relative to where the actual WML begins.
323 Return the likely starting position of the WML if it is found.
324 Otherwise return nil."
326 (goto-char (point-min))
327 (when (search-forward-regexp
328 (concat "^[\t ]*\\(\\[\\)\\|\\("
329 wesnoth-preprocessor-opening-regexp
335 (defun wesnoth-indent-line-default ()
336 "Indent the current line as WML using normal-style indentation."
338 (if (or (not (wesnoth-wml-start-pos))
339 (<= (point) (wesnoth-wml-start-pos))
340 (nth 3 (syntax-ppss (point))))
342 (let ((not-indented t
) cur-indent
)
344 (concat "^[ \t]*\\(\\[\\/[^]]*?\\|\\("
345 wesnoth-preprocessor-closing-regexp
"\\)\\)"))
348 (search-backward-regexp
349 (concat "^[\t ]*\\[\\|\\(" wesnoth-preprocessor-regexp
"\\)"))
350 (setq cur-indent
(current-indentation))
353 (concat "^[\t ]*\\(\\[/.+\\]\\|\\("
354 wesnoth-preprocessor-closing-regexp
356 (setq cur-indent
(- (current-indentation) wesnoth-indent
))))
358 (setq cur-indent
0)))
360 (concat "^[ \t]*\\(\\[[^/]*?\\]\\|\\("
361 wesnoth-preprocessor-closing-regexp
"\\)\\)")))
363 (search-backward-regexp
364 (concat "^[\t ]*\\(\\[\\|\\("
365 wesnoth-preprocessor-regexp
"\\)\\)"))
367 (concat "^[\t ]*\\(\\[/.+\\]\\|\\("
368 wesnoth-preprocessor-closing-regexp
371 (setq cur-indent
(- (current-indentation) wesnoth-indent
))
374 (setq not-indented nil
))
375 (setq cur-indent
(current-indentation))
376 (setq not-indented nil
)))
379 (search-backward-regexp
380 (concat "^[\t ]*\\([[}]\\|\\("
381 wesnoth-preprocessor-regexp
384 (concat "^[ \t]*\\(\\[[^/]*?\\]\\|\\("
385 wesnoth-preprocessor-opening-regexp
"\\)\\)"))
386 (setq cur-indent
(+ (current-indentation) wesnoth-indent
))
387 (setq cur-indent
(current-indentation)))))))
388 (unless (and (not cur-indent
) (= (current-indentation) cur-indent
))
389 (indent-line-to cur-indent
))))
392 (defun wesnoth-indent-line-savefile ()
393 "Indent the current line as WML code using savefile-style indentation."
395 (if (or (not (wesnoth-wml-start-pos))
396 (<= (point) (wesnoth-wml-start-pos))
397 (nth 3 (syntax-ppss (point))))
401 (concat "^[ \t]*\\(\\[\\/[^]]*?\\|\\("
402 wesnoth-preprocessor-closing-regexp
"\\)\\)"))
405 (search-backward-regexp "^[ \t]+.\\|^[{[#]")
406 (setq cur-indent
(- (current-indentation) wesnoth-indent
))
408 (concat "^[ \t]*\\(\\[[^/].+\\]\\|\\("
409 wesnoth-preprocessor-opening-regexp
"\\)\\)"))
410 (setq cur-indent
(current-indentation))))
412 (setq cur-indent
0)))
415 (search-backward-regexp "^[\t ]*\\([[#}]\\)")
417 (concat "^[ \t]*\\(\\[[^/]+?\\]\\|\\("
418 wesnoth-preprocessor-opening-regexp
"\\)\\)"))
419 (setq cur-indent
(+ (current-indentation) wesnoth-indent
))
420 (setq cur-indent
(current-indentation))))))
421 (unless (and (not cur-indent
)
422 (= (current-indentation) cur-indent
))
423 (indent-line-to cur-indent
))))
426 (defun wesnoth-newline ()
427 "Indent both the current line and the newline created.
428 If `wesnoth-auto-indent-flag' is nil, indentation will not be
431 (when wesnoth-auto-indent-flag
434 (if (looking-at "^[\t ]*$")
436 (wesnoth-indent-line))))
439 (defun wesnoth-newline-and-indent ()
440 "Indent both the current line and the newline created.
441 If `wesnoth-auto-indent-flag' is nil, indentation will not be
445 (wesnoth-indent-line))
447 (defun wesnoth-check-tag-names ()
448 "Check the names of all tags in the buffer for correctness.
449 If a tag is found which is not present in the list an error will
450 be signalled and point will be moved to the corresponding
453 (let ((tag-position nil
)
454 (missing-tag-name nil
))
456 (goto-char (point-min))
458 (search-forward-regexp "^[\t ]*\\[" (point-max) t
)
461 (when (looking-at "^[\t ]*\\[[/]?\\(\\w+\\)\\]")
462 (unless (member (match-string-no-properties 1) wesnoth-tags-list
)
463 (setq tag-position
(point))
464 (setq missing-tag-name
(match-string-no-properties 1))))
467 (goto-char tag-position
)
468 (message "'%s' is not known to exist"
471 (defun wesnoth-check-structure ()
472 "Check the buffer for correct nesting of elements.
473 If a problem is found in the structure, point will be placed at
474 the location which an element was expected and the expected
475 element will be displayed in the minibuffer."
477 (let ((unmatched-tag-list '())
478 (error-position nil
))
480 (goto-char (point-min))
482 (search-forward-regexp
483 (concat "^[\t ]*\\[\\|\\(" wesnoth-preprocessor-regexp
"\\)")
485 (not error-position
))
487 (if (looking-at "^[\t ]*#\\(\\w+\\)")
488 (let ((preprocessor-name (match-string-no-properties 1)))
490 ((string= preprocessor-name
"define")
491 (setq unmatched-tag-list
492 (cons preprocessor-name unmatched-tag-list
)))
493 ((string= preprocessor-name
"ifdef")
494 (setq unmatched-tag-list
495 (cons preprocessor-name unmatched-tag-list
)))
496 ((string= preprocessor-name
"else")
497 (unless (string= (car unmatched-tag-list
) "ifdef")
498 (setq error-position
(point))))
499 ((string= preprocessor-name
"endif")
500 (if (string= (car unmatched-tag-list
) "ifdef")
501 (setq unmatched-tag-list
(cdr unmatched-tag-list
))
502 (setq error-position
(point))))
503 ((string= preprocessor-name
"enddef")
504 (if (string= (car unmatched-tag-list
) "define")
505 (setq unmatched-tag-list
(cdr unmatched-tag-list
))
506 (setq error-position
(point))))))
507 (if (looking-at "^[\t ]*\\[\\(\\w+\\)\\]")
508 (setq unmatched-tag-list
509 (cons (match-string-no-properties 1)
511 (when (looking-at "^[\t ]*\\[/\\(\\w+\\)\\]")
512 (if (string= (match-string-no-properties 1)
513 (car unmatched-tag-list
))
514 (setq unmatched-tag-list
(cdr unmatched-tag-list
))
515 (setq error-position
(point))))))
517 (when (or unmatched-tag-list error-position
)
519 (goto-char error-position
)
520 (goto-char (point-max)))
521 (let ((expected nil
))
522 (cond ((string= (car unmatched-tag-list
) "define")
523 (setq expected
"#enddef"))
524 ((string= (car unmatched-tag-list
) "ifdef")
525 (setq expected
"#endif"))
526 ((not unmatched-tag-list
)
527 (setq expected
"end of file")))
529 (message "Expecting: '%s'"
531 expected
(concat "[/" (car unmatched-tag-list
)
533 (or expected
(concat "[/" (car unmatched-tag-list
) "]"))))))
535 (defun wesnoth-analyse-structure ()
538 (when (wesnoth-wml-start-pos)
540 ;; search for both keys and tags
541 ;; if looking at a tag, append to list of tags
542 ;; check that tag is a possible child
543 ;; assess all keys found
544 ;; remove tag from list when closing is found
545 ;; repeat generating a list of errors/warnings as it goes (in compile buffer)
547 ;; (search-forward-regexp "\\[\\(\\w+\\)\\]")
548 (let ((tag (match-string 1))
549 (info (member tag wesnoth-wml-structure
))
552 (tag-end (wesnoth-jump-to-matching)))
555 (defun wesnoth-fix-structure ()
556 "Attempt to fix all faults in the structure of the current buffer."
558 (let ((element (wesnoth-check-structure)))
560 (if (string= element
"end of file")
561 (delete-region (point) (point-max))
565 (wesnoth-indent-line))
566 (setq element
(wesnoth-check-structure)))))
568 (defun wesnoth-indent-line ()
569 "Determine and performs indentation on the current line.
570 The Indentation style can be customised by modifying
571 `wesnoth-indentation-function'."
573 (funcall wesnoth-indentation-function
))
575 (define-derived-mode wesnoth-mode fundamental-mode
"wesnoth-mode"
576 "Major mode for editing WML."
577 (set-syntax-table wesnoth-syntax-table
)
578 (set (make-local-variable 'outline-regexp
) "^[\t ]*\\[\\w+")
579 (set (make-local-variable 'comment-start
) "#")
580 (set (make-local-variable 'indent-line-function
) 'wesnoth-indent-line
)
581 (set (make-local-variable 'font-lock-defaults
)
582 '(wesnoth-font-lock-keywords
584 (font-lock-syntactic-keywords . wesnoth-syntactic-keywords
)))
585 (setq mode-name
"WML")
586 (run-hooks 'wesnoth-mode-hook
))
588 (provide 'wesnoth-mode
)