From 70cf5f3241b32f3a28961c8265dd3056158b83af Mon Sep 17 00:00:00 2001 From: Chris Mann Date: Sun, 19 Oct 2008 09:40:04 +1030 Subject: [PATCH] * wesnoth-mode.el (wesnoth-mode-map): Added C-c C-u binding to update macro information. (wesnoth-menu): Also added it to the menu. (wesnoth-element-opening, wesnoth-element): Updated to fix a bug not matching [+text] tags. (wesnoth-macro-arguments): Allow tags; multiple spaces between arguments. (wesnoth-insert-tag): Only insert extra space if point will be positioned there (therefore, not #define or #ifn?def). (wesnoth-insert-element-separately): Avoid leaving extra space if used immediately after an element. (wesnoth-check-element-type): Ensure previous match data used when checking member'ship. (wesnoth-extract-macro-details): New function. (wesnoth-check-wml): Check macros separately. Check macro arguments. Fixed a bug where not all macros were checked. Don't check strings. Check preprocessor has arguments when necessary. --- wesnoth-mode.el | 188 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 105 insertions(+), 83 deletions(-) diff --git a/wesnoth-mode.el b/wesnoth-mode.el index 342a596..c8effcb 100644 --- a/wesnoth-mode.el +++ b/wesnoth-mode.el @@ -35,10 +35,13 @@ ;;; History: ;; 1.3.1 ;; * Completion history available is now specific to wesnoth-mode. +;; * Added binding to explicitly update macro information from the current +;; buffer (C-c C-u). ;; * Significantly improved performance of completion and WML checking. ;; * Improved performance for inserting missing tags. ;; * Fixed a bug where #ifdef was never matched when checking WML. ;; * Added completion for preprocessor statements. +;; * Added completion and checking for macro arguments. ;; 1.3.0 ;; * Added support for Xemacs. ;; * WML checking is now context sensitive; checks attributes and macros. @@ -191,6 +194,7 @@ level as their parent.") (define-key map (kbd "C-c C-a") 'wesnoth-complete-attribute) (define-key map (kbd "C-c C-t") 'wesnoth-complete-tag) (define-key map (kbd "C-c C-p") 'wesnoth-complete-preprocessor) + (define-key map (kbd "C-c C-u") 'wesnoth-update-project-information) (define-key map (kbd "M-TAB") 'wesnoth-complete-tag) (define-key map (kbd "C-c C-m") 'wesnoth-complete-macro) (define-key map (kbd "C-c C-o") 'wesnoth-jump-to-matching) @@ -211,7 +215,8 @@ level as their parent.") ["Insert Macro" wesnoth-complete-macro t] ["Insert Preprocessor" wesnoth-complete-preprocessor t] ["Insert Missing Tag" wesnoth-insert-missing-closing t] - ["Jump to Matching" wesnoth-jump-to-matching t])) + ["Jump to Matching" wesnoth-jump-to-matching t] + ["Update Macros" wesnoth-update-project-information t])) (defvar wesnoth-syntax-table (let ((wesnoth-syntax-table (make-syntax-table))) @@ -262,7 +267,7 @@ If LIMITED is non-nil, return a regexp which matches only the "Return the regexp to match a closing element. If LIMITED is non-nil, return a regexp which matches only the #define preprocessor." - (concat "^[\t ]*\\(\\[\\(\\w\\|_\\)+\\]\\|#define " + (concat "^[\t ]*\\(\\[\\+?\\(\\w\\|_\\)+\\]\\|#define " (if limited "" "\\|#ifn?def ") @@ -272,7 +277,7 @@ If LIMITED is non-nil, return a regexp which matches only the "Return the regexp to match a closing element. If LIMITED is non-nil, return a regexp which matches only the #define and #enddef preprocessors." - (concat "^[\t ]*\\(\\[/?\\(\\w\\|_\\)+\\]?\\|" + (concat "^[\t ]*\\(\\[[/+]?\\(\\w\\|_\\)+\\]?\\|" (if limited "#define \\|#enddef" (substring wesnoth-preprocessor-regexp 5)) @@ -409,7 +414,7 @@ If COMPLETEP is non-nil, attempt to complete preprocessor at point." (unless (= 0 (wesnoth-within-define (point))) (save-match-data (search-backward-regexp - "[\t ]*#define \\(?:\\w+\\|_\\)*\\(\\( \\(\\w\\|_\\)+\\)*\\)" + "[\t ]*#define \\(?:\\w+\\|_\\)*\\(\\([\t ]*\\(\\w\\|_\\)+\\)*\\)" (point-min) t) (mapcar (lambda (macro) (list macro nil)) (split-string @@ -553,7 +558,7 @@ TAGNAME is the name of the tag to be inserted." (save-excursion (if end (goto-char (marker-position end)) - (newline 2)) + (newline (if (string-match wesnoth-preprocessor-regexp tagname) 1 2))) (if (string-match wesnoth-preprocessor-opening-regexp tagname) (wesnoth-insert-element-separately (if (string= tagname "#define ") @@ -567,17 +572,17 @@ TAGNAME is the name of the tag to be inserted." (defun wesnoth-insert-element-separately (&rest strings) "Concatenate STRINGS and insert them on a line of their own." - (let ((create-newline (save-excursion - (beginning-of-line) - (if (looking-at "^[\t ]*$") nil t)))) - (when create-newline - (if (> (point) (save-excursion (back-to-indentation) (point))) + (if (save-excursion (and (> (point) (progn (back-to-indentation) (point))))) + (if (save-excursion (forward-line 1) (looking-at "^[\t ]*$")) (progn - (end-of-line) - (newline)) - (beginning-of-line) - (open-line 1))) - (insert (apply 'concat strings)))) + (forward-line 1) + (end-of-line)) + (end-of-line) + (newline)) + (beginning-of-line) + (unless (looking-at "^[\t ]*$") + (open-line 1))) + (insert (apply 'concat strings))) (defun wesnoth-insert-missing-closing (&optional start end) "Insert the next expected closing element at point. @@ -895,14 +900,14 @@ be performed." "Determine the context of the element. POSITION is the position of the element in the list. LAST-TAG is the parent element." - (save-match-data - (if (or (not last-tag) - (string-match "#\\(?:define\\|ifn?def\\)" last-tag)) - (member (match-string-no-properties 1) - (mapcar 'car wesnoth-tag-data)) + (if (or (not last-tag) + (save-match-data + (string-match "#\\(?:define\\|ifn?def\\)" last-tag))) (member (match-string-no-properties 1) - (nth position (gethash last-tag - wesnoth-tag-hash-table)))))) + (mapcar 'car wesnoth-tag-data)) + (member (match-string-no-properties 1) + (nth position (gethash last-tag + wesnoth-tag-hash-table))))) ;; Provide `line-number-at-pos' implementation (not available in Emacs 21). (defun wesnoth-line-number-at-pos (&optional pos) @@ -929,6 +934,17 @@ ARGS is any additional data required by `format' to handle FORMAT-STRING." (insert (apply 'format (concat "Line %d: " format-string "\n") lnap args))))) +(defun wesnoth-extract-macro-details (macro-arguments) + "Return a list of all macros in MACRO-ARGUMENTS." + (when macro-arguments + (let ((results '())) + (save-match-data + (dolist (macro (split-string macro-arguments "[{}][\t ]*" t)) + (when (string-match "^\\(\\(?:\\w\\|_\\)+\\)" + macro) + (add-to-list 'results (match-string-no-properties 1 macro))))) + results))) + (defun wesnoth-check-wml () "Perform context-sensitive analysis of WML-code." (interactive) @@ -945,71 +961,77 @@ ARGS is any additional data required by `format' to handle FORMAT-STRING." (message (format "Checking %s..." buffer)))) (save-excursion (goto-char (or (wesnoth-wml-start-pos) (point-min))) + (while (search-forward-regexp "[\t ]*{\\(\\(\\(\\w\\|_\\)+\\)[^= +]*\\)}" (point-max) t) + (dolist (macro (wesnoth-extract-macro-details + (match-string-no-properties 1))) + (unless (assoc macro + (append (wesnoth-macro-arguments) + wesnoth-local-macro-data + wesnoth-macro-data)) + (wesnoth-check-output outbuf "Unknown macro definition: '{%s}'" + macro)))) + (goto-char (or (wesnoth-wml-start-pos) (point-min))) (while (search-forward-regexp - ;; Match tags, preprocessor statements, macros and attributes. - (concat "^[\t ]*\\(\\[[+/]?\\(\\(\\w\\|_\\)+\\)\\]\\|" - "\\(\\w\\|_\\)+=\\|{\\(\\(\\w\\|_\\)+\\).*}\\|" + ;; Match tags, preprocessor statements and attributes. + (concat "^[\t ]*\\(\\[[+/]?\\([a-z]\\(\\w\\|_\\)+\\)\\]\\|" + "\\(\\w\\|_\\)+=\\|" wesnoth-preprocessor-regexp "\\)") (point-max) t) - (beginning-of-line) - (cond ((looking-at "^[\t ]*\\[\\+?\\(\\(\\w\\|_\\)+\\)\\]") - (unless (wesnoth-check-element-type 0 (car unmatched)) - (wesnoth-check-output outbuf - "Tag not available in this context: '%s'" - (match-string-no-properties 1))) - (setq unmatched (cons (match-string-no-properties 1) - unmatched))) - ((looking-at - (concat "[\t ]*\\(#define\\|#ifdef\\|#ifndef\\|#undef\\)" - "\\( \\(\\w\\|_\\)+\\)*")) - (unless (match-string-no-properties 2) - (wesnoth-check-output - outbuf (concat "Preprocessor statement has no argument: " - (match-string-no-properties 1)))) - (unless (string= (match-string-no-properties 1) "#undef") + (save-excursion + (goto-char (match-beginning 1)) + (cond ((nth 3 (parse-partial-sexp (point-min) (point))) + nil) + ((looking-at "[\t ]*\\[\\+?\\(\\(\\w\\|_\\)+\\)\\]") + (unless (wesnoth-check-element-type 0 (car unmatched)) + (wesnoth-check-output outbuf + "Tag not available in this context: '%s'" + (match-string-no-properties 1))) (setq unmatched (cons (match-string-no-properties 1) - unmatched)))) - ((looking-at wesnoth-preprocessor-closing-regexp) - (when (and unmatched - (not (string-match - (cdr (assoc (match-string-no-properties 1) - '(("enddef" . "#define") - ("endif" . "#ifn?def")))) - (car unmatched)))) - (wesnoth-check-output - outbuf - "Preprocessor statement does not nest correctly")) - (setq unmatched (cdr unmatched))) - ((looking-at "^[\t ]*\\(\\(\\w\\|_\\)+\\)=\\(.+\\)?") - (unless (wesnoth-check-element-type 1 (car unmatched)) - (wesnoth-check-output - outbuf "Attribute not available in this context: '%s'" - (match-string-no-properties 1))) - (unless (match-string 3) - (wesnoth-check-output - outbuf "Attribute has no value"))) - ((looking-at "^[\t ]*#else") - (unless (string-match "ifn?def" (car unmatched)) - (if (string= (car unmatched) "#define") - (wesnoth-check-output outbuf "Expecting: '%s'" - (car unmatched)) + unmatched))) + ((looking-at + (concat "[\t ]*\\(#define\\|#ifdef\\|#ifndef\\|#undef\\)" + "\\( \\(\\w\\|_\\)+\\)*")) + (unless (match-string-no-properties 2) + (wesnoth-check-output + outbuf (concat "Preprocessor statement has no argument: " + (match-string-no-properties 1)))) + (unless (string= (match-string-no-properties 1) "#undef") + (setq unmatched (cons (match-string-no-properties 1) + unmatched)))) + ((looking-at wesnoth-preprocessor-closing-regexp) + (when (and unmatched + (not (string-match + (cdr (assoc (match-string-no-properties 1) + '(("enddef" . "#define") + ("endif" . "#ifn?def")))) + (car unmatched)))) + (wesnoth-check-output + outbuf + "Preprocessor statement does not nest correctly")) + (setq unmatched (cdr unmatched))) + ((looking-at "[\t ]*\\(\\(\\w\\|_\\)+\\)=\\(.+\\)?") + (unless (wesnoth-check-element-type 1 (car unmatched)) + (wesnoth-check-output + outbuf "Attribute not available in this context: '%s'" + (match-string-no-properties 1))) + (unless (match-string 3) + (wesnoth-check-output + outbuf "Attribute has no value"))) + ((looking-at "[\t ]*#else") + (unless (string-match "ifn?def" (car unmatched)) + (if (string= (car unmatched) "#define") + (wesnoth-check-output outbuf "Expecting: '%s'" + (car unmatched)) + (wesnoth-check-output outbuf "Expecting: '[/%s]'" + (car unmatched))))) + ((looking-at "[\t ]*\\[/\\(\\(\\w\\|_\\)+\\)\\]") + (when (and unmatched + (not (string= (match-string-no-properties 1) + (car unmatched)))) (wesnoth-check-output outbuf "Expecting: '[/%s]'" - (car unmatched))))) - ((looking-at "^[\t ]*{\\(\\(\\w\\|_\\)+\\).*}") - (unless (assoc (match-string-no-properties 1) - (append (wesnoth-macro-arguments) - wesnoth-local-macro-data - wesnoth-macro-data)) - (wesnoth-check-output outbuf "Unknown macro definition: '{%s}'" - (match-string-no-properties 1)))) - ((looking-at "^[\t ]*\\[/\\(\\(\\w\\|_\\)+\\)\\]") - (when (and unmatched - (not (string= (match-string-no-properties 1) - (car unmatched)))) - (wesnoth-check-output outbuf "Expecting: '[/%s]'" - (car unmatched))) - (setq unmatched (cdr unmatched)))) - (end-of-line)) + (car unmatched))) + (setq unmatched (cdr unmatched)))))) (if unmatched (dolist (tag unmatched) (wesnoth-check-output outbuf "Unmatched element: '%s'" @@ -1087,7 +1109,7 @@ positions of the buffer, respectively." (if (or (looking-at "^[\t ]*\\[\\(\\(\\w\\|_\\)+\\)\\]") (looking-at "[\t ]*#\\(define \\|ifdef \\|ifndef \\)")) (setq unmatched (cons (match-string-no-properties 1) - unmatched)) + unmatched)) (cond ((wesnoth-element-requires "#else" "ifn?def ")) ((wesnoth-element-requires "#endif" "ifn?def " t)) -- 2.11.4.GIT