branch: elpa/lua-mode
commit 6245e505525aa183f55c6875fb5879ffe0732788
Author: immerrr <[email protected]>
Commit: immerrr <[email protected]>

    Add helper functions for reimplementation of font-lock-keywords
---
 lua-mode.el | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/lua-mode.el b/lua-mode.el
index 442c6cf..039e7f4 100644
--- a/lua-mode.el
+++ b/lua-mode.el
@@ -412,6 +412,97 @@ traceback location."
 This is a compilation of 5.1 and 5.2 builtins taken from the
 index of respective Lua reference manuals.")
 
+(defun lua-make-delimited-matcher (elt-regexp sep-regexp end-regexp)
+  "Construct matcher function for `font-lock-keywords' to match a sequence.
+
+It's supposed to match sequences with following EBNF:
+
+ELT-REGEXP { SEP-REGEXP ELT-REGEXP } END-REGEXP
+
+The sequence is parsed one token at a time.  If non-nil is
+returned, `match-data' will have one or more of the following
+groups set according to next matched token:
+
+1. matched element token
+2. unmatched garbage characters
+3. misplaced token (i.e. SEP-REGEXP when ELT-REGEXP is expected)
+4. matched separator token
+5. matched end token
+
+Blanks & comments between tokens are silently skipped.
+Groups 6-9 can be used in any of argument regexps."
+  (lexical-let*
+      ((delimited-matcher-re-template
+        
"\\=\\(?2:.*?\\)\\(?:\\(?%s:\\(?4:%s\\)\\|\\(?5:%s\\)\\)\\|\\(?%s:\\(?1:%s\\)\\)\\)")
+       ;; There's some magic to this regexp. It works as follows:
+       ;;
+       ;; A. start at (point)
+       ;; B. non-greedy match of garbage-characters (?2:)
+       ;; C. try matching separator (?4:) or end-token (?5:)
+       ;; D. try matching element (?1:)
+       ;;
+       ;; Simple, but there's a trick: pt.C and pt.D are embraced by one more
+       ;; group whose purpose is determined only after the template is
+       ;; formatted (?%s:):
+       ;;
+       ;; - if element is expected, then D's parent group becomes "shy" and C's
+       ;;   parent becomes group 3 (aka misplaced token), so if D matches when
+       ;;   an element is expected, it'll be marked with warning face.
+       ;;
+       ;; - if separator-or-end-token is expected, then it's the opposite:
+       ;;   C's parent becomes shy and D's will be matched as misplaced token.
+       (elt-expected-re (format delimited-matcher-re-template
+                                3 sep-regexp end-regexp "" elt-regexp))
+       (sep-or-end-expected-re (format delimited-matcher-re-template
+                                       "" sep-regexp end-regexp 3 elt-regexp)))
+
+    (lambda (end)
+      (let* ((prev-elt-p (match-beginning 1))
+             (prev-sep-p (match-beginning 4))
+             (prev-end-p (match-beginning 5))
+
+             (regexp (if prev-elt-p sep-or-end-expected-re elt-expected-re))
+             (comment-start (lua-comment-start-pos (syntax-ppss)))
+             (parse-stop end))
+
+        ;; If token starts inside comment, or end-token was encountered, stop.
+        (when (and (not comment-start)
+                   (not prev-end-p))
+          ;; Skip all comments & whitespace. forward-comment doesn't have 
boundary
+          ;; argument, so make sure point isn't beyond parse-stop afterwards.
+          (while (and (< (point) end)
+                      (forward-comment 1)))
+          (goto-char (min (point) parse-stop))
+
+          ;; Reuse comment-start variable to store beginning of comment that is
+          ;; placed before line-end-position so as to make sure token search 
doesn't
+          ;; enter that comment.
+          (setq comment-start
+                (lua-comment-start-pos
+                 (save-excursion
+                   (parse-partial-sexp (point) parse-stop
+                                       nil nil nil 'stop-inside-comment)))
+                parse-stop (or comment-start parse-stop))
+
+          ;; Now, let's match stuff.  If regular matcher fails, declare a span 
of
+          ;; non-blanks 'garbage', and the next iteration will start from 
where the
+          ;; garbage ends.  If couldn't match any garbage, move point to the 
end
+          ;; and return nil.
+          (or (re-search-forward regexp parse-stop t)
+              (re-search-forward "\\(?1:\\(?2:[^ \t]+\\)\\)" parse-stop 'skip)
+              (prog1 nil (goto-char end))))))))
+
+(defconst lua-local-defun-regexp
+  ;; Function matchers are very crude, need rewrite at some point.
+  (rx (or (seq (regexp "\\(?:\\_<function\\_>\\)")
+               (* blank)
+               (? (regexp "\\(?1:\\_<[[:alpha:]][[:alnum:]]*\\_>\\)"))
+               (group-n 2 (* nonl)))
+          (seq (? (regexp "\\(?1:\\_<[[:alpha:]][[:alnum:]]*\\_>\\)"))
+               (* blank) "=" (* blank)
+               (regexp "\\(?:\\_<function\\_>\\)")
+               (group-n 2 (* nonl))))))
+
 (defvar lua-font-lock-keywords
   (eval-when-compile
     (list
@@ -619,6 +710,12 @@ This function replaces previous prefix-key binding with a 
new one."
   "Returns true if the point is in a string."
   (save-excursion (elt (syntax-ppss pos) 3)))
 
+(defun lua-comment-start-pos (parsing-state)
+  "Return position of comment containing current point.
+
+If point is not inside a comment, return nil."
+  (and parsing-state (nth 4 parsing-state) (nth 8 parsing-state)))
+
 (defun lua-comment-p (&optional pos)
   "Returns true if the point is in a comment."
   (save-excursion (elt (syntax-ppss pos) 4)))

Reply via email to