branch: elpa/haskell-tng-mode
commit 5536d23fba8389c9a02e188d3e3325f6745c048e
Author: Tseen She <ts33n....@gmail.com>
Commit: Tseen She <ts33n....@gmail.com>

    all font locks use the new macro
---
 haskell-tng-font-lock.el | 269 ++++++++++++++++-------------------------------
 haskell-tng-mode.el      |  10 +-
 haskell-tng-util.el      |  35 ++++++
 3 files changed, 127 insertions(+), 187 deletions(-)

diff --git a/haskell-tng-font-lock.el b/haskell-tng-font-lock.el
index cd3b34f..47da66f 100644
--- a/haskell-tng-font-lock.el
+++ b/haskell-tng-font-lock.el
@@ -19,16 +19,9 @@
 ;;; Code:
 
 ;; TODO: regression tests https://github.com/Lindydancer/faceup
-;;
-;; TODO: pragmas
-;;
-;; TODO: numeric / char primitives?
-;;
-;; TODO: haddock, different face vs line comments, and some markup.
-;;
 ;; TODO use levels so users can turn off type fontification
 
-(require 'subr-x)
+(require 'haskell-tng-util)
 
 (defgroup haskell-tng:faces nil
   "Haskell font faces."
@@ -61,15 +54,15 @@
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Here are `rx' patterns that are reused as a very simple form of BNF grammar
-(defconst haskell-tng:conid '(: upper (* wordchar)))
-(defconst haskell-tng:qual `(: (+ (: ,haskell-tng:conid (char ?.)))))
-(defconst haskell-tng:consym '(: ":" (+ (syntax symbol)))) ;; TODO exclude ::, 
limited symbol set
-(defconst haskell-tng:toplevel
+(defconst haskell-tng:rx:conid '(: upper (* wordchar)))
+(defconst haskell-tng:rx:qual `(: (+ (: ,haskell-tng:rx:conid (char ?.)))))
+(defconst haskell-tng:rx:consym '(: ":" (+ (syntax symbol)))) ;; TODO exclude 
::, limited symbol set
+(defconst haskell-tng:rx:toplevel
   `(: line-start (group (| (: (any lower ?_) (* wordchar))
                            (: "(" (+? (syntax symbol)) ")")))
       symbol-end))
 ;; note that \n has syntax `comment-end'
-(defconst haskell-tng:newline
+(defconst haskell-tng:rx:newline
   '(| (syntax comment-end)
       (: symbol-start
          "--"
@@ -84,10 +77,10 @@
  ;; These regexps use the `rx' library so we can reuse common subpatterns. It
  ;; also increases the readability of the code and, in many cases, allows us to
  ;; do more work in a single regexp instead of multiple passes.
- (let ((conid haskell-tng:conid)
-       (qual haskell-tng:qual)
-       (consym haskell-tng:consym)
-       (toplevel haskell-tng:toplevel))
+ (let ((conid haskell-tng:rx:conid)
+       (qual haskell-tng:rx:qual)
+       (consym haskell-tng:rx:consym)
+       (toplevel haskell-tng:rx:toplevel))
    `(;; reservedid / reservedop
      (,(rx-to-string
         '(|
@@ -104,26 +97,21 @@
       . 'haskell-tng:keyword)
 
      ;; Types
-     (haskell-tng:explicit-type-keyword
+     (haskell-tng:font:explicit-type:keyword
       (1 'haskell-tng:type keep))
-     (haskell-tng:topdecl
+     (haskell-tng:font:topdecl:keyword
       (1 'haskell-tng:type keep))
-     (haskell-tng:type
+     (haskell-tng:font:type:keyword
       (1 'haskell-tng:type keep))
-     (haskell-tng:deriving
+     (haskell-tng:font:deriving:keyword
       (1 'haskell-tng:keyword keep)
       (2 'haskell-tng:type keep))
 
-     ;; TODO types in import / export statements
-     ;; TODO ExplicitNamespaces to disambiguate TypeOperators
-
-     ;; TypeApplications (very conservative)
+     ;; TypeApplications
      (,(rx-to-string `(: symbol-start "@" (* space)
                          (group (opt ,qual) (| ,conid ,consym))))
       (1 'haskell-tng:type))
 
-     ;; TODO: multiline module / import sections
-
      ;; modules
      ;; (,(rx-to-string `(: symbol-start "module" symbol-end (+ space)
      ;;                     symbol-start (group (opt ,qual) ,conid) 
symbol-end))
@@ -147,6 +135,10 @@
      ;;  (,(rx-to-string `(: symbol-start (group (| ,conid ,consym)) 
symbol-end))
      ;;   nil nil (1 'haskell-tng:type)))
 
+     ;; TODO: pragmas
+     ;; TODO: numeric / char primitives?
+     ;; TODO: haddock, different face vs line comments, and some markup.
+
      ;; top-level
      (,(rx-to-string toplevel)
       . 'haskell-tng:toplevel)
@@ -162,66 +154,28 @@
      )))
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Here are `function' matchers for use in `font-lock-keywords', and reusable 
in
-;; the `font-lock-extend-region-functions' below. These set the match region 
and
-;; return nil if there is not match in the limited search.
+;; Here are `function' matchers for use in `font-lock-keywords' and
+;; `font-lock-extend-region-functions' procedures for extending the region.
 ;;
 ;; For these more complicated structures, the general rule is to find "negative
 ;; space" rather than to detect valid entries. Language extensions almost 
always
 ;; scupper any plan, e.g. TypeOperators and type literals.
-
-(defun haskell-tng:topdecl (limit)
-  "Matches the left hand side of a data, newtype, class or instance in group 
1."
-  (re-search-forward
-   (rx
-    line-start (| "data" "newtype" "class" "instance") symbol-end
-    (group (+? anything))
-    (|
-     (: line-start symbol-start)
-     (: symbol-start (| "where" "=") symbol-end)))
-   limit t))
-
-(defun haskell-tng:type (limit)
-  "Matches types in group 1."
-  (when (re-search-forward
-         (rx line-start "type" symbol-end)
-         limit t)
-    (goto-char (match-beginning 0))
-    (let ((indent (haskell-tng:indent-close)))
-      (re-search-forward
-       (rx line-start "type" symbol-end
-           (+ space) (group (+ anything)))
-       (min limit (or indent limit))))))
-
-(defun haskell-tng:deriving (limit)
-  "Matches a deriving section putting keywords in group 1, types in group 2."
-  ;; DeriveAnyClass
-  ;; DerivingStrategies
-  ;; GeneralizedNewtypeDeriving
-  ;; TODO DerivingVia
-  ;; TODO StandaloneDeriving
-  (when (re-search-forward
-         (rx symbol-start "deriving" symbol-end)
-         limit t)
-    (goto-char (match-beginning 0))
-    (let ((indent (haskell-tng:indent-close)))
-      (re-search-forward
-       (rx
-        symbol-start "deriving" (+ space)
-        (group (opt (| "anyclass" "stock" "newtype"))) (* space)
-        ?\( (group (* anything)) ?\))
-       (min limit (or indent limit)) t))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Here are `font-lock-extend-region-functions' procedures for extending the
-;; region. Note that because we are using `font-lock-multiline' then multiline
-;; patterns will always be rehighlighted as a group.
+;;
+;; Note that because we are using `font-lock-multiline', multiline patterns 
will
+;; always be re-highlighted as a group.
 (eval-when-compile
   ;; NOTE: font-lock-end is non-inclusive.
   (defvar font-lock-beg)
   (defvar font-lock-end))
 
-(defmacro haskell-tng:multiline (prefix trigger find)
+(defcustom haskell-tng:font:debug-extend nil
+  "Print debugging when the font-lock region is extended."
+  :type 'boolean)
+
+;; TODO (perf) don't call FIND or extend if there is a multiline property
+;; TODO simplify FIND to use paren-close / indent-close automatically?
+;; TODO option to avoid the initial regexp in -keyword if it overlaps
+(defmacro haskell-tng:font:multiline (name trigger find)
   "Defines `font-lock-keywords' / `font-lock-extend-region-functions' entries.
 
 TRIGGER is a referentially transparent form that produces a regexp.
@@ -230,6 +184,9 @@ FIND is a form that must behave the same as 
`re-search-forward',
 i.e. setting the match groups and placing point after the match.
 The variable `limit' is dynamically bound within this form.
 
+Both TRIGGER and FIND should be optimised as they will be called
+repeatedly as the user is entering text and navigating the code.
+
 The generated `haskell-tng:PREFIX-extend' uses searches
 backwards from the end of the proposed region with TRIGGER. If a
 match is found, then FIND is evaluated with an unlimited limit to
@@ -237,125 +194,75 @@ calculate the end position, which may extend the region.
 
 The generated `haskell-tng:PREFIX-keyword' searches forward for
 TRIGGER within the fontification limit. The point is reset to the
-beginning of the TRIGGER's match and FIND is evaluated.
-
-`font-lock-multiline' ensures that the full match is painted with
-the multiline property and should not not require further
-expansion.
-
-Use `pp-macroexpand-expression' to debug."
-  ;; TODO (perf) don't call FIND or extend if there is a multiline property
-  ;; TODO simplify FIND to use paren-close / indent-close automatically?
-  (let* ((name (symbol-name prefix))
-         (regexp (intern (concat name "-regexp")))
-         (match (intern (concat name "-keyword")))
-         (extend (intern (concat name "-extend"))))
+beginning of the TRIGGER's match and FIND is evaluated."
+  (declare (indent defun))
+  (let* ((sname (concat "haskell-tng:font:" (symbol-name name)))
+         (regexp (intern (concat sname ":trigger")))
+         (keyword (intern (concat sname ":keyword")))
+         (extend (intern (concat sname ":extend"))))
     `(progn
        (defconst ,regexp ,trigger)
-       (defun ,match (limit)
+       (defun ,keyword (limit)
          (when (re-search-forward ,regexp limit t)
            (goto-char (match-beginning 0))
            ,find))
        (defun ,extend ()
          (goto-char font-lock-end)
          (when (re-search-backward ,regexp font-lock-beg t)
-           (goto-char (match-beginning 0)) ;; is this needed?
            (let ((limit (point-max))) ,find)
            (when (< font-lock-end (point))
-             ;;(haskell-tng:debug-extend (point))
+             (when haskell-tng:font:debug-extend
+               (haskell-tng:font:debug-extend (point)))
              (setq font-lock-end (point))
              nil))))))
 
-(pp-macroexpand-expression
- '(haskell-tng:multiline
-   haskell-tng:explicit-type
-   (rx symbol-start "::" symbol-end)
-   (let ((paren (haskell-tng:paren-close))
-         (indent (haskell-tng:indent-close (- (point) 1))))
-     (re-search-forward
-      (rx symbol-start "::" symbol-end (group (+ anything)))
-      (min limit (or paren limit) (or indent limit)) t))))
-
-(haskell-tng:multiline
- haskell-tng:explicit-type
- (rx symbol-start "::" symbol-end)
- (let ((paren (haskell-tng:paren-close))
-       (indent (haskell-tng:indent-close (- (point) 1))))
-   (re-search-forward
-    (rx symbol-start "::" symbol-end (group (+ anything)))
-    (min limit (or paren limit) (or indent limit)) t)))
-
-(defun haskell-tng:extend-topdecl ()
-  "Multiline data, newtype, class and instance top level definitions."
-  (goto-char font-lock-end)
-  (when (re-search-backward
-         (rx line-start (| "data" "newtype" "class" "instance") symbol-end)
-         font-lock-beg t)
-    (goto-char (match-beginning 0))
-    (haskell-tng:topdecl (point-max))
-    (haskell-tng:extend)))
-
-(defun haskell-tng:extend-type ()
-  "Multiline type top-level definitions."
-  (goto-char font-lock-end)
-  (when (re-search-backward
-         (rx line-start "type" symbol-end)
-         font-lock-beg t)
-    (goto-char (match-beginning 0))
-    (haskell-tng:type (point-max))
-    (haskell-tng:extend)))
-
-(defun haskell-tng:extend-deriving ()
-  "Multiline deriving definitions."
-  (goto-char font-lock-end)
-  (when (re-search-backward
-         (rx symbol-start "deriving" symbol-end)
-         font-lock-beg t)
-    (goto-char (match-beginning 0))
-    (haskell-tng:deriving (point-max))
-    (haskell-tng:extend)))
-
-(defun haskell-tng:extend-module ()
-  "For use in `font-lock-extend-region-functions'.
-Ensures that multiline module definitions are opened."
-  nil)
-
-(defun haskell-tng:extend-import ()
-  "For use in `font-lock-extend-region-functions'.
-Ensures that multiline import definitions are opened."
-  nil)
+(haskell-tng:font:multiline explicit-type
+  (rx symbol-start "::" symbol-end)
+  (let ((paren (haskell-tng:paren-close))
+        (indent (haskell-tng:indent-close (- (point) 1))))
+    (re-search-forward
+     (rx symbol-start "::" symbol-end (group (+ anything)))
+     (min limit (or paren limit) (or indent limit)) t)))
+
+(haskell-tng:font:multiline topdecl
+  (rx line-start (| "data" "newtype" "class" "instance") symbol-end)
+  (re-search-forward
+   (rx line-start (| "data" "newtype" "class" "instance") symbol-end
+       (group (+? anything))
+       (| (: line-start symbol-start)
+          (: symbol-start (| "where" "=") symbol-end)))
+   limit t))
+
+(haskell-tng:font:multiline type
+  (rx line-start "type" symbol-end)
+  (let ((indent (haskell-tng:indent-close)))
+    (re-search-forward
+     (rx line-start "type" symbol-end (+ space) (group (+ anything)))
+     (min limit (or indent limit)))))
+
+(haskell-tng:font:multiline deriving
+  (rx symbol-start "deriving" symbol-end)
+  ;; DeriveAnyClass
+  ;; DerivingStrategies
+  ;; GeneralizedNewtypeDeriving
+  ;; TODO DerivingVia
+  ;; TODO StandaloneDeriving
+  (let ((indent (haskell-tng:indent-close)))
+    (re-search-forward
+     (rx
+      symbol-start "deriving" (+ space)
+      (group (opt (| "anyclass" "stock" "newtype"))) (* space)
+      ?\( (group (* anything)) ?\))
+     (min limit (or indent limit)) t)))
+
+;; TODO modules
+;; TODO imports
+;; TODO ExplicitNamespaces
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Helpers
-(defun haskell-tng:paren-close (&optional pos)
-  "The next `)', if it closes `POS's paren depth."
-  (save-excursion
-    (goto-char (or pos (point)))
-    (when-let (close (ignore-errors (scan-lists (point) 1 1)))
-      (goto-char (- close 1))
-      (when (looking-at ")")
-        (point)))))
-
-(defun haskell-tng:indent-close (&optional pos)
-  "The beginning of the line with indentation that closes `POS'."
-  (save-excursion
-    (goto-char (or pos (point)))
-    (let ((level (current-column)))
-      (catch 'closed
-        (while (and (forward-line) (not (eobp)))
-          (when (<= (current-indentation) level)
-            (throw 'closed (point))))
-        nil))))
-
-;; TODO: should these be in the macro?
-(defun haskell-tng:extend ()
-  "Extend the `font-lock-end' if point is further ahead."
-  (when (< font-lock-end (point))
-    ;;(haskell-tng:debug-extend (point))
-    (setq font-lock-end (point))
-    nil))
-
-(defun haskell-tng:debug-extend (to)
+
+(defun haskell-tng:font:debug-extend (to)
   (message "extending `%s' to include `%s'!"
            (buffer-substring-no-properties font-lock-beg font-lock-end)
            (if (<= to font-lock-beg)
diff --git a/haskell-tng-mode.el b/haskell-tng-mode.el
index fa780cc..9fbdec8 100644
--- a/haskell-tng-mode.el
+++ b/haskell-tng-mode.el
@@ -50,12 +50,10 @@
    font-lock-defaults '(haskell-tng:keywords)
    font-lock-multiline t
    font-lock-extend-region-functions '(font-lock-extend-region-wholelines
-                                       haskell-tng:explicit-type-extend
-                                       haskell-tng:extend-topdecl
-                                       haskell-tng:extend-type
-                                       haskell-tng:extend-deriving
-                                       haskell-tng:extend-module
-                                       haskell-tng:extend-import)
+                                       haskell-tng:font:explicit-type:extend
+                                       haskell-tng:font:topdecl:extend
+                                       haskell-tng:font:type:extend
+                                       haskell-tng:font:deriving:extend)
 
    ;; whitespace is meaningful, no electric indentation
    electric-indent-inhibit t)
diff --git a/haskell-tng-util.el b/haskell-tng-util.el
new file mode 100644
index 0000000..3a19d3f
--- /dev/null
+++ b/haskell-tng-util.el
@@ -0,0 +1,35 @@
+;;; haskell-tng-util.el --- Helpful Utilities -*- lexical-binding: t -*-
+
+;; Copyright (C) 2018 Tseen She
+;; License: GPL 3 or any later version
+
+;;; Commentary:
+;;
+;; Useful common utilities.
+;;
+;;; Code:
+
+(require 'subr-x)
+
+(defun haskell-tng:paren-close (&optional pos)
+  "The next `)', if it closes `POS's paren depth."
+  (save-excursion
+    (goto-char (or pos (point)))
+    (when-let (close (ignore-errors (scan-lists (point) 1 1)))
+      (goto-char (- close 1))
+      (when (looking-at ")")
+        (point)))))
+
+(defun haskell-tng:indent-close (&optional pos)
+  "The beginning of the line with indentation that closes `POS'."
+  (save-excursion
+    (goto-char (or pos (point)))
+    (let ((level (current-column)))
+      (catch 'closed
+        (while (and (forward-line) (not (eobp)))
+          (when (<= (current-indentation) level)
+            (throw 'closed (point))))
+        nil))))
+
+(provide 'haskell-tng-util)
+;;; haskell-tng-util.el ends here

Reply via email to