FUEL: new indentation system based on smie

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
char-rename
Björn Lindqvist 2016-07-02 03:24:46 +02:00
parent c646db3ae5
commit 114ced868a
2 changed files with 100 additions and 180 deletions

View 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 ()
(defsubst factor-end-of-defun-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 ()
(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)))
;;; 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))))))
;;; 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.

90
misc/fuel/factor-smie.el Normal file
View File

@ -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