From 74da1d86de86330b1329e4c618d69373ef600bcb Mon Sep 17 00:00:00 2001 From: Chris Mann Date: Sun, 21 Sep 2008 22:12:32 +0930 Subject: [PATCH] * wesnoth-mode.el: Update history for upcoming release. (wesnoth-mode-version): Updated to 1.2.4+git. (wesnoth-indent-preprocessor-bol, wesnoth-indent-savefile): Removed variables. WML convention is now set firmly enough that they are no longer required. (wesnoth-element-closing, wesnoth-element-opening, wesnoth-element): Removed functions. (wesnoth-element-closing, wesnoth-element-opening, wesnoth-element): Added new constants. (wesnoth-tags-list): Added several new tags. (wesnoth-build-completion): Updated docstring. (wesnoth-insert-tag): Operate across prefix-arg number of elements instead of a region. Improved accuracy of tag positioning considerably. (wesnoth-insert-element-separately): New function. (wesnoth-insert-and-indent): No longer move to the end of the line. Stay where positioned by `wesnoth-indent'. (wesnoth-navigate-element): New macro. (wesnoth-forward-element, wesnoth-backward-element): Use it. (wesnoth-search-for-matching-tag): New macro. (wesnoth-jump-to-matching): Use it. (wesnoth-wml-start-pos): Fix bug where `buffer-size' was used over `point-max' for search bound. (first-column-indent-p): New function. (wesnoth-indent): Use it. Use wesnoth-element.* constants over obsoleted functions. Minor clean-up. (wesnoth-within-define): Minor clean-up. (wesnoth-determine-context, wesnoth-check-structure): Use wesnoth-element.* constants over obsoleted functions. (wesnoth-check-tag-names): Use wesnoth-element.* constants over obsoleted functions. Minor clean-up. --- wesnoth-mode.el | 411 ++++++++++++++++++++++++-------------------------------- 1 file changed, 173 insertions(+), 238 deletions(-) diff --git a/wesnoth-mode.el b/wesnoth-mode.el index e2f711b..850406d 100644 --- a/wesnoth-mode.el +++ b/wesnoth-mode.el @@ -31,6 +31,11 @@ ;; to automatically load wesnoth-mode for all files ending in '.cfg'. ;;; History: +;; 1.2.5 +;; * `wesnoth-insert-tag' now takes an optional numeric argument indicating +;; how many blocks to wrap across instead of a region. +;; * Indentation customisation removed; all indentation is performed to +;; current conventions. ;; 1.2.4 ;; * Improved syntax-highlighting for macro calls. ;; * Underscore is now treated as whitespace. @@ -93,28 +98,13 @@ ;; * Added support for #ifndef. ;;; Code: -(defconst wesnoth-mode-version "1.2.4" +(defconst wesnoth-mode-version "1.2.4+git" "The current version of `wesnoth-mode'.") (defgroup wesnoth-mode nil "Wesnoth-mode access" :group 'languages :prefix "wesnoth-") -(defcustom wesnoth-indent-preprocessor-bol t - "Style used to indent preprocessor statements. -If non-nil, preprocessor statements are indented to the first -column. Otherwise, they are indented as tags." - :type 'boolean - :group 'wesnoth-mode) - -(defcustom wesnoth-indent-savefile t - "Style used to indent WML elements. -If non-nil, attributes will be indented further than their -containing element. Otherwise, attributes will be indented at -the same level." - :type 'boolean - :group 'wesnoth-mode) - (defcustom wesnoth-auto-indent-flag t "Non-nil means indent the current line upon creating a newline." :type 'boolean @@ -217,68 +207,48 @@ the same level." 1 font-lock-variable-name-face)) "Syntax highlighting for `wesnoth-mode'.") -(defun wesnoth-element-closing (&optional all) - "Return string to use for a closing element. - -If the optional argument ALL is non-nil, include all closing -preprocessor statements." - (if (or (not wesnoth-indent-preprocessor-bol) all) - (concat "^[ \t]*\\[/\\|" - wesnoth-preprocessor-closing-regexp) - "^[ \t]*\\(\\[/\\|#enddef\\)")) - -(defun wesnoth-element-opening (&optional all) - "Return string to use for an opening element. - -If the optional argument ALL is non-nil, include all closing -preprocessor statements." - (if (or (not wesnoth-indent-preprocessor-bol) all) - (concat "^[ \t]*\\[[^/]\\|" - wesnoth-preprocessor-opening-regexp) - "^[ \t]*\\(\\[[^/]\\|#define\\)")) - -(defun wesnoth-element (&optional all) - "Return string to use for an opening or closing element. - -If the optional argument ALL is non-nil, include all preprocessor -statements." - (if (or (not wesnoth-indent-preprocessor-bol) all) - (concat "^[\t ]*\\[/?\\|" - wesnoth-preprocessor-regexp) - "^[\t ]*\\(\\[/?\\|#define\\|#enddef\\)")) +(defconst wesnoth-element-closing "[ \t]*\\(\\[/\\|#enddef\\)" + "String to use for a closing element.") + +(defconst wesnoth-element-opening "[ \t]*\\(\\[[^/]\\|#define\\)" + "String to use for an opening element.") + +(defconst wesnoth-element "[\t ]*\\(\\[[^]]?\\|#define\\|#enddef\\)" + "String to use for an opening or closing element.") ;;; Insertion (defvar wesnoth-tags-list - (list - "abilities" "about" "advances" "advancefrom" "ai" "allow_recruit" "and" - "animation" "array" "attack" "attacks" "avoid" "binary_path" "bold" - "campaign" "capture_village" "choose""clear_variable" "colour_adjust" - "command" "deaths" "debug_message" "defend" "defends" "defense" "delay" - "destination" "disallow_recruit" "do" "effect" "else" "end_turn" "endlevel" - "entry" "era" "event" "expenses" "filter" "filter_adjacent_location" - "filter_location" "filter_radius" "filter_second" "filter_vision" "format" - "frame" "game_config" "generator" "gold" "have_unit" "header" "hide_unit" - "if" "illuminated_time" "image" "img" "income" "italic" "item" "jump" - "kill" "killed" "label" "language" "leader_goal" "main_map" "menu" - "message" "mini_map" "missile_frame" "modifications" "modify_side" - "modify_turns" "move" "move_unit_fake" "movement_costs" "movetype" - "multiplayer" "multiplayer_side" "music" "not" "num_units" "object" - "objectives" "objective" "observers" "option" "or" "panel" "part" - "place_shroud" "position" "print" "protect_location" "protect_unit" "race" - "random" "recall" "recalls" "recruit" "recruits" "redraw" "ref" - "remove_shroud" "remove_unit_overlay" "removeitem" "replay" "replay_start" - "resistance" "resolution" "results" "role" "save" "scenario" "scroll" - "scroll_to" "scroll_to_unit" "section" "set_menu_item" "set_recruit" - "set_variable" "show_if" "side" "side_playing" "snapshot" "sound" "source" - "specials" "statistics" "status" "stone" "store_gold" "store_locations" - "store_starting_location" "store_side" "store_unit" "story" "target" "team" - "teleport" "teleport_anim" "terrain" "terrain_graphics" "test" "text_input" - "textdomain" "theme" "then" "tile" "time" "time_area" "time_of_day" "topic" - "toplevel" "trait" "turn" "tutorial" "unhide_unit" "unit" "unit_abilities" - "unit_alignment" "unit_description" "unit_hp" "unit_image" "unit_level" - "unit_moves" "unit_overlay" "unit_profile" "unit_status" "unit_traits" - "unit_type" "unit_weapons" "unit_xp" "units" "unstone" "unstore_unit" - "upkeep" "variable" "variables" "village" "villages" "while" "wml_filter") + (list "abilities" "about" "advances" "advancefrom" "ai" "allow_recruit" + "and" "animation" "array" "attack_anim" "attack_filter" "attack" "attacks" + "avoid" "binary_path" "bold" "campaign" "capture_village" + "choose""clear_variable" "colour_adjust" "command" "deaths" "debug_message" + "defend" "defends" "defense" "delay" "destination" "disallow_recruit" "do" + "effect" "else" "end_turn" "endlevel" "entry" "era" "event" "expenses" + "filter" "filter_adjacent_location" "filter_location" "filter_radius" + "filter_second" "filter_vision" "format" "frame" "game_config" "generator" + "gold" "have_location" "have_unit" "header" "hide_unit" "if" + "illuminated_time" "image" "img" "income" "italic" "item" "jump" "kill" + "killed" "label" "language" "leader_goal" "main_map" "menu" "message" + "mini_map" "missile_frame" "modifications" "modify_side" "modify_turns" + "move" "move_unit_fake" "movement_costs" "movetype" "multiplayer" + "multiplayer_side" "music" "not" "num_units" "object" "objectives" + "objective" "observers" "option" "or" "panel" "part" "place_shroud" + "position" "print" "protect_location" "protect_unit" "race" "random" + "recall" "recalls" "recruit" "recruits" "redraw" "ref" "regenerate" + "remove_shroud" "remove_unit_overlay" "removeitem" "replay" "replay_start" + "resistance" "resolution" "results" "role" "save" "scenario" "scroll" + "scroll_to" "scroll_to_unit" "section" "set_menu_item" "set_specials" + "set_recruit" "set_variable" "show_if" "side" "side_playing" "snapshot" + "sound" "source" "specials" "statistics" "status" "stone" "store_gold" + "store_locations" "store_starting_location" "store_side" "store_unit" + "story" "target" "team" "teleport" "teleport_anim" "terrain" + "terrain_graphics" "test" "text_input" "textdomain" "theme" "then" "tile" + "time" "time_area" "time_of_day" "topic" "toplevel" "trait" "turn" + "tutorial" "unhide_unit" "unit" "unit_abilities" "unit_alignment" + "unit_description" "unit_hp" "unit_image" "unit_level" "unit_moves" + "unit_overlay" "unit_profile" "unit_status" "unit_traits" "unit_type" + "unit_weapons" "unit_xp" "units" "unstone" "unstore_unit" "upkeep" + "variable" "variables" "village" "villages" "while" "wml_filter") "A list containing all tags which are available in WML.") (defvar wesnoth-completion-cache '() @@ -286,7 +256,8 @@ statements." (defun wesnoth-build-completion (&optional rebuild) "Create a new list for tag completion if necessary. -If REBUILD is non-nil, regenerate `wesnoth-completion-cache'." +Rebuilding list is required for versions of GNU Emacs earlier +than 22. If REBUILD is non-nil, regenerate `wesnoth-completion-cache'." (interactive "P") (if (> emacs-major-version 21) wesnoth-tags-list @@ -299,46 +270,55 @@ If REBUILD is non-nil, regenerate `wesnoth-completion-cache'." (setq tags (append tags (list (cons tag iter))))) (setq wesnoth-completion-cache tags))))) -(defun wesnoth-insert-tag (tagname &optional start end) +(defun wesnoth-insert-tag (&optional elements tagname) "Insert the specified opening tag and it's matching closing tag. Both the opening and closing tags will be placed on their own lines with point positioned between them. Completion of tags at the prompt uses `wesnoth-tags-list'. -TAGNAME is the name of the tag to be inserted. If START and END -are given, the tags will be inserted around the specified region. -Enabling function `transient-mark-mode' will cause `wesnoth-insert-tag' to -insert opening and closing tags around the specified region." - (interactive - (list (completing-read "Tag: " (wesnoth-build-completion) nil nil))) - (when (and (not (or start end)) transient-mark-mode mark-active) - (setq start (region-beginning) - end (copy-marker (region-end)))) - (if (and start end) - (progn - (goto-char start) - (or (looking-at "^[\t ]*$") - (open-line 1)) - (insert "[" tagname "]") - (goto-char end) - (beginning-of-line) - (if (looking-at "^[\t ]*$") - (open-line 1) - (end-of-line) - (newline)) - (insert "[/" tagname "]") - (setq end (point)) ;; New target for indent-region - (indent-region start end nil)) - (beginning-of-line) - (or (looking-at "^[\t ]*$") - (progn - (end-of-line) - (newline))) - (end-of-line) - (wesnoth-insert-and-indent "[" tagname "]") - (wesnoth-insert-and-indent "\n") +ELEMENTS is specifies the number of following blocks which the +tag should wrap around. + +TAGNAME is the name of the tag to be inserted." + (interactive "Ps") + (unless tagname + (setq tagname (completing-read "Tag: " (wesnoth-build-completion)))) + (or elements (setq elements 0)) + (let ((depth 0) + (start (save-excursion (forward-line -1) (point)))) + (wesnoth-insert-element-separately "[" tagname "]") + (when (= elements 0) + (newline)) + (save-excursion + (when (= elements 0) + (newline)) + (while (> elements 0) + (wesnoth-search-for-matching-tag + 'search-forward-regexp wesnoth-element-closing 'point-max) + (decf elements) + (beginning-of-line)) + (wesnoth-insert-element-separately "[/" tagname "]") + (indent-region start (point))) + (wesnoth-indent))) + +(defun wesnoth-insert-element-separately (&rest strings) + "Concatenate STRINGS and insert them on a line of their own." + (let ((create-newline nil)) (save-excursion - (wesnoth-insert-and-indent "\n[/" tagname "]")))) + (beginning-of-line) + (unless (looking-at "^[\t ]*$") + (setq create-newline t))) + (when create-newline + (if (> (point) + (save-excursion + (back-to-indentation) + (point))) + (progn + (end-of-line) + (newline)) + (beginning-of-line)) + (open-line 1)) + (insert (apply 'concat strings)))) (defun wesnoth-insert-missing-closing (&optional start end) "Insert the next expected closing element at point. @@ -355,24 +335,21 @@ respectively." (setq start (point-min) end (point-max))) (let ((element (wesnoth-check-structure start end))) - (if element - (if (string= element "Unexpected end of file") - (message "%s" "Error: Expected end of file") - (when (not (looking-at "^[\t ]*$")) - (end-of-line) - (wesnoth-newline)) - (insert element) - (wesnoth-indent) - (end-of-line)) - (error "%s" "Unable to find element to insert")))) + (if (not element) + (error "%s" "Unable to find element to insert") + (when (string= element "Unexpected end of file") + (error "%s" element)) + (when (not (looking-at "[\t ]*$")) + (end-of-line) + (wesnoth-newline)) + (wesnoth-insert-and-indent element)))) (defun wesnoth-insert-and-indent (&rest args) "Concatenate and insert the given string(s) before indenting. ARGS is a list of strings to be inserted." (insert (apply 'concat args)) - (wesnoth-indent) - (end-of-line)) + (wesnoth-indent)) (defun wesnoth-newline (&optional indent) "Indent both the current line and the newline created. @@ -387,106 +364,65 @@ non-nil." (newline)) ;;; Movement +(defmacro wesnoth-navigate-element (repeat search-function bound) + "Move point to the tag in the given direction REPEAT times. + +SEARCH-FUNCTION is the symbol of the function for searching in +the required direction, with BOUND marking the furthest point to +search." + `(progn + (or ,repeat (setq ,repeat 1)) + (while (> ,repeat 0) + (and (eq ,search-function 'search-forward-regexp) (end-of-line)) + (funcall ,search-function wesnoth-element-opening + ,bound t) + (back-to-indentation) + (decf ,repeat)))) + (defun wesnoth-forward-element (repeat) "Move point to the end of the next tag. REPEAT is an optional numeric argument. If REPEAT is non-nil, jump forward the specified number of tags." (interactive "p") - (or repeat (setq repeat 1)) (if (< repeat 0) (wesnoth-backward-element (abs repeat)) - (let ((iterations 0)) - (while (< iterations repeat) - (end-of-line) - (search-forward-regexp - (wesnoth-element-opening t) - (buffer-size) t) - (setq iterations (1+ iterations)))) - (end-of-line))) + (wesnoth-navigate-element repeat 'search-forward-regexp (point-max)))) (defun wesnoth-backward-element (repeat) "Move point to the beginning of the previous tag. REPEAT is an optional numeric argument. If REPEAT is non-nil, jump backward the specified number of tags." (interactive "p") - (or repeat (setq repeat 1)) (if (< repeat 0) (wesnoth-forward-element (abs repeat)) - (let ((iterations 0)) - (while (< iterations repeat) - (beginning-of-line) - (search-backward-regexp - (wesnoth-element-opening t) - 0 t) - (unless (bobp) - (search-forward-regexp "[^[:blank:]]") - (backward-char)) - (setq iterations (1+ iterations)))))) + (wesnoth-navigate-element repeat 'search-backward-regexp (point-min)))) + +(defmacro wesnoth-search-for-matching-tag (search-function search-string bound) + "Search for the matching tag for the current line. + +SEARCH-FUNCTION is the name of the function used to perform the search. +SEARCH-STRING is a string representing the matching tag type. +BOUND is the bound to be passed to the search function." + `(let ((depth 1)) + (unless (looking-at ,search-string) + (unless (> (point) (funcall ,bound)) (end-of-line)) + (while (and (> depth 0) + (funcall ,search-function wesnoth-element + (funcall ,bound) t)) + (if (string-match ,search-string (match-string 0)) + (decf depth) + (incf depth)))))) (defun wesnoth-jump-to-matching () - "Jump point to the matching opening/closing tag. -A tag must be on the same line as point for jumping to occur. -Tag structure between the start and target positions must be -consistent for jumping to occur." + "Jump point to the matching opening/closing tag." (interactive) - (let ((open-tags 0) - (search-started nil) - (tag-position nil) - (search-backward nil)) - (save-excursion - (beginning-of-line) - (if (looking-at - (wesnoth-element t)) - (when (looking-at (wesnoth-element-closing t)) - (setq search-backward t)) - (if (wesnoth-wml-start-pos) - (if (> (point) (wesnoth-wml-start-pos)) - (search-backward-regexp - (wesnoth-element t) - (point-min) t) - (goto-char (point-min)) - (search-forward-regexp - (wesnoth-element t)) - (beginning-of-line)) - (error "%s" "Unable to locate tag to jump from"))) - (if search-backward - (progn - (end-of-line) - (while (and - (or (< open-tags 0) (not search-started)) - (search-backward-regexp - (wesnoth-element t) - (point-min) t)) - (setq search-started t) - (if (looking-at (wesnoth-element-opening t)) - (setq open-tags (1+ open-tags)) - (when (looking-at (wesnoth-element-closing t)) - (setq open-tags (1- open-tags))))) - (end-of-line) - (search-backward-regexp "\\[\\|#")) - (while (and - (or (> open-tags 0) (not search-started)) - (search-forward-regexp - (wesnoth-element t) - (point-max) t)) - (beginning-of-line) - (setq search-started t) - (if (looking-at (wesnoth-element-opening t)) - (setq open-tags (1+ open-tags)) - (when (looking-at (wesnoth-element-closing t)) - (setq open-tags (1- open-tags)))) - (end-of-line)) - (end-of-line)) - (setq tag-position (point))) - (and search-backward - (end-of-line)) - (and (wesnoth-check-structure (min (point) tag-position) - (max (point) tag-position)) - (message "%s" "Region concerning jump does not nest correctly; \ -target may not be correct")) - (if (interactive-p) - (goto-char tag-position) - tag-position))) + (beginning-of-line) + (if (looking-at wesnoth-element-opening) + (wesnoth-search-for-matching-tag + 'search-forward-regexp wesnoth-element-closing 'point-max) + (wesnoth-search-for-matching-tag + 'search-backward-regexp wesnoth-element-opening 'wesnoth-wml-start-pos)) + (back-to-indentation)) (defun wesnoth-wml-start-pos () "Determine the position of `point' relative to where the actual WML begins. @@ -494,37 +430,39 @@ Return the likely starting position of the WML if it is found. Otherwise return nil." (save-excursion (goto-char (point-min)) - (when (search-forward-regexp (wesnoth-element t) (buffer-size) t) + (when (search-forward-regexp wesnoth-element (point-max) t) (beginning-of-line) (point)))) +(defun first-column-indent-p (point) + "Return non-nil if the current line should not be indented. + +POINT is the position in the buffer to check. +CONTEXT represents the type of element which precedes the current element." + (or (not (wesnoth-wml-start-pos)) + (<= (point) (wesnoth-wml-start-pos)) + (nth 3 (parse-partial-sexp (point-min) point)) + (looking-at wesnoth-preprocessor-regexp))) + (defun wesnoth-indent () "Indent the current line as WML." (beginning-of-line) - (multiple-value-bind (context ref-indent) - (wesnoth-determine-context (point)) - (if (or (not (wesnoth-wml-start-pos)) - (<= (point) (wesnoth-wml-start-pos)) - (nth 3 (parse-partial-sexp (point-min) (point))) - (and wesnoth-indent-preprocessor-bol - (looking-at wesnoth-preprocessor-regexp)) - (not context)) - (indent-line-to 0) - (let ((cur-indent)) + (unless (first-column-indent-p (point)) + (multiple-value-bind (context ref-indent) + (wesnoth-determine-context (point)) + (let ((cur-indent 0)) (cond ((eq context 'opening) (if (or (looking-at "^[\t ]*\\[[^/]") - (looking-at (wesnoth-element-opening)) - (and (not (looking-at (wesnoth-element-closing))) - wesnoth-indent-savefile)) + (looking-at wesnoth-element-opening) + (not (looking-at wesnoth-element-closing))) (setq cur-indent (+ ref-indent wesnoth-base-indent)) (setq cur-indent ref-indent))) ((eq context 'closing) (if (looking-at "^[\t ]*\\[/") (setq cur-indent (- ref-indent wesnoth-base-indent)) (setq cur-indent ref-indent)))) - (when cur-indent - (indent-line-to (max cur-indent 0))))))) + (indent-line-to (max cur-indent 0)))))) (defun wesnoth-within-define (position) "Determine whether point is currently inside a #define block. @@ -572,10 +510,10 @@ Creates and destroys a cache of macro definition details as necessary." (setq wesnoth-define-blocks (wesnoth-find-macro-definitions)) (or (bolp) (forward-line 1)) (while (< (point) end) - (forward-line 1) (if (looking-at "^[\t ]*$") (indent-line-to 0) - (funcall indent-line-function)))) + (funcall indent-line-function)) + (forward-line 1))) (setq wesnoth-define-blocks nil))) (defun wesnoth-determine-context (position) @@ -584,12 +522,12 @@ Creates and destroys a cache of macro definition details as necessary." POSITION is the buffer position of the element for which to determine the context." (save-excursion - (search-backward-regexp (wesnoth-element) (wesnoth-wml-start-pos) t) + (search-backward-regexp wesnoth-element (wesnoth-wml-start-pos) t) (let ((match (or (match-string 1) "")) (depth (wesnoth-within-define position))) (while (and (> (wesnoth-within-define (point)) depth) (not (= (point) (wesnoth-wml-start-pos)))) - (search-backward-regexp (wesnoth-element) + (search-backward-regexp wesnoth-element (wesnoth-wml-start-pos) t) (setq match (match-string 1))) (when (and (= (point) (wesnoth-wml-start-pos)) (= depth 0) @@ -625,23 +563,20 @@ position." (missing-tag-name nil)) (save-excursion (goto-char (point-min)) - (while (and - (search-forward-regexp "^[\t ]*\\[" (point-max) t) - (not tag-position)) + (while (and (search-forward-regexp "^[\t ]*\\[" (point-max) t) + (not tag-position)) (beginning-of-line) - (when (looking-at "^[\t ]*\\[[/]?\\(\\(\\(\\w\\|_\\)+\\|_\\)\\)\\]") + (when (looking-at "^[\t ]*\\[/?\\(\\(\\w\\|_\\)+\\|_\\)\\]") (unless (member (match-string-no-properties 1) wesnoth-tags-list) (setq tag-position (point)) (setq missing-tag-name (match-string-no-properties 1)))) (end-of-line))) - (if tag-position - (progn - (goto-char tag-position) - (end-of-line) - (search-backward "[") - (message "'%s' is not known to exist" - missing-tag-name)) - (message "%s" "No unknown tag names found.")))) + (if (not tag-position) + (message "%s" "No unknown tag names found.") + (goto-char tag-position) + (back-to-indentation) + (message "'%s' is not known to exist" + missing-tag-name)))) (defun wesnoth-check-structure (&optional start end) "Check the buffer for correct nesting of elements. @@ -666,11 +601,11 @@ positions of the buffer, respectively." (save-excursion (goto-char start) (while (and (search-forward-regexp - (wesnoth-element t) + wesnoth-element end t) (not error-position)) (search-backward-regexp - (wesnoth-element t) + wesnoth-element start t) (if (looking-at wesnoth-preprocessor-regexp) (let ((preprocessor-name (match-string-no-properties 1))) -- 2.11.4.GIT