diff --git a/misc/fuel/factor-mode.el b/misc/fuel/factor-mode.el index ecde6c1ae9..95ec91bd0d 100644 --- a/misc/fuel/factor-mode.el +++ b/misc/fuel/factor-mode.el @@ -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. diff --git a/misc/fuel/factor-smie.el b/misc/fuel/factor-smie.el new file mode 100644 index 0000000000..51189d3ad7 --- /dev/null +++ b/misc/fuel/factor-smie.el @@ -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