changeset 142:e610c7820306

add d-mode
author Jordi Gutiérrez Hermoso <jordigh@octave.org>
date Mon, 02 May 2016 11:52:09 -0400
parents ee4b6191b61e
children 107cbc75c936
files packages/d-mode.el
diffstat 1 files changed, 663 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/d-mode.el
@@ -0,0 +1,663 @@
+;;; d-mode.el --- D Programming Language major mode for (X)Emacs
+;;;               Requires a cc-mode of version 5.30 or greater
+
+;; Author:  William Baxter
+;; Contributors:  Andrei Alexandrescu
+;; Contributors:  Russel Winder
+;; Maintainer:  Russel Winder <russel@winder.org.uk>
+;; Created:  March 2007
+;; Version:  201512060745
+;; Keywords:  D programming language emacs cc-mode
+
+;;;; NB Version number is date and time yyyymmddhhMM in GMT (aka UTC).
+
+;; 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.
+
+;;; Usage:
+;; Put these lines in your init file.
+;;   (autoload 'd-mode "d-mode" "Major mode for editing D code." t)
+;;   (add-to-list 'auto-mode-alist '("\\.d[i]?\\'" . d-mode))
+;;
+;; cc-mode version 5.30 or greater is required.
+;; You can check your cc-mode with the command M-x c-version.
+;; You can get the latest version of cc-mode at http://cc-mode.sourceforge.net
+
+;;; Commentary:
+;;   This mode supports most of D's syntax, including nested /+ +/
+;;   comments and backquote `string literals`.
+;;
+;;   This mode has been dubbed "2.0" because it is a complete rewrite
+;;   from scratch.  The previous d-mode was based on cc-mode 5.28 or
+;;   so.  This version is based on the cc-mode 5.30 derived mode
+;;   example by Martin Stjernholm, 2002.
+
+;;; Bugs:
+;; Bug tracking is currently handled using the GitHub issue tracker at
+;; 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.
+
+;;; Notes:
+
+;;; TODO:
+;;   Issues with this code are managed via the project issue management
+;;   on GitHub: https://github.com/Emacs-D-Mode-Maintainers/Emacs-D-Mode/issues?state=open
+
+;;; History:
+;;   History is tracked in the Git repository rather than in this file.
+;;   See https://github.com/Emacs-D-Mode-Maintainers/Emacs-D-Mode/commits/master
+
+;;----------------------------------------------------------------------------
+;;; Code:
+
+(require 'cc-mode)
+
+;; Needed to prevent
+;;   "Symbol's value as variable is void: compilation-error-regexp-alist-alist" errors
+(require 'compile)
+
+;; The set-difference function is used from the Common Lisp extensions.
+(require 'cl)
+
+;; 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
+;; (eval-after-load "font-lock" ...) but then some trickery is
+;; necessary to get them compiled.)
+;; Comment out 'when-compile part for debugging
+(eval-when-compile
+  (require 'cc-langs)
+  (require 'cc-fonts)
+)
+
+(eval-and-compile
+  ;; Make our mode known to the language constant system.  Use Java
+  ;; mode as the fallback for the constants we don't change here.
+  ;; This needs to be done also at compile time since the language
+  ;; 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 "\\("
+		   "[*\(]"
+		   "\\|"
+		   (c-lang-const c-type-decl-prefix-key)
+		   "\\)"
+		   "\\([^=]\\|$\\)"))
+
+;; D has fixed arrays
+(c-lang-defconst c-opt-type-suffix-key
+  d "\\(\\[[^]]*\\]\\|\\.\\.\\.\\)")
+
+(c-lang-defconst c-identifier-ops
+  ;; For recognizing "~this", ".foo", and "foo.bar.baz" as identifiers
+  d '((prefix "~")(prefix ".")(left-assoc ".")))
+
+(c-lang-defconst c-after-id-concat-ops
+  ;; Also for handling ~this
+  d '("~"))
+
+(c-lang-defconst c-string-escaped-newlines
+  ;; Set to true to indicate the D handles backslash escaped newlines in strings
+  d t)
+
+(c-lang-defconst c-multiline-string-start-char
+  ;; Set to true to indicate that D doesn't mind raw embedded newlines in strings
+  d t)
+
+(c-lang-defconst c-opt-cpp-prefix
+  ;; Preprocessor directive recognizer.  D doesn't have cpp, but it has #line
+  d "\\s *#\\s *")
+
+(c-lang-defconst c-cpp-message-directives d nil)
+(c-lang-defconst c-cpp-include-directives d nil)
+(c-lang-defconst c-opt-cpp-macro-define d nil)
+(c-lang-defconst c-cpp-expr-directives d nil)
+(c-lang-defconst c-cpp-expr-functions d nil)
+
+(c-lang-defconst c-assignment-operators
+  ;; List of all assignment operators.
+  d  '("=" "*=" "/=" "%=" "+=" "-=" ">>=" "<<=" ">>>=" "&=" "^=" "^^="
+       "|=" "~="))
+
+(c-lang-defconst c-other-op-syntax-tokens
+  "List of the tokens made up of characters in the punctuation or
+parenthesis syntax classes that have uses other than as expression
+operators."
+ d (append '("/+" "+/" "..." ".." "!" "*" "&")
+	    (c-lang-const c-other-op-syntax-tokens)))
+
+(c-lang-defconst c-block-comment-starter d "/*")
+(c-lang-defconst c-block-comment-ender   d "*/")
+
+(c-lang-defconst c-comment-start-regexp  d "/[*+/]")
+(c-lang-defconst c-block-comment-start-regexp d "/[*+]")
+(c-lang-defconst c-literal-start-regexp
+  ;; Regexp to match the start of comments and string literals.
+  d "/[*+/]\\|\"\\|`")
+
+(c-lang-defconst c-block-prefix-disallowed-chars
+  ;; Allow ':' for inherit list starters.
+  d (set-difference (c-lang-const c-block-prefix-disallowed-chars)
+                    '(?:)))
+
+(defconst doxygen-font-lock-doc-comments
+  (let ((symbol "[a-zA-Z0-9_]+")
+	(header "^ \\* "))
+    `((,(concat header "\\("     symbol "\\):[ \t]*$")
+       1 ,c-doc-markup-face-name prepend nil)
+      (,(concat                  symbol     "()")
+       0 ,c-doc-markup-face-name prepend nil)
+      (,(concat header "\\(" "@" symbol "\\):")
+       1 ,c-doc-markup-face-name prepend nil)
+      (,(concat "[#%@]" symbol)
+       0 ,c-doc-markup-face-name prepend nil))
+    ))
+
+(defconst doxygen-font-lock-keywords
+  `((,(lambda (limit)
+	(c-font-lock-doc-comments "/\\+[+!]\\|/\\*[*!]\\|//[/!]" limit
+	  doxygen-font-lock-doc-comments)))))
+
+
+;;; 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.
+
+;; 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)
+
+;; 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]+\\)):"
+               1 2 nil 2))
+(add-to-list 'compilation-error-regexp-alist 'd-exceptions)
+
+;;----------------------------------------------------------------------------
+
+;; Built-in basic types
+(c-lang-defconst c-primitive-type-kwds
+  d '("bit" "bool" "byte" "ubyte" "char" "delegate" "double" "float"
+      "function" "int" "long" "short" "uint" "ulong" "ushort"
+      "cent" "ucent" "real" "ireal" "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"
+      "final" "in" "out" "inout" "lazy" "mixin" "override" "private"
+      "protected" "public" "ref" "scope" "shared" "static" "synchronized"
+      "volatile" "__vector"))
+
+(c-lang-defconst c-class-decl-kwds
+  ;; Keywords introducing declarations where the following block (if any)
+  ;; contains another declaration level that should be considered a class.
+  d '("class" "struct" "union" "interface" "template"))
+
+;; (c-lang-defconst c-brace-list-decl-kwds
+;;   d '("enum"))
+
+(c-lang-defconst c-type-modifier-kwds
+  d '("__gshared" "const" "inout" "lazy" "shared" "volatile"
+      "invariant" "enum" "__vector"))
+
+(c-lang-defconst c-type-prefix-kwds
+  ;; Keywords where the following name - if any - is a type name, and
+  ;; where the keyword together with the symbol works as a type in
+  ;; declarations.  In this case, like "mixin foo!(x) bar;"
+  d    '("mixin" "align"))
+
+;;(c-lang-defconst c-other-block-decl-kwds
+;;  ;; Keywords where the following block (if any) contains another
+;;  ;; declaration level that should not be considered a class.
+;;  ;; Each of these has associated offsets e.g.
+;;  ;;   'with-open', 'with-close' and 'inwith'
+;;  ;; that can be customized individually
+;;  ;;   TODO: maybe also do this for 'static if' ?  in/out?
+;;  ;;   TODO: figure out how to make this work properly
+;;  d '("with" "version" "extern"))
+
+(c-lang-defconst c-typedef-decl-kwds
+ d (append (c-lang-const c-typedef-decl-kwds)
+	    '("typedef" "alias")))
+
+(c-lang-defconst c-decl-hangon-kwds
+  d '("export"))
+
+(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"
+      "private" "package" "protected" "public" "export"))
+
+;;(c-lang-defconst c-postfix-decl-spec-kwds
+;;  ;Keywords introducing extra declaration specifiers in the region
+;;  ;between the header and the body (i.e. the "K&R-region") in
+;;  ;declarations.
+;;; This doesn't seem to have any effect.  They aren't exactly "K&R-regions".
+;;  d '("in" "out" "body"))
+
+(c-lang-defconst c-type-list-kwds
+  d '("import"))
+
+(c-lang-defconst c-ref-list-kwds
+  d '("module"))
+
+(c-lang-defconst c-colon-type-list-kwds
+  ;; Keywords that may be followed (not necessarily directly) by a colon
+  ;; and then a comma separated list of type identifiers.
+  d  '("class" "enum" "interface"))
+
+(c-lang-defconst c-paren-nontype-kwds
+  ;;Keywords that may be followed by a parenthesis expression that doesn't
+  ;; contain type identifiers.
+  d '("version" "debug" "extern" "macro" "mixin"))
+
+(c-lang-defconst c-paren-type-kwds
+  ;; Keywords that may be followed by a parenthesis expression containing
+  ;; type identifiers separated by arbitrary tokens.
+  d  '("delete" "throw"))
+
+(c-lang-defconst c-block-stmt-1-kwds
+  ;; Statement keywords followed directly by a substatement.
+  d '("do" "else" "finally" "try" "in" "out" "body"))
+
+(c-lang-defconst c-block-stmt-2-kwds
+  ;; Statement keywords followed by a paren sexp and then by a substatement.
+  d '("for" "if" "switch" "while" "catch" "synchronized" "scope"
+      "foreach" "foreach_reverse" "with" "unittest"))
+
+(c-lang-defconst c-simple-stmt-kwds
+  ;; Statement keywords followed by an expression or nothing.
+  d '("break" "continue" "goto" "return" "throw"))
+
+(c-lang-defconst c-paren-stmt-kwds
+  ;; Statement keywords followed by a parenthesis expression that
+  ;; nevertheless contains a list separated with ';' and not ','."
+  d '("for" "foreach" "foreach_reverse"))
+
+(c-lang-defconst c-asm-stmt-kwds
+  ;; Statement keywords followed by an assembler expression.
+  d '("asm"))
+
+(c-lang-defconst c-label-kwds
+  ;; Keywords introducing colon terminated labels in blocks.
+  d '("case" "default"))
+
+(c-lang-defconst c-before-label-kwds
+  ;; Keywords that might be followed by a label identifier.
+  d '("goto" "break" "continue"))
+
+(c-lang-defconst c-constant-kwds
+  ;; Keywords for constants.
+  d '("null" "true" "false"))
+
+(c-lang-defconst c-primary-expr-kwds
+  ;; Keywords besides constants and operators that start primary expressions.
+  d '("this" "super"))
+
+(c-lang-defconst c-inexpr-class-kwds
+  ;; Keywords that can start classes inside expressions.
+  d nil)
+
+(c-lang-defconst c-inexpr-brace-list-kwds
+  ;; Keywords that can start brace list blocks inside expressions.
+  d nil)
+
+(c-lang-defconst c-other-decl-kwds
+  d '("module" "import"))
+
+(c-lang-defconst c-other-kwds
+  ;; Keywords not accounted for by any other `*-kwds' language constant.
+  d '("__gshared" "__traits" "assert" "cast" "is" "nothrow" "pure" "ref"
+      "sizeof" "typeid" "typeof"))
+
+
+(defcustom d-font-lock-extra-types nil
+  "*List of extra types (aside from the type keywords) to recognize in D mode.
+   Each list item should be a regexp matching a single identifier."
+  :group 'd-mode)
+
+(defconst d-font-lock-keywords-1 (c-lang-const c-matchers-1 d)
+  "Minimal highlighting for D mode.")
+
+(defconst d-font-lock-keywords-2 (c-lang-const c-matchers-2 d)
+  "Fast normal highlighting for D mode.")
+
+(defconst d-font-lock-keywords-3 (c-lang-const c-matchers-3 d)
+  "Accurate normal highlighting for D mode.")
+
+(defvar d-font-lock-keywords d-font-lock-keywords-3
+  "Default expressions to highlight in D mode.")
+
+(defun d-font-lock-keywords-2 ()
+  (c-compose-keywords-list d-font-lock-keywords-2))
+(defun d-font-lock-keywords-3 ()
+  (c-compose-keywords-list d-font-lock-keywords-3))
+(defun d-font-lock-keywords ()
+  (c-compose-keywords-list d-font-lock-keywords))
+
+(defvar d-mode-syntax-table nil
+  "Syntax table used in d-mode buffers.")
+(or d-mode-syntax-table
+    (setq d-mode-syntax-table
+	 (let ((table (funcall (c-lang-const c-make-mode-syntax-table d))))
+	   ;; Make it recognize D `backquote strings`
+	   (modify-syntax-entry ?` "\"" table)
+
+	   ;; Make it recognize D's nested /+ +/ comments
+	   (modify-syntax-entry ?+  ". 23n"   table)
+	   table)))
+
+(defvar d-mode-abbrev-table nil
+  "Abbreviation table used in d-mode buffers.")
+(c-define-abbrev-table 'd-mode-abbrev-table
+  ;; Use the abbrevs table to trigger indentation actions
+  ;; on keywords that, if they occur first on a line, might alter the
+  ;; syntactic context.
+  ;; Syntax for abbrevs is:
+  ;; ( pattern replacement command initial-count)
+  '(("else" "else" c-electric-continued-statement 0)
+    ("while" "while" c-electric-continued-statement 0)
+    ("catch" "catch" c-electric-continued-statement 0)
+    ("finally" "finally" c-electric-continued-statement 0)))
+
+(defvar d-mode-map ()
+  "Keymap used in d-mode buffers.")
+(if d-mode-map
+    nil
+  (setq d-mode-map (c-make-inherited-keymap))
+  ;; Add bindings which are only useful for D
+  ;; (define-key d-mode-map "\C-c\C-e"  'd-cool-function)
+  )
+
+(c-lang-defconst c-mode-menu
+  ;; The definition for the mode menu.  The menu title is prepended to
+  ;; this before it's fed to `easy-menu-define'.
+  t `(["Comment Out Region"     comment-region
+       (c-fn-region-is-active-p)]
+      ["Uncomment Region"       (comment-region (region-beginning)
+						(region-end) '(4))
+       (c-fn-region-is-active-p)]
+      ["Indent Expression"      c-indent-exp
+       (memq (char-after) '(?\( ?\[ ?\{))]
+      ["Indent Line or Region"  c-indent-line-or-region t]
+      ["Fill Comment Paragraph" c-fill-paragraph t]
+      "----"
+      ["Backward Statement"     c-beginning-of-statement t]
+      ["Forward Statement"      c-end-of-statement t]
+      "----"
+      ("Toggle..."
+       ["Syntactic indentation" c-toggle-syntactic-indentation
+	:style toggle :selected c-syntactic-indentation]
+       ["Electric mode"         c-toggle-electric-state
+	:style toggle :selected c-electric-flag]
+       ["Auto newline"          c-toggle-auto-newline
+	:style toggle :selected c-auto-newline]
+       ["Hungry delete"         c-toggle-hungry-state
+	:style toggle :selected c-hungry-delete-key]
+       ["Subword mode"          c-subword-mode
+	:style toggle :selected (and (boundp 'c-subword-mode)
+                                     c-subword-mode)])))
+
+(easy-menu-define d-menu d-mode-map "D Mode Commands"
+  (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 '{'
+
+(defun d-imenu-method-index-function ()
+  (and
+   (let ((pt))
+     (setq pt (re-search-backward d-imenu-method-name-pattern nil t))
+     ;; The method name regexp will match lines like
+     ;; "return foo(x);" or "static if(x) {"
+     ;; so we exclude type name 'static' or 'return' here
+     (while (let ((type (match-string 1)))
+              (and pt type
+                   (save-match-data
+                     (string-match (c-lang-const c-regular-keywords-regexp) type))))
+       (setq pt (re-search-backward d-imenu-method-name-pattern nil t)))
+     pt)
+   ;; Do not count invisible definitions.
+   (let ((invis (invisible-p (point))))
+     (or (not invis)
+         (progn
+           (while (and invis
+                       (not (bobp)))
+             (setq invis (not (re-search-backward
+                               d-imenu-method-name-pattern nil 'move))))
+           (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)
+    (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 (orig-fun &rest args)
+  (let ((rxp (car args)))
+    (if (and (stringp rxp) (string= rxp "if\\>[^_]"))
+        (or (apply orig-fun '("static\\>\\s-+if\\>[^_]"))
+            (apply orig-fun '("version\\>[^_]"))
+            (apply orig-fun '("debug\\>[^_]"))
+            (apply orig-fun args))
+      (apply orig-fun args))))
+
+(defun d-around--c-add-stmt-syntax (orig-fun &rest args)
+  (if (not (string= major-mode "d-mode"))
+      (apply orig-fun args)
+    (progn
+      (add-function :around (symbol-function 'looking-at)
+                    #'d-special-case-looking-at)
+      (unwind-protect
+          (apply orig-fun args)
+        (remove-function (symbol-function 'looking-at)
+                         #'d-special-case-looking-at)))))
+
+(when (version<= "24.4" emacs-version)
+  (advice-add 'c-add-stmt-syntax :around #'d-around--c-add-stmt-syntax))
+
+;;----------------------------------------------------------------------------
+;;;###autoload (add-to-list 'auto-mode-alist '("\\.d[i]?\\'" . d-mode))
+
+;; Custom variables
+;;;###autoload
+(defcustom d-mode-hook nil
+  "*Hook called by `d-mode'."
+  :type 'hook
+  :group 'c)
+
+;; For compatibility with Emacs < 24
+(defalias 'd-parent-mode
+  (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
+
+;;;###autoload
+(define-derived-mode d-mode d-parent-mode "D"
+  "Major mode for editing code written in the D Programming Language.
+
+See http://dlang.org for more information about the D language.
+
+The hook `c-mode-common-hook' is run with no args at mode
+initialization, then `d-mode-hook'.
+
+Key bindings:
+\\{d-mode-map}"
+  (c-initialize-cc-mode t)
+  (setq local-abbrev-table d-mode-abbrev-table
+        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)
+  ;; 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 "."))))))
+
+;;----------------------------------------------------------------------------
+;; "Hideous hacks" to support appropriate font-lock behaviour.
+;;
+;; * auto/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
+;;   be fontified as a variable.
+;;
+;; * public/protected/private appear both in c-modifier-kwds and in
+;;   c-protection-kwds. This causes cc-mode to fail parsing the first
+;;   declaration after an access level label (because cc-mode trys to
+;;   parse them as modifier but will fail due to the colon). But
+;;   unfortunately we cannot remove them from either c-modifier-kwds
+;;   or c-protection-kwds. Removing them from the former causes valid
+;;   syntax like "private int foo() {}" to fail. Removing them from
+;;   the latter cause indentation of the access level labels to
+;;   fail. The solution used here is to use font-lock-add-keywords to
+;;   add back the syntax highlight.
+
+(defconst d-var-decl-pattern "^[ \t]*\\(?:[_a-zA-Z0-9]+[ \t\n]+\\)*\\([_a-zA-Z0-9.!]+\\)\\(?:\\[[^]]*\\]\\|\\*\\)?[ \t\n]+\\([_a-zA-Z0-9]+\\)[ \t\n]*[;=]")
+(defconst d-fun-decl-pattern "^[ \t]*\\(?:[_a-zA-Z0-9]+[ \t\n]+\\)*\\([_a-zA-Z0-9.!]+\\)\\(?:\\[[^]]*\\]\\|\\*\\)?[ \t\n]+\\([_a-zA-Z0-9]+\\)[ \t\n]*(")
+(defmacro d-try-match-decl (regex)
+  `(let ((pt))
+     (setq pt (re-search-forward ,regex limit t))
+     (while (let ((type (match-string 1)))
+              (and pt type
+                   (save-match-data
+                     (string-match (c-lang-const c-regular-keywords-regexp) type))))
+       (setq pt (re-search-forward ,regex limit t)))
+     pt))
+(defun d-match-var-decl (limit)
+  (d-try-match-decl d-var-decl-pattern))
+(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))
+
+(font-lock-add-keywords
+ 'd-mode
+ '((d-match-auto 1 font-lock-keyword-face t)
+   (d-match-var-decl (1 font-lock-type-face) (2 font-lock-variable-name-face))
+   (d-match-fun-decl (1 font-lock-type-face) (2 font-lock-function-name-face)))
+ t)
+
+;;----------------------------------------------------------------------------
+;;
+;; Support for "Adjusting Alignment Rules for UCFS-Chains in D",
+;; cf. https://stackoverflow.com/questions/25797945/adjusting-alignment-rules-for-ucfs-chains-in-d
+;;
+;; The code here was originally created by Sergei Nosov
+;; (https://stackoverflow.com/users/1969069/sergei-nosov) based on the c-lineup-cascaded-calls code, see
+;; 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))))
+
+(defun d-lineup-cascaded-calls (langelem)
+  "This is a modified `c-lineup-cascaded-calls' function for the
+D programming language which accounts for optional parenthesis
+and compile-time parameters in function calls."
+
+  (if (and (eq (c-langelem-sym langelem) 'arglist-cont-nonempty)
+           (not (eq (c-langelem-2nd-pos c-syntactic-element)
+                    (c-most-enclosing-brace (c-parse-state)))))
+      ;; The innermost open paren is not our one, so don't do
+      ;; anything. This can occur for arglist-cont-nonempty with
+      ;; nested arglist starts on the same line.
+      nil
+
+    (save-excursion
+      (back-to-indentation)
+      (let ((operator (and (looking-at "\\.")
+                           (regexp-quote (match-string 0))))
+            (stmt-start (c-langelem-pos langelem)) col)
+
+        (when (and operator
+                   (looking-at operator)
+                   (or (and
+                        (zerop (c-backward-token-2 1 t stmt-start))
+                        (eq (char-after) ?\()
+                        (zerop (c-backward-token-2 2 t stmt-start))
+                        (looking-at operator))
+                       (and
+                        (zerop (c-backward-token-2 1 t stmt-start))
+                        (looking-at operator))
+                       (and
+                        (zerop (c-backward-token-2 1 t stmt-start))
+                        (looking-at operator))
+                       )
+                   )
+          (setq col (current-column))
+
+          (while (or (and
+                      (zerop (c-backward-token-2 1 t stmt-start))
+                      (eq (char-after) ?\()
+                      (zerop (c-backward-token-2 2 t stmt-start))
+                      (looking-at operator))
+                     (and
+                      (zerop (c-backward-token-2 1 t stmt-start))
+                      (looking-at operator))
+                     (and
+                      (zerop (c-backward-token-2 1 t stmt-start))
+                      (looking-at operator))
+                     )
+            (setq col (current-column)))
+
+          (vector col))))))
+
+;;----------------------------------------------------------------------------
+
+(provide 'd-mode)
+
+;;; d-mode.el ends here