changeset 174:bd5ad617c85a

d-mode: update
author Jordi Gutiérrez Hermoso <jordigh@octave.org>
date Sun, 07 Jan 2018 12:03:05 -0500
parents 66cbafedef6c
children 56ea66d76309
files dotemacs.el packages/d-mode.el
diffstat 2 files changed, 275 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/dotemacs.el
+++ b/dotemacs.el
@@ -184,6 +184,7 @@
        '(
          ("\\.m\\'" . octave-mode)
          ("\\.eml\\'" . mail-mode)
+         ("\\.d[i]?\\'" . d-mode)
          ("\\.pro\\'" . conf-mode) ;; Qt project files
          ("\\.php\\'" . web-mode) ;; Default php mode isn't as good
          ("\\.?hgrc\\'"  . conf-mode)
--- a/packages/d-mode.el
+++ b/packages/d-mode.el
@@ -2,14 +2,19 @@
 ;;;               Requires a cc-mode of version 5.30 or greater
 
 ;; Author:  William Baxter
-;; Contributors:  Andrei Alexandrescu
-;; Contributors:  Russel Winder
+;; Contributor:  Andrei Alexandrescu
+;; Contributor:  Russel Winder
 ;; Maintainer:  Russel Winder <russel@winder.org.uk>
+;;              Vladimir Panteleev <vladimir@thecybershadow.net>
 ;; Created:  March 2007
-;; Version:  201512060745
+;; Version:  201610221417
 ;; Keywords:  D programming language emacs cc-mode
 
-;;;; NB Version number is date and time yyyymmddhhMM in GMT (aka UTC).
+;;;; NB Version number is date and time yyyymmddhhMM UTC.
+;;;; A hook to update it automatically on save is available here:
+;;;; https://gist.github.com/CyberShadow/28f60687c3bf83d32900cd6074c012cb
+
+;; This file is not part of GNU Emacs.
 
 ;; 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
@@ -49,8 +54,9 @@
 ;; https://github.com/Emacs-D-Mode-Maintainers/Emacs-D-Mode/issues
 
 ;;; Versions:
-;;  This mode is available on MELPA which tracks the mainline Git repository on GitHub, so there is a rolling release
-;;  system based on commits to the mainline.
+;;  This mode is available on MELPA which tracks the mainline Git repository on GitHub, so there is a
+;;  rolling release system based on commits to the mainline. For those wanting releases, the repository is
+;;  tagged from time to time and this creates an entry in MELPA Stable and a tarball on GitHub.
 
 ;;; Notes:
 
@@ -72,8 +78,14 @@
 (require 'compile)
 
 ;; The set-difference function is used from the Common Lisp extensions.
+;; Note that this line produces a compilation warning in Emacs 24 and newer,
+;; however the replacement (cl-seq.el for our use case) was introduced
+;; in the same major version.
 (require 'cl)
 
+;; Used to specify regular expressions in a sane way.
+(require 'rx)
+
 ;; These are only required at compile time to get the sources for the
 ;; language constants.  (The cc-fonts require and the font-lock
 ;; related constants could additionally be put inside an
@@ -183,25 +195,42 @@
 
 ;;; Patterns to recognize the compiler generated messages
 
-;; The following regexp recognizes messages generated by the LDC and DMD
-;; compilers.  Subexpression 1 is the filename, 2 is the line number, nil is the
-;; column, because it's not present in the LDC error messages, and the
-;; subexpressions 3 and 4 are the message type -- error, warning, or info.
+(defun d-mode-add-dmd-message-pattern (expr level symbol)
+  "Register DMD `compile' pattern for an error level.
 
-;; GDC messages are recognized by gnu symbol already listed in
-;; compilation-error-regexp-alist.
-(add-to-list 'compilation-error-regexp-alist-alist
-             '(ldc
-               "^\\([^: \n]+\\)(\\([0-9]+\\)): \\(?: *\\(?3:\\(?:W\\(?::\\|arning\\)\\|warning\\)\\)\\| *\\(?4:[Ii]nfo\\(?:\\>\\|rmationa?l?\\)\\|I:\\|\\[ skipping \\.+ ]\\|instantiated from\\|required from\\|[Nn]ote\\)\\| *\\(?:[Ee]rror\\)\\| *Deprecation\\)"
-               1 2 nil (3 . 4)))
-(add-to-list 'compilation-error-regexp-alist 'ldc)
+EXPR is the `rx' message sub-expression indicating the error level LEVEL.
+The expression is added to `compilation-error-regexp-alist' and
+`compilation-error-regexp-alist-alist' as SYMBOL."
+  (add-to-list
+   'compilation-error-regexp-alist-alist
+   `(,symbol
+     ,(rx-form
+      `(and
+	line-start
+	(group-n 1 (one-or-more any))		; File name
+	"("
+	(group-n 2 (one-or-more digit))		; Line number
+	(zero-or-one
+	 ","
+	 (group-n 3 (one-or-more digit)))	; Column number
+	"): "
+	,expr
+	(group-n 4 (one-or-more nonl))		; Message
+	line-end))
+     1 2 3 ,level 4))
+  (add-to-list 'compilation-error-regexp-alist symbol))
+
+(d-mode-add-dmd-message-pattern "Error: "          2 'dmd-error       )
+(d-mode-add-dmd-message-pattern "Warning: "        1 'dmd-warning     )
+(d-mode-add-dmd-message-pattern "Deprecation: "    1 'dmd-deprecation )
+(d-mode-add-dmd-message-pattern '(one-or-more " ") 0 'dmd-continuation)
 
 ;; The following regexp recognizes messages generated by the D runtime for
 ;; unhandled exceptions (e.g. assert failures).
 
 (add-to-list 'compilation-error-regexp-alist-alist
              '(d-exceptions
-               "^[^@]*?@\\(.*?\\)(\\([0-9]+\\)):"
+               "^[a-zA-z0-9\.]*?@\\(.*?\\)(\\([0-9]+\\)):"
                1 2 nil 2))
 (add-to-list 'compilation-error-regexp-alist 'd-exceptions)
 
@@ -209,14 +238,14 @@
 
 ;; Built-in basic types
 (c-lang-defconst c-primitive-type-kwds
-  d '("bit" "bool" "byte" "ubyte" "char" "delegate" "double" "float"
+  d '("bool" "byte" "ubyte" "char" "delegate" "double" "float"
       "function" "int" "long" "short" "uint" "ulong" "ushort"
-      "cent" "ucent" "real" "ireal" "ifloat" "creal" "cfloat" "cdouble"
+      "cent" "ucent" "real" "ireal" "idouble" "ifloat" "creal" "cfloat" "cdouble"
       "wchar" "dchar" "void" "string" "wstring" "dstring"))
 
 ;; Keywords that can prefix normal declarations of identifiers
 (c-lang-defconst c-modifier-kwds
-  d '("__gshared" "abstract" "const" "deprecated" "extern"
+  d '("__gshared" "abstract" "deprecated" "extern"
       "final" "in" "out" "inout" "lazy" "mixin" "override" "private"
       "protected" "public" "ref" "scope" "shared" "static" "synchronized"
       "volatile" "__vector"))
@@ -230,7 +259,7 @@
 ;;   d '("enum"))
 
 (c-lang-defconst c-type-modifier-kwds
-  d '("__gshared" "const" "inout" "lazy" "shared" "volatile"
+  d '("__gshared" "inout" "lazy" "shared" "volatile"
       "invariant" "enum" "__vector"))
 
 (c-lang-defconst c-type-prefix-kwds
@@ -259,7 +288,7 @@
 (c-lang-defconst c-protection-kwds
   ;; Access protection label keywords in classes.
   d '("deprecated" "static" "extern" "final" "synchronized" "override"
-      "abstract" "scope" "const" "inout" "shared" "__gshared"
+      "abstract" "scope" "inout" "shared" "__gshared"
       "private" "package" "protected" "public" "export"))
 
 ;;(c-lang-defconst c-postfix-decl-spec-kwds
@@ -436,15 +465,73 @@
   (cons "D" (c-lang-const c-mode-menu d)))
 
 (defconst d-imenu-method-name-pattern
-  (concat
-   "^\\s-*"
-   "\\(?:[_a-z@]+\\s-+\\)*"             ; qualifiers
-   "\\([][_a-zA-Z0-9.*!]+\\)\\s-+"      ; type
-   "\\([_a-zA-Z0-9]+\\)\\s-*"           ; function name
-   "\\(?:([^)]*)\\s-*\\)?"              ; type arguments
-   "([^)]*)\\s-*"                       ; arguments
-   "\\(?:[a-z@]+\\s-*\\)?"              ; pure/const etc.
-   "\\(?:;\\|[ \t\n]*\\(?:if\\|{\\)\\)")) ; ';' or 'if' or '{'
+  (rx
+   ;; Whitespace
+   bol
+   (zero-or-more space)
+
+   ;; Conditionals
+   (zero-or-one
+    "else"
+    (zero-or-more space))
+   (zero-or-one
+    "version"
+    (zero-or-more space)
+    "("
+    (zero-or-more space)
+    (one-or-more (any "a-zA-Z0-9_"))
+    (zero-or-more space)
+    ")"
+    (zero-or-more space))
+
+   ;; Qualifiers
+   (zero-or-more
+    (one-or-more (any "a-z_@()C+"))
+    (one-or-more space))
+
+   ;; Type
+   (group
+    (one-or-more (any "a-zA-Z0-9_.*![]()")))
+   (one-or-more space)
+
+   ;; Function name
+   (group
+    (one-or-more (any "a-zA-Z0-9_")))
+   (zero-or-more space)
+
+   ;; Type arguments
+   (zero-or-one
+    "(" (zero-or-more (not (any ")"))) ")"
+    (zero-or-more space))
+
+   ;; Arguments
+   "("
+   (zero-or-more (not (any "()")))
+   (zero-or-more
+    "("
+    (zero-or-more (not (any "()")))
+    ")"
+    (zero-or-more (not (any "()"))))
+   ")"
+   (zero-or-more (any " \t\n"))
+
+   ;; Pure/const etc.
+   (zero-or-more
+    (one-or-more (any "a-z@"))
+    (zero-or-more (any " \t\n")))
+
+   (zero-or-more
+    "//"
+    (zero-or-more not-newline)
+    (zero-or-more space))
+
+   ;; ';' or 'if' or '{'
+   (or
+    ";"
+    (and
+     (zero-or-more (any " \t\n"))
+     (or "if" "{")))
+   ))
 
 (defun d-imenu-method-index-function ()
   (and
@@ -470,10 +557,127 @@
            (not invis))))))
 
 (defvar d-imenu-generic-expression
-  `(("*Classes*" "^\\s-*\\(?:\\(?:final\\|abstract\\)\\s-+\\)?\\<class\\s-+\\([a-zA-Z0-9_]+\\)" 1)
-    ("*Interfaces*" "^\\s-*\\<interface\\s-+\\([a-zA-Z0-9_]+\\)" 1)
-    ("*Structs*" "^\\s-*\\<struct\\s-+\\([a-zA-Z0-9_]+\\)" 1)
-    ("*Templates*" "^\\s-*\\(?:mixin\\s-+\\)?\\<template\\s-+\\([a-zA-Z0-9_]+\\)" 1)
+  `(("*Classes*"
+     ,(rx
+       line-start
+       (zero-or-more (syntax whitespace))
+       (zero-or-more
+	(or "final" "abstract" "private" "package" "protected" "public" "export" "static")
+	(one-or-more (syntax whitespace)))
+       word-start
+       "class"
+       (one-or-more (syntax whitespace))
+       (submatch
+	(one-or-more
+	 (any ?_
+	      (?0 . ?9)
+	      (?A . ?Z)
+	      (?a . ?z)))))
+     1)
+    ("*Interfaces*"
+     ,(rx
+       line-start
+       (zero-or-more (syntax whitespace))
+       word-start
+       "interface"
+       (one-or-more (syntax whitespace))
+       (submatch
+	(one-or-more
+	 (any ?_
+	      (?0 . ?9)
+	      (?A . ?Z)
+	      (?a . ?z)))))
+     1)
+    ("*Structs*"
+     ,(rx
+       line-start
+       (zero-or-more (syntax whitespace))
+       (zero-or-more
+	(or "private" "package" "protected" "public" "export" "static")
+	(one-or-more (syntax whitespace)))
+       word-start
+       "struct"
+       (one-or-more (syntax whitespace))
+       (submatch
+	(one-or-more
+	 (any ?_
+	      (?0 . ?9)
+	      (?A . ?Z)
+	      (?a . ?z)))))
+     1)
+    ("*Templates*"
+     ,(rx
+       line-start
+       (zero-or-more (syntax whitespace))
+       (zero-or-one
+	"mixin"
+	(one-or-more (syntax whitespace)))
+       word-start
+       "template"
+       (one-or-more (syntax whitespace))
+       (submatch
+	(one-or-more
+	 (any ?_
+	      (?0 . ?9)
+	      (?A . ?Z)
+	      (?a . ?z)))))
+     1)
+    ("*Enums*"
+     ,(rx
+       line-start
+       (zero-or-more (syntax whitespace))
+       word-start
+       "enum"
+       (one-or-more (syntax whitespace))
+       (submatch
+	(one-or-more
+	 (any ?_
+	      (?0 . ?9)
+	      (?A . ?Z)
+	      (?a . ?z))))
+       (zero-or-more (any " \t\n"))
+       (or ":" "{"))
+     1)
+    ;; NB: We can't easily distinguish aliases declared outside
+    ;; functions from local ones, so just search for those that are
+    ;; declared at the beginning of lines.
+    ("*Aliases*"
+     ,(rx
+       line-start
+       "alias"
+       (one-or-more (syntax whitespace))
+       (submatch
+	(one-or-more
+	 (any ?_
+	      (?0 . ?9)
+	      (?A . ?Z)
+	      (?a . ?z))))
+       (zero-or-more (syntax whitespace))
+       "=")
+     1)
+    ("*Aliases*"
+     ,(rx
+       line-start
+       "alias"
+       (one-or-more (syntax whitespace))
+       (one-or-more
+	(not (any ";")))
+       (one-or-more (syntax whitespace))
+       (submatch
+	(one-or-more
+	 (any ?_
+	      (?0 . ?9)
+	      (?A . ?Z)
+	      (?a . ?z))))
+       (zero-or-more (syntax whitespace))
+       ";"
+       (zero-or-more (syntax whitespace))
+       (or
+	eol
+	"//"
+	"/*")
+       )
+     1)
     (nil d-imenu-method-index-function 2)))
 
 ;;----------------------------------------------------------------------------
@@ -502,7 +706,7 @@
   (advice-add 'c-add-stmt-syntax :around #'d-around--c-add-stmt-syntax))
 
 ;;----------------------------------------------------------------------------
-(add-to-list 'auto-mode-alist '("\\.d[i]?\\'" . d-mode))
+;;;###autoload (add-to-list 'auto-mode-alist '("\\.d[i]?\\'" . d-mode))
 
 ;; Custom variables
 ;;;###autoload
@@ -531,23 +735,41 @@
         abbrev-mode t)
   (use-local-map d-mode-map)
   (c-init-language-vars d-mode)
-  (c-common-init 'd-mode)
-  (easy-menu-add d-menu)
-  (c-run-mode-hooks 'c-mode-common-hook 'd-mode-hook)
-  (c-update-modeline)
-  (cc-imenu-init d-imenu-generic-expression)
+  (when (fboundp 'c-make-noise-macro-regexps)
+    (c-make-noise-macro-regexps))
+
   ;; Generate a function that applies D-specific syntax properties.
   ;; Concretely, inside back-quoted string literals the backslash
   ;; character '\' is treated as a punctuation symbol.  See help for
   ;; syntax-propertize-rules function for more information.
   (when (version<= "24.3" emacs-version)
-    (setq-local syntax-propertize-function
-                (syntax-propertize-rules ("`[^\\\\`]*?\\(\\(\\\\\\)[^\\\\`]*?\\)+?`" (2 "."))))))
+    (setq-local
+     syntax-propertize-function
+     (syntax-propertize-rules
+      ((rx
+	"`"
+	(minimal-match
+	 (zero-or-more
+	  (not (any "`\\"))))
+	(minimal-match
+	 (one-or-more
+	  (submatch "\\")
+	  (minimal-match
+	   (zero-or-more
+	    (not (any "`\\"))))))
+	"`")
+       (1 ".")))))
+
+  (c-common-init 'd-mode)
+  (easy-menu-add d-menu)
+  (c-run-mode-hooks 'c-mode-common-hook 'd-mode-hook)
+  (c-update-modeline)
+  (cc-imenu-init d-imenu-generic-expression))
 
 ;;----------------------------------------------------------------------------
 ;; "Hideous hacks" to support appropriate font-lock behaviour.
 ;;
-;; * auto/immutable: If we leave them in c-modifier-kwds (like
+;; * auto/const/immutable: If we leave them in c-modifier-kwds (like
 ;;   c++-mode) then in the form "auto var;" var will be highlighted in
 ;;   type name face. Moving auto/immutable to font-lock-add-keywords
 ;;   lets cc-mode seeing them as a type name, so the next symbol can
@@ -580,7 +802,7 @@
 (defun d-match-fun-decl (limit)
   (d-try-match-decl d-fun-decl-pattern))
 (defun d-match-auto (limit)
-  (c-syntactic-re-search-forward "\\<\\(auto\\|immutable\\)\\>" limit t))
+  (c-syntactic-re-search-forward "\\<\\(auto\\|const\\|immutable\\)\\>" limit t))
 
 (font-lock-add-keywords
  'd-mode
@@ -599,10 +821,12 @@
 ;; StackOverflow, and then amended by Nordlöw (https://stackoverflow.com/users/683710/nordl%C3%B6w) it
 ;; provides a function that people can make use of in their d-mode-hook thus:
 ;;
-;; (add-hook 'd-mode-hook
-;;                  '(lambda ()
-;;                     (add-to-list 'c-offsets-alist '(arglist-cont-nonempty . d-lineup-cascaded-calls))
-;;                     (add-to-list 'c-offsets-alist '(statement-cont . d-lineup-cascaded-calls))))
+;; (add-hook 'd-mode-hook 'd-setup-cascaded-call-indentation)
+
+(defun d-setup-cascaded-call-indentation ()
+  "Set up `d-lineup-cascaded-calls'."
+  (add-to-list 'c-offsets-alist '(arglist-cont-nonempty . d-lineup-cascaded-calls))
+  (add-to-list 'c-offsets-alist '(statement-cont . d-lineup-cascaded-calls)))
 
 (defun d-lineup-cascaded-calls (langelem)
   "This is a modified `c-lineup-cascaded-calls' function for the