branch: elpa/d-mode
commit 322250aa765f15b6e302992d9e59d67c600e18d8
Merge: 38ddb5a ed4ae58
Author: Russel Winder <[email protected]>
Commit: Russel Winder <[email protected]>
Merge pull request #46 from dmakarov/testing
Testing infrastructure and various other amendments.
---
.travis.yml | 26 ++++++++
Cask | 5 ++
Makefile | 13 ++++
README.md | 4 ++
d-mode-test.el | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
d-mode.el | 42 +++++++++----
tests/I0039.d | 54 ++++++++++++++++
7 files changed, 326 insertions(+), 12 deletions(-)
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..8cf2346
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,26 @@
+language: emacs-lisp
+
+before_install:
+ # PPA for stable Emacs packages
+ - sudo add-apt-repository -y ppa:cassou/emacs
+ # PPA for Emacs nightlies
+ - sudo add-apt-repository -y ppa:ubuntu-elisp/ppa
+
+install:
+ # Update and install the Emacs for our environment
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq -yy ${EMACS}-nox ${EMACS}-el
+ # Install cask dependencies
+ - curl -fsSLo /tmp/cask-master.zip
https://github.com/cask/cask/archive/master.zip
+ - sudo unzip -qq -d /opt /tmp/cask-master.zip
+ - sudo ln -sf /opt/cask-master/bin/cask /usr/local/bin/cask
+ - cask
+
+env:
+ - EMACS=emacs24
+ - EMACS=emacs-snapshot
+
+script:
+ - emacs --version
+ - make compile
+ - make test
diff --git a/Cask b/Cask
new file mode 100644
index 0000000..5336cec
--- /dev/null
+++ b/Cask
@@ -0,0 +1,5 @@
+(source gnu)
+(source melpa)
+
+(development
+ (depends-on "undercover"))
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a51239f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+emacs ?= emacs
+all: test
+
+test: clean
+ cask exec emacs -Q -batch -l d-mode-test.el -l d-mode.el -f
ert-run-tests-batch-and-exit
+
+compile:
+ $(emacs) -Q -batch -f batch-byte-compile d-mode.el
+
+clean:
+ rm -f d-mode.elc
+
+.PHONY: all test
diff --git a/README.md b/README.md
index c3cd9a3..5ccc3bb 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
# Emacs D Mode
+[](https://travis-ci.org/dmakarov/Emacs-D-Mode)
+[](https://coveralls.io/r/dmakarov/Emacs-D-Mode?branch=testing)
+[](http://melpa.org/#/d-mode)
+
An Emacs major mode for editing D code.
This mode is currently known to work with Emacs 24 and believed to work with
Emacs 23.
diff --git a/d-mode-test.el b/d-mode-test.el
new file mode 100644
index 0000000..65d8d05
--- /dev/null
+++ b/d-mode-test.el
@@ -0,0 +1,194 @@
+;;; d-mode-test.el --- Tests for D Programming Language major mode
+
+;; Author: Dmitri Makarov <[email protected]>
+;; Maintainer: Russel Winder <[email protected]>
+;; Created: April 2015
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+(when (require 'undercover nil t)
+ (undercover "d-mode.el"))
+
+(require 'd-mode nil t)
+
+(defconst d-test-teststyle
+ '((c-tab-always-indent . t)
+ (c-basic-offset . 2)
+ (c-comment-only-line-offset . 0)
+ (c-comment-prefix-regexp . "\\(//+\\|\\**\\)[.!|]?")
+ (c-hanging-braces-alist . ((block-open after)
+ (brace-list-open)
+ (substatement-open after)
+ (inexpr-class-open after)
+ (inexpr-class-close before)))
+ (c-hanging-colons-alist . ((member-init-intro before)
+ (inher-intro)
+ (case-label after)
+ (label after)
+ (access-key after)))
+ (c-cleanup-list . (scope-operator empty-defun-braces defun-close-semi))
+ (c-offsets-alist
+ . ((string . c-lineup-dont-change)
+ (c . c-lineup-C-comments)
+ (defun-open . 0)
+ (defun-close . 0)
+ (defun-block-intro . +)
+ (class-open . 0)
+ (class-close . 0)
+ (inline-open . 0)
+ (inline-close . 0)
+ (func-decl-cont . +)
+ (topmost-intro . 0)
+ (topmost-intro-cont . c-lineup-topmost-intro-cont)
+ (member-init-intro . +)
+ (member-init-cont . c-lineup-multi-inher)
+ (inher-intro . +)
+ (inher-cont . c-lineup-multi-inher)
+ (block-open . 0)
+ (block-close . 0)
+ (brace-list-open . 0)
+ (brace-list-close . 0)
+ (brace-list-intro . +)
+ (brace-list-entry . 0)
+ (statement . 0)
+ (statement-cont . +)
+ (statement-block-intro . +)
+ (statement-case-intro . +)
+ (statement-case-open . 0)
+ (substatement . +)
+ (substatement-open . +)
+ (substatement-label . *)
+ (case-label . 0)
+ (access-label . -)
+ (label . *)
+ (do-while-closure . 0)
+ (else-clause . 0)
+ (catch-clause . 0)
+ (comment-intro . c-lineup-comment)
+ (arglist-intro . +)
+ (arglist-cont . (c-lineup-gcc-asm-reg 0))
+ (arglist-cont-nonempty . (c-lineup-gcc-asm-reg c-lineup-arglist))
+ (arglist-close . +)
+ (stream-op . c-lineup-streamop)
+ (inclass . +)
+ (extern-lang-open . 0)
+ (extern-lang-close . 0)
+ (inextern-lang . +)
+ (namespace-open . 0)
+ (namespace-close . 0)
+ (innamespace . +)
+ (module-open . 0)
+ (module-close . 0)
+ (inmodule . 0)
+ (composition-open . 0)
+ (composition-close . 0)
+ (incomposition . 0)
+ (template-args-cont . (c-lineup-template-args +))
+ (inlambda . c-lineup-inexpr-block)
+ (lambda-intro-cont . +)
+ (inexpr-statement . +)
+ (inexpr-class . +)))
+ (c-echo-syntactic-information-p . t)
+ (c-indent-comment-alist . nil))
+ "Style for testing.")
+
+(c-add-style "teststyle" d-test-teststyle)
+
+(defun make-test-buffers (filename)
+ (let ((testbuf (get-buffer-create "*d-mode-test*"))
+ (enable-local-eval t))
+ ;; setup the test file buffer.
+ (set-buffer testbuf)
+ (kill-all-local-variables)
+ (buffer-disable-undo testbuf)
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ (insert-file-contents filename)
+ ;; test that we make no (hidden) changes.
+ (setq buffer-read-only t)
+ (goto-char (point-min))
+ (let ((c-default-style "TESTSTYLE")
+ d-mode-hook c-mode-common-hook)
+ (d-mode))
+ (hack-local-variables)
+ (list testbuf)))
+
+(defun kill-test-buffers ()
+ (let (buf)
+ (if (setq buf (get-buffer "*d-mode-test*"))
+ (kill-buffer buf))))
+
+(defun d-test-message (msg &rest args)
+ (if noninteractive
+ (send-string-to-terminal
+ (concat (apply 'format msg args) "\n"))
+ (apply 'message msg args)))
+
+(defun do-one-test (filename)
+ (interactive "fFile to test: ")
+ (let* ((save-buf (current-buffer))
+ (save-point (point))
+ (font-lock-maximum-decoration t)
+ (font-lock-global-modes nil)
+ (enable-local-variables ':all)
+ (buflist (make-test-buffers filename))
+ (testbuf (car buflist))
+ (pop-up-windows t)
+ (linenum 1)
+ error-found-p
+ expectedindent
+ c-echo-syntactic-information-p)
+
+ (switch-to-buffer testbuf)
+ ;; Record the expected indentation and reindent. This is done
+ ;; in backward direction to avoid cascading errors.
+ (while (= (forward-line -1) 0)
+ (back-to-indentation)
+ (setq expectedindent (cons (current-column) expectedindent))
+ (unless (eolp)
+ ;; Do not reindent empty lines; the test cases might have
+ ;; whitespace at eol trimmed away, so that could produce
+ ;; false alarms.
+ (let ((buffer-read-only nil))
+ (if no-error
+ (condition-case err (c-indent-line)
+ (error
+ (unless error-found-p
+ (setq error-found-p t)
+ (d-test-message
+ "%s:%d: c-indent-line error: %s" filename
+ (1+ (count-lines (point-min) (c-point 'bol)))
+ (error-message-string err)))))
+ (c-indent-line)))))
+
+ (when (and error-found-p (not no-error))
+ (set-buffer testbuf)
+ (buffer-enable-undo testbuf)
+ (set-buffer-modified-p nil)
+
+ (error "Regression found in file %s" filename))
+
+ (set-buffer save-buf)
+ (goto-char save-point)
+ (when (and (not error-found-p) (interactive-p))
+ (kill-test-buffers))
+ (not error-found-p)))
+
+;; Run the tests
+(ert-deftest d-mode-basic ()
+ (should (equal (do-one-test "tests/I0039.d") t)))
+
+(provide 'd-mode-test)
diff --git a/d-mode.el b/d-mode.el
index ac613d8..2c6c2ff 100644
--- a/d-mode.el
+++ b/d-mode.el
@@ -82,6 +82,11 @@
;; constants are evaluated then.
(c-add-language 'd-mode 'java-mode))
+;; muffle the warnings about using free variables and undefined
+;; functions
+(defvar c-syntactic-element)
+(declare-function c-populate-syntax-table "cc-langs.el" (table))
+
;; D has pointers
(c-lang-defconst c-type-decl-prefix-key
d (concat "\\("
@@ -455,24 +460,37 @@ operators."
(nil d-imenu-method-index-function 2)))
;;----------------------------------------------------------------------------
-;;;Workaround for special case of 'else static if' not being handled properly
-(defun d-special-case-looking-at (oldfun &rest args)
+;;; Workaround for special case of 'else static if' not being handled properly
+(defun d-special-case-looking-at (orig-fun &rest args)
(let ((rxp (car args)))
(if (and (stringp rxp) (string= rxp "if\\>[^_]"))
- (or (apply oldfun '("static\\>[^_]"))
- (apply oldfun '("version\\>[^_]"))
- (apply oldfun '("debug\\>[^_]"))
- (apply oldfun args))
- (apply oldfun args))))
+ (or (apply orig-fun '("static\\>\\s-+if\\>[^_]"))
+ (apply orig-fun '("version\\>[^_]"))
+ (apply orig-fun '("debug\\>[^_]"))
+ (apply orig-fun args))
+ (apply orig-fun args))))
-(defadvice c-add-stmt-syntax (around my-c-add-stmt-syntax-wrapper activate)
+(defun d-around--c-add-stmt-syntax (orig-fun &rest args)
(if (not (string= major-mode "d-mode"))
- ad-do-it
+ (apply orig-fun args)
(progn
- (add-function :around (symbol-function 'looking-at)
#'d-special-case-looking-at)
+ (add-function :around (symbol-function 'looking-at)
+ #'d-special-case-looking-at)
(unwind-protect
- ad-do-it
- (remove-function (symbol-function 'looking-at)
#'d-special-case-looking-at)))))
+ (apply orig-fun args)
+ (remove-function (symbol-function 'looking-at)
+ #'d-special-case-looking-at)))))
+
+(if (> emacs-major-version 24)
+ (advice-add 'c-add-stmt-syntax :around #'d-around--c-add-stmt-syntax)
+ (defadvice c-add-stmt-syntax (around d-around--c-add-stmt-syntax activate)
+ (if (not (string= major-mode "d-mode"))
+ ad-do-it
+ (progn
+ (add-function :around (symbol-function 'looking-at)
#'d-special-case-looking-at)
+ (unwind-protect
+ ad-do-it
+ (remove-function (symbol-function 'looking-at)
#'d-special-case-looking-at))))))
;;----------------------------------------------------------------------------
;;;###autoload (add-to-list 'auto-mode-alist '("\\.d[i]?\\'" . d-mode))
diff --git a/tests/I0039.d b/tests/I0039.d
new file mode 100644
index 0000000..ff02216
--- /dev/null
+++ b/tests/I0039.d
@@ -0,0 +1,54 @@
+void foo()
+{
+ version (a)
+ {
+ }
+ else version (b)
+ {
+ }
+ else
+ {
+ }
+
+ debug (A)
+ {
+ }
+ else debug (B)
+ {
+ }
+ else
+ {
+ }
+
+ version (a)
+ {
+ }
+ else
+ version (b)
+ {
+ }
+ else
+ {
+ }
+
+ if (true)
+ {
+ }
+ else
+ if (true)
+ {
+ }
+ else
+ {
+ }
+
+ static if (1 < 2)
+ {
+ }
+ else static if (false)
+ {
+ }
+ else static if (true)
+ {
+ }
+}