]> gitweb.factorcode.org Git - factor.git/commitdiff
FUEL: new indentation system based on smie
authorBjörn Lindqvist <bjourne@gmail.com>
Sat, 2 Jul 2016 01:24:46 +0000 (03:24 +0200)
committerBjörn Lindqvist <bjourne@gmail.com>
Sat, 2 Jul 2016 13:50:59 +0000 (15:50 +0200)
smie makes writing the indentation code much simpler. It also takes care
of some corner cases that led to bad indentation before like stack
effects split over multiple lines.

See: http://emacs.1067599.n5.nabble.com/SMIE-examples-or-guides-td400784.html

misc/fuel/factor-mode.el
misc/fuel/factor-smie.el [new file with mode: 0644]

index ecde6c1ae9a9f68bf9ce9f6b48b8c15a7a0745d0..95ec91bd0d5176d2aa21c18e3f54720dc6358d12 100644 (file)
@@ -21,6 +21,7 @@
 (require 'font-lock)
 (require 'ring)
 (require 'fuel-base)
+(require 'factor-smie)
 
 ;;; Customization:
 
@@ -40,12 +41,6 @@ source/docs/tests file. When set to false, you'll be asked only once."
   :type 'boolean
   :group 'factor)
 
-(defcustom factor-indent-level 4
-  "Indentation of Factor statements."
-  :type 'integer
-  :safe 'integerp
-  :group 'factor)
-
 (defcustom factor-comment-column 32
   "Indentation column of comments."
   :type 'integer
@@ -335,27 +330,6 @@ these lines in your .emacs:
 
 (defconst factor-sub-vocab-regex "^<\\([^ \n]+\\) *$")
 
-(defconst factor-indent-def-starts
-  '("" ":"
-    "AFTER" "BEFORE"
-    "COM-INTERFACE" "CONSULT"
-    "ENUM" "ERROR"
-    "FROM" "FUNCTION:" "FUNCTION-ALIAS:"
-    "INTERSECTION:"
-    "M" "M:" "MACRO" "MACRO:"
-    "MAIN-WINDOW:" "MEMO" "MEMO:" "METHOD"
-    "SYNTAX"
-    "PREDICATE" "PRIMITIVE" "PROTOCOL"
-    "SINGLETONS"
-    "STRUCT" "SYMBOLS" "TAG" "TUPLE"
-    "TYPED" "TYPED:"
-    "UNIFORM-TUPLE"
-    "UNION-STRUCT" "UNION"
-    "VARIANT" "VERTEX-FORMAT"))
-
-(defconst factor-no-indent-def-starts
-  '("ARTICLE" "HELP" "SPECIALIZED-ARRAYS"))
-
 (defconst factor-definition-start-regex
   (format "^\\(%s:\\) " (regexp-opt (append factor-no-indent-def-starts
                                             factor-indent-def-starts))))
@@ -567,76 +541,22 @@ these lines in your .emacs:
 (defsubst factor-brackets-start ()
   (nth 1 (syntax-ppss)))
 
-(defun factor-brackets-end ()
-  (save-excursion
-    (goto-char (factor-brackets-start))
-    (condition-case nil
-        (progn (forward-sexp)
-               (1- (point)))
-      (error -1))))
-
-(defsubst factor-indentation-at (pos)
-  (save-excursion (goto-char pos) (current-indentation)))
-
-(defsubst factor-at-begin-of-def ()
-  (looking-at factor-begin-of-def-regex))
-
-(defsubst factor-at-end-of-def ()
-  (looking-at factor-end-of-def-regex))
-
-(defsubst factor-is-last-char (pos)
-  (save-excursion
-    (goto-char (1+ pos))
-    (looking-at-p "[ ]*$")))
-
-(defsubst factor-line-offset (pos)
-  (- pos (save-excursion
-           (goto-char pos)
-           (beginning-of-line)
-           (point))))
-
 (defsubst factor-beginning-of-defun (&optional times)
   (re-search-backward factor-begin-of-def-regex nil t times))
 
 (defsubst factor-end-of-defun ()
   (re-search-forward factor-end-of-def-regex nil t))
 
-(defun factor-beginning-of-block-pos ()
-  (save-excursion
-    (if (> (factor-brackets-depth) 0)
-        (factor-brackets-start)
-      (factor-beginning-of-defun)
-      (point))))
-
-(defun factor-at-setter-line ()
-  (save-excursion
-    (beginning-of-line)
-    (when (re-search-forward factor-setter-regex
-                             (line-end-position)
-                             t)
-      (let* ((to (match-beginning 0))
-             (from (factor-beginning-of-block-pos)))
-        (goto-char from)
-        (let ((depth (factor-brackets-depth)))
-          (and (or (re-search-forward factor-constructor-regex to t)
-                   (re-search-forward factor-setter-regex to t))
-               (= depth (factor-brackets-depth))))))))
-
-(defun factor-at-constructor-line ()
+(defsubst factor-end-of-defun-pos ()
   (save-excursion
-    (beginning-of-line)
-    (re-search-forward factor-constructor-regex (line-end-position) t)))
+    (re-search-forward factor-end-of-def-regex nil t)
+    (point)))
 
 (defun factor-on-vocab ()
   "t if point is on a vocab name. We just piggyback on
   font-lock's pretty accurate information."
   (eq (get-char-property (point) 'face) 'factor-font-lock-vocabulary-name))
 
-(defsubst factor-end-of-defun-pos ()
-  (save-excursion
-    (re-search-forward factor-end-of-def-regex nil t)
-    (point)))
-
 (defun factor-find-end-of-def (&rest foo)
   (save-excursion
     (re-search-forward "[ \n];" nil t)
@@ -712,101 +632,6 @@ these lines in your .emacs:
         (push (concat (factor-find-in) ".private") usings))
       usings)))
 
-\f
-;;; Indentation:
-
-(defsubst factor-increased-indentation (&optional i)
-  (+ (or i (current-indentation)) factor-indent-level))
-
-(defsubst factor-decreased-indentation (&optional i)
-  (- (or i (current-indentation)) factor-indent-level))
-
-(defun factor-indent-in-brackets ()
-  (save-excursion
-    (beginning-of-line)
-    (when (> (factor-brackets-depth) 0)
-      (let* ((bs (factor-brackets-start))
-             (be (factor-brackets-end))
-             (ln (line-number-at-pos)))
-        (when (> ln (line-number-at-pos bs))
-          (cond ((and (> be 0)
-                      (= (- be (point)) (current-indentation))
-                      (= ln (line-number-at-pos be)))
-                 (factor-indentation-at bs))
-                ((or (factor-is-last-char bs)
-                     (not (eq ?\ (char-after (1+ bs)))))
-                 (factor-increased-indentation
-                  (factor-indentation-at bs)))
-                (t (+ 2 (factor-line-offset bs)))))))))
-
-(defun factor-indent-definition ()
-  (save-excursion
-    (beginning-of-line)
-    (when (factor-at-begin-of-def) 0)))
-
-(defsubst factor-previous-non-empty ()
-  "Move caret to the beginning of the last non-empty line."
-  (forward-line -1)
-  (while (and (not (bobp))
-              (looking-at "^[ ]*$\\|$"))
-    (forward-line -1)))
-
-(defun factor-indent-setter-line ()
-  (when (factor-at-setter-line)
-    (or (save-excursion
-          (let ((indent (and (factor-at-constructor-line)
-                             (current-indentation))))
-            (while (not (or indent
-                            (bobp)
-                            (factor-at-begin-of-def)
-                            (factor-at-end-of-def)))
-              (if (factor-at-constructor-line)
-                  (setq indent (factor-increased-indentation))
-                (forward-line -1)))
-            indent))
-        (save-excursion
-          (factor-previous-non-empty)
-          (current-indentation)))))
-
-(defconst factor-indent-def-start-regex
-  (format "^\\(%s:\\)\\( \\|\n\\)" (regexp-opt factor-indent-def-starts)))
-
-(defsubst factor-at-begin-of-indent-def ()
-  (looking-at factor-indent-def-start-regex))
-
-(defun factor-indent-continuation ()
-  (save-excursion
-    (factor-previous-non-empty)
-    (cond ((or (factor-at-end-of-def)
-               (factor-at-setter-line))
-           (factor-decreased-indentation))
-          ((factor-at-begin-of-indent-def)
-           (factor-increased-indentation))
-          (t (current-indentation)))))
-
-(defun factor-calculate-indentation ()
-  "Calculate Factor indentation for line at point."
-  (or (and (bobp) 0)
-      (factor-indent-definition)
-      (factor-indent-in-brackets)
-      (factor-indent-setter-line)
-      (factor-indent-continuation)
-      0))
-
-(defun factor-indent-line (&optional ignored)
-  "Indents the current Factor line."
-  (interactive)
-  (let ((target (factor-calculate-indentation))
-        (pos (- (point-max) (point))))
-    (if (= target (current-indentation))
-        (if (< (current-column) (current-indentation))
-            (back-to-indentation))
-      (beginning-of-line)
-      (delete-horizontal-space)
-      (indent-to target)
-      (if (> (- (point-max) pos) (point))
-          (goto-char (- (point-max) pos))))))
-
 \f
 ;;; Buffer cycling:
 
@@ -946,10 +771,15 @@ With prefix, non-existing files will be created."
   (setq-local electric-indent-chars
               (append '(?\] ?\} ?\n) electric-indent-chars))
 
-  (setq-local indent-line-function 'factor-indent-line)
   ;; No tabs for you!!
   (setq-local indent-tabs-mode nil)
 
+  (add-hook 'smie-indent-functions #'factor-smie-indent nil t)
+  (smie-setup factor-smie-grammar #'factor-smie-rules
+              :forward-token #'factor-smie-forward-token
+              :backward-token #'factor-smie-backward-token)
+  (setq-local smie-indent-basic factor-block-offset)
+
   (setq-local beginning-of-defun-function 'factor-beginning-of-defun)
   (setq-local end-of-defun-function 'factor-end-of-defun)
   ;; Load fuel-mode too if factor-mode-use-fuel is t.
diff --git a/misc/fuel/factor-smie.el b/misc/fuel/factor-smie.el
new file mode 100644 (file)
index 0000000..51189d3
--- /dev/null
@@ -0,0 +1,90 @@
+;;; factor-smie.el --- Helper function for indenting factor code
+
+;; Copyright (C) 2016  Björn Lindqvist
+;; See http://factorcode.org/license.txt for BSD license.
+
+;;; Commentary:
+
+;; Factor indentation using the SMIE framework.
+
+;;; Code:
+
+(defcustom factor-block-offset 4
+  "Indentation of Factor statements."
+  :type 'integer
+  :safe 'integerp
+  :group 'factor)
+
+(defconst factor-indent-def-starts
+  '("" ":"
+    "AFTER" "BEFORE"
+    "COM-INTERFACE" "CONSULT"
+    "ENUM" "ERROR"
+    "FROM" "FUNCTION:" "FUNCTION-ALIAS:"
+    "INTERSECTION:"
+    "M" "M:" "MACRO" "MACRO:"
+    "MAIN-WINDOW:" "MEMO" "MEMO:" "METHOD"
+    "SYNTAX"
+    "PREDICATE" "PRIMITIVE" "PROTOCOL"
+    "SINGLETONS"
+    "STRUCT" "SYMBOLS" "TAG" "TUPLE"
+    "TYPED" "TYPED:"
+    "UNIFORM-TUPLE"
+    "UNION-STRUCT" "UNION"
+    "VARIANT" "VERTEX-FORMAT"))
+
+(defconst factor-no-indent-def-starts
+  '("ARTICLE" "HELP" "SPECIALIZED-ARRAYS"))
+
+(defconst factor-indent-def-regex
+  (format "^\\(%s:\\)$" (regexp-opt factor-indent-def-starts)))
+
+(defconst factor-smie-grammar
+  (smie-prec2->grammar
+   (smie-bnf->prec2
+    '(
+      (exp (":" exp ";"))
+      ))))
+
+(defun factor-smie-rules (kind token)
+  (pcase (cons kind token)
+    (`(:before . ";") factor-block-offset)
+    (`(:list-intro . ,_) t)
+    ))
+
+(defun factor-smie-token (dir)
+  (pcase dir
+    ('forward (forward-comment (point-max)))
+    ('backward (forward-comment (- (point)))))
+  (let ((tok (buffer-substring-no-properties
+              (point)
+              (let ((syntax "w_\\\""))
+                (pcase dir
+                  ('forward (skip-syntax-forward syntax))
+                  ('backward (skip-syntax-backward syntax)))
+                (point)))))
+    ;; Token normalization. This way we only need one rule in
+    ;; factor-smie-grammar.
+    (cond ((string-match factor-indent-def-regex tok) ":")
+          (t tok))))
+
+(defun factor-smie-forward-token ()
+  (factor-smie-token 'forward))
+
+(defun factor-smie-backward-token ()
+  (factor-smie-token 'backward))
+
+(defun factor-smie-indent ()
+  (unless (looking-at ";\\_>")
+    (save-excursion
+      (let ((x nil))
+        (while (progn (setq x (smie-backward-sexp))
+                      (null (car-safe x))))
+        (when (string-match factor-indent-def-regex
+                            (or (nth 2 x) ""))
+          (goto-char (nth 1 x))
+          (+ factor-block-offset (smie-indent-virtual)))))))
+
+(provide 'factor-smie)
+
+;;; factor-smie.el ends here