Merge pull request #864 from Fanael/ninja-mode.el-refactor

Ninja mode.el refactor
diff --git a/misc/ninja-mode.el b/misc/ninja-mode.el
index ee5fe48..71825d5 100644
--- a/misc/ninja-mode.el
+++ b/misc/ninja-mode.el
@@ -1,4 +1,6 @@
-;;; ninja-mode.el --- Major mode for editing .ninja files
+;;; ninja-mode.el --- Major mode for editing .ninja files -*- lexical-binding: t -*-
+
+;; Package-Requires: ((emacs "24"))
 
 ;; Copyright 2011 Google Inc. All Rights Reserved.
 ;;
@@ -22,31 +24,58 @@
 ;;; Code:
 
 (defvar ninja-keywords
-      (list
-       '("^#.*" . font-lock-comment-face)
-       (cons (concat "^" (regexp-opt '("rule" "build" "subninja" "include"
-                                       "pool" "default")
-                                     'words))
-             font-lock-keyword-face)
-       '("\\([[:alnum:]_]+\\) =" . (1 font-lock-variable-name-face))
-       ;; Variable expansion.
-       '("\\($[[:alnum:]_]+\\)" . (1 font-lock-variable-name-face))
-       '("\\(${[[:alnum:]._]+}\\)" . (1 font-lock-variable-name-face))
-       ;; Rule names
-       '("rule +\\([[:alnum:]_.-]+\\)" . (1 font-lock-function-name-face))
-       ;; Build Statement - highlight the rule used,
-       ;; allow for escaped $,: in outputs.
-       '("build +\\(?:[^:$\n]\\|$[:$]\\)+ *: *\\([[:alnum:]_.-]+\\)" .
-         (1 font-lock-function-name-face))
-       ))
+  `((,(concat "^" (regexp-opt '("rule" "build" "subninja" "include"
+                                "pool" "default")
+                              'words))
+     . font-lock-keyword-face)
+    ("\\([[:alnum:]_]+\\) =" 1 font-lock-variable-name-face)
+    ;; Variable expansion.
+    ("$[[:alnum:]_]+" . font-lock-variable-name-face)
+    ("${[[:alnum:]._]+}" . font-lock-variable-name-face)
+    ;; Rule names
+    ("rule +\\([[:alnum:]_.-]+\\)" 1 font-lock-function-name-face)
+    ;; Build Statement - highlight the rule used,
+    ;; allow for escaped $,: in outputs.
+    ("build +\\(?:[^:$\n]\\|$[:$]\\)+ *: *\\([[:alnum:]_.-]+\\)"
+     1 font-lock-function-name-face)))
+
+(defvar ninja-mode-syntax-table
+  (let ((table (make-syntax-table)))
+    (modify-syntax-entry ?\" "." table)
+    table)
+  "Syntax table used in `ninja-mode'.")
+
+(defun ninja-syntax-propertize (start end)
+  (save-match-data
+    (save-excursion
+      (goto-char start)
+      (while (search-forward "#" end t)
+        (let ((match-pos (match-beginning 0)))
+          (when (and
+                 ;; Is it the first non-white character on the line?
+                 (eq match-pos (save-excursion (back-to-indentation) (point)))
+                 (save-excursion
+                   (goto-char (line-end-position 0))
+                   (or
+                    ;; If we're continuting the previous line, it's not a
+                    ;; comment.
+                    (not (eq ?$ (char-before)))
+                    ;; Except if the previous line is a comment as well, as the
+                    ;; continuation dollar is ignored then.
+                    (nth 4 (syntax-ppss)))))
+            (put-text-property match-pos (1+ match-pos) 'syntax-table '(11))
+            (let ((line-end (line-end-position)))
+              ;; Avoid putting properties past the end of the buffer.
+              ;; Otherwise we get an `args-out-of-range' error.
+              (unless (= line-end (1+ (buffer-size)))
+                (put-text-property line-end (1+ line-end) 'syntax-table '(12))))))))))
 
 ;;;###autoload
 (define-derived-mode ninja-mode prog-mode "ninja"
-  (setq comment-start "#")
-  ; Pass extra "t" to turn off syntax-based fontification -- we don't want
-  ; quoted strings highlighted.
-  (setq font-lock-defaults '(ninja-keywords t))
-  )
+  (set (make-local-variable 'comment-start) "#")
+  (set (make-local-variable 'parse-sexp-lookup-properties) t)
+  (set (make-local-variable 'syntax-propertize-function) #'ninja-syntax-propertize)
+  (setq font-lock-defaults '(ninja-keywords)))
 
 ;; Run ninja-mode for files ending in .ninja.
 ;;;###autoload