[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/haskell-tng-mode 2320b89 167/385: alternative to smie-rule
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/haskell-tng-mode 2320b89 167/385: alternative to smie-rules-* in :elem and :list-intro |
Date: |
Tue, 5 Oct 2021 23:59:24 -0400 (EDT) |
branch: elpa/haskell-tng-mode
commit 2320b89ba59d363d5e5c698602076b931bd7c72a
Author: Tseen She <ts33n.sh3@gmail.com>
Commit: Tseen She <ts33n.sh3@gmail.com>
alternative to smie-rules-* in :elem and :list-intro
---
haskell-tng-layout.el | 8 ++
haskell-tng-smie.el | 198 +++++++++++++++-------------------
haskell-tng-util.el | 10 ++
test/haskell-tng-indent-test.el | 4 +
test/src/indentation.hs.append.indent | 4 +-
test/src/indentation.hs.insert.indent | 20 ++--
test/src/indentation.hs.reindent | 2 +-
7 files changed, 123 insertions(+), 123 deletions(-)
diff --git a/haskell-tng-layout.el b/haskell-tng-layout.el
index 106bccd..bdedb2f 100644
--- a/haskell-tng-layout.el
+++ b/haskell-tng-layout.el
@@ -36,6 +36,14 @@
(require 'haskell-tng-util)
+;; FIXME incorrect layout for brackets near the edge, don't add semis for
commas
+;; and parens.
+;;
+;; ;data Record1 = Record1 {
+;; fieldA :: String
+;; ;, fieldB :: String
+;; ;}
+
;; Easiest cache... full buffer parse with full invalidation on any insertion.
;;
;; A list of (OPEN . (CLOSE . SEPS)) positions, one per inferred block.
diff --git a/haskell-tng-smie.el b/haskell-tng-smie.el
index 6f2cc0a..18707ed 100644
--- a/haskell-tng-smie.el
+++ b/haskell-tng-smie.el
@@ -136,95 +136,89 @@ information, to aid in the creation of new rules."
;; indentation of that token. For example, consider a `do' block, we may get an
;; `:after' and a `:before' for `do' which may be at column 20 but virtually at
;; column 0.
+;;
+;; NOTE https://debbugs.gnu.org/cgi/bugreport.cgi?bug=36434
+;;
+;; smie-rule-* are not designed be used in :elem or :list-intro
(defun haskell-tng-smie:rules (method arg)
(when haskell-tng-smie:debug
(let ((sym (symbol-at-point))
(parent (and (boundp 'smie--parent)
- (caddr (smie-indent--parent))))
- (grand (and (boundp 'smie--parent)
- (caddr (smie-indent--grandparent)))))
+ (caddr (smie-indent--parent)))))
(with-current-buffer haskell-tng-smie:debug
(insert
(format
- "RULES: %S %S %S\n PARENT: %S\n GRAND: %S\n"
- method arg sym parent grand)))))
+ "RULES: %S %S %S\n P: %S\n"
+ method arg sym parent)))))
(pcase method
-
(:elem
(pcase arg
((or 'args 'basic) 0)
('empty-line-token
- ;; WORKAROUND https://debbugs.gnu.org/cgi/bugreport.cgi?bug=36434
- ;;
- ;; smie-rule-* are not designed be used in :elem because there is no
- ;; clear current token. We force their use to mean relative to the
- ;; current empty line, prior to knowing what the expected value should
- ;; be.
- (defvar smie--after)
- (setq smie--after (point))
- (defvar smie--parent)
- (setq smie--parent nil)
- (when haskell-tng-smie:debug
- (let ((parent (caddr (smie-indent--parent)))
- (grand (caddr (smie-indent--grandparent)))
- (pnonid (caddr (smie-indent--prev-nonid))))
- (with-current-buffer haskell-tng-smie:debug
- (insert
- (format
- " PARENT': %S\n GRAND': %S\n NONID: %S\n"
- parent grand pnonid)))))
-
- (cond
- ((or (smie-rule-parent-p "[" "(")
- (and (smie-rule-parent-p "{")
- (smie-rule-grandparent-p "=")))
- ",")
-
- ((or (smie-rule-parent-p "|")
- (and (smie-rule-parent-p "=")
- (smie-rule-grandparent-p "data"))
- (smie-rule-prev-nonid-p "|"))
+ (let* ((parents (save-excursion
+ (haskell-tng-smie:ancestors 2)))
+ (parent (car parents))
+ (grand (cadr parents))
+ (prev (save-excursion
+ (car (smie-indent-backward-token))))
+ (next (save-excursion
+ (car (smie-indent-forward-token)))))
+
(when haskell-tng-smie:debug
(with-current-buffer haskell-tng-smie:debug
- (insert " NEWLINE IS |\n")))
- "|")
-
- ((smie-rule-next-p ";" "}")
- ;; TODO semantic indentation
- ;;
- ;; Consult a local table, populated by an external tool, containing
- ;; the parameter requirements for function calls. For simple cases,
- ;; we should be able to infer if the user wants to terminate ; or
- ;; continue "" the current line.
- ";")
-
- ((save-excursion
- (forward-comment (point-max))
- (eobp))
- ;; this happens when we're at the end of the buffer. Must use
- ;; heuristics before we get to this point.
- ";")
- ))))
+ (insert (format " ^^: %S\n ^: %S\n -1: %S\n +1: %S\n"
+ grand parent prev next))))
+
+ (cond
+ ((or
+ (equal next ",")
+ (member parent '("[" "(" ","))
+ (and (equal parent "{")
+ (equal grand "=")))
+ ",")
+
+ ((or (equal parent "|")
+ ;; TODO not if there is a deriving keyword somewhere
+ (and (equal parent "=")
+ (equal grand "data")
+ (not (equal prev "}"))))
+ "|")
+
+ ((member next '(";" "}"))
+ ;; TODO we could do semantic indentation here
+ ;;
+ ;; Consult a local table, populated by an external tool, containing
+ ;; the parameter requirements for function calls. For simple cases,
+ ;; we should be able to infer if the user wants to terminate ; or
+ ;; continue "" the current line.
+ ";")
+
+ ((save-excursion
+ (forward-comment (point-max))
+ (eobp))
+ ;; this happens when we're at the end of the buffer. Must use
+ ;; heuristics before we get to this point.
+ ";")
+ )))))
(:list-intro
(pcase arg
((or "<-" "SYMID") t)
- ("=" (not (smie-rule-parent-p "data")))
))
(:after
(pcase arg
((or "let" "do" "of" "in" "->" "\\") 2)
- ("=" (when (not (smie-rule-parent-p "data")) 2))
("\\case" 2) ;; LambdaCase
- ("where" (when (not (smie-rule-parent-p "module")) 2))
+ ((and "=" (guard (not (smie-rule-parent-p "data")))) 2)
+ ((and "where" (guard (not (smie-rule-parent-p "module")))) 2)
((or "[" "(") 2)
- ("{" (when (not (smie-rule-prev-p
- "\\case" ;; LambdaCase
- "where" "let" "do" "of"))
- 2))
+ ((and "{" (guard (not (smie-rule-prev-p
+ "\\case" ;; LambdaCase
+ "where" "let" "do" "of"))))
+ 2)
("," (smie-rule-separator method))
((or "SYMID")
(if (smie-rule-hanging-p) 2 (smie-rule-parent)))
@@ -248,14 +242,13 @@ information, to aid in the creation of new rules."
(smie-rule-parent))
("|"
(if (smie-rule-parent-p "=")
- (smie-rule-parent-column)
+ (haskell-tng-smie:rule-parent-column)
(smie-rule-separator method)))
- ((or "[" "(" "{")
- (when (smie-rule-hanging-p)
- (smie-rule-parent)))
+ ((and (or "[" "(" "{") (guard (smie-rule-hanging-p)))
+ (smie-rule-parent))
("," (smie-rule-separator method))
- (_ (when (smie-rule-parent-p "SYMID")
- (smie-rule-parent)))
+ ((guard (smie-rule-parent-p "SYMID"))
+ (smie-rule-parent))
))
))
@@ -372,6 +365,33 @@ BEFORE is t if the line appears before the indentation."
:backward-token #'haskell-tng-lexer:backward-token)
)
+(defun haskell-tng-smie:rule-parent-column ()
+ "For use inside `smie-rules-function',
+use the column indentation as the parent. Note that
+`smie-rule-parent' may use relative values."
+ (save-excursion
+ (goto-char (cadr (smie-indent--parent)))
+ `(column . ,(current-column))))
+
+(defun haskell-tng-smie:ancestors (n)
+ "A list of the Nth non-{identifier, matched paren, string}
+tokens before point, closest first. Leaves the point at the most
+extreme parent.
+
+Inspired by `smie-indent--parent', which can only be used in
+:before and :after."
+ (when-let ((res (or (smie-backward-sexp t)
+ (haskell-tng:until
+ (smie-backward-sexp)
+ (bobp))))
+ (tok (if (car res)
+ ;; break through open parens
+ (car (smie-indent-backward-token))
+ (caddr res))))
+ (if (< 1 n)
+ (cons tok (haskell-tng-smie:ancestors (- n 1)))
+ (list tok))))
+
;; SMIE wishlist, in order of desirability:
;;
;; 1. if the lexer could return lists of tokens.
@@ -389,47 +409,5 @@ BEFORE is t if the line appears before the indentation."
;; but can otherwise be used as a varid. I'd like to be able to lex it as
(or
;; "via" "VARID") so that it can appear in multiple places in the grammar.
-;; Extensions to SMIE
-(defun smie-rule-parent-column ()
- "For use inside `smie-rules-function',
-use the column indentation as the parent. Note that
-`smie-rule-parent' may use relative values."
- (save-excursion
- (goto-char (cadr (smie-indent--parent)))
- `(column . ,(current-column))))
-
-(defun smie-indent--grandparent ()
- "Like `smie-indent--parent' but for the parent's parent."
- (defvar smie--parent)
- (let (cache)
- (save-excursion
- (goto-char (cadr (smie-indent--parent)))
- (setq cache smie--parent)
- (setq smie--parent nil)
- (let ((res (smie-indent--parent)))
- (setq smie--parent cache)
- res))))
-
-(defun smie-rule-grandparent-p (&rest grandparents)
- "Like `smie-rule-parent-p' but for the parent's parent."
- (member (nth 2 (smie-indent--grandparent)) grandparents))
-
-(defun smie-indent--prev-nonid ()
- "Returns the previous non-identifier s-expression."
- (save-excursion
- (let (seen)
- (while (null (setq seen (smie-backward-sexp))))
- seen)))
-
-(defun smie-rule-prev-nonid-p (&rest tokens)
- "Non-nil if the previous non-identifier s-expression is one of TOKENS."
- (member (nth 2 (smie-indent--prev-nonid)) tokens))
-
-(defun smie-debug-parent ()
- (interactive)
- (defvar smie--parent)
- (setq smie--parent nil)
- (smie-indent--parent))
-
(provide 'haskell-tng-smie)
;;; haskell-tng-smie.el ends here
diff --git a/haskell-tng-util.el b/haskell-tng-util.el
index 5f464db..2c08e86 100644
--- a/haskell-tng-util.el
+++ b/haskell-tng-util.el
@@ -60,5 +60,15 @@ and taking a regexp."
default-directory
(lambda (dir) (directory-files dir nil regexp))))
+(defmacro haskell-tng:until (form &optional guard)
+ "Runs `while' on FORM until it is non-nil, returning the value.
+
+A guard is provided which may cause the loop to exit early with nil."
+ (let ((res (gensym "res")))
+ `(let (,res)
+ (while (and (not ,guard)
+ (not (setq ,res ,form))))
+ ,res)))
+
(provide 'haskell-tng-util)
;;; haskell-tng-util.el ends here
diff --git a/test/haskell-tng-indent-test.el b/test/haskell-tng-indent-test.el
index 11a14ca..da015fc 100644
--- a/test/haskell-tng-indent-test.el
+++ b/test/haskell-tng-indent-test.el
@@ -14,10 +14,14 @@
;; FIXME implement more indentation rules
;;
+;; TODO records
+;; TODO coproducts
;; TODO multiline type signatures
;; TODO if/then/else
;; TODO data: one conid ~> record, multi ~> coproduct
+;; TODO reindenting needs attention, it's all over the radar
+
;; Three indentation regression tests are possible:
;;
;; 1. newline-and-indent with the rest of the file deleted (append)
diff --git a/test/src/indentation.hs.append.indent
b/test/src/indentation.hs.append.indent
index a5d99e0..1f9b5ee 100644
--- a/test/src/indentation.hs.append.indent
+++ b/test/src/indentation.hs.append.indent
@@ -149,7 +149,7 @@ not_dollars = do
1 v 2 3
data Wibble = Wibble Int
-v
+1 v
| Wobble Int
1 v
| Vibble Int
@@ -167,7 +167,7 @@ v
v
data Record2 = Record2
-v
+1 v
{ fieldA :: String
1 v
, fieldB :: String
diff --git a/test/src/indentation.hs.insert.indent
b/test/src/indentation.hs.insert.indent
index dc63d4c..5be54f3 100644
--- a/test/src/indentation.hs.insert.indent
+++ b/test/src/indentation.hs.insert.indent
@@ -45,7 +45,7 @@ basic_do = do
sideeffect' blah
2 v 1 3
let baz = blah blah
-3 2 1 4 v
+4 2 1 5 3 v
blah -- manual correction
2 3 v 4 1
gaz = blah
@@ -69,13 +69,13 @@ nested_do = -- manual correction
1 2 v 3 4
nested_where a b = foo a b
-1 v
+1 v
where -- manual correction
1 v
foo = bar baz -- indented
1 v
baz = blah blah -- same level as foo
-2 1 v
+2 1 v
where -- manual correction
1 2 v
gaz a = blah -- indented
@@ -171,7 +171,7 @@ data Record2 = Record2
{ fieldA :: String
1 v
, fieldB :: String
-2 1 v
+1 v
}
v 1
@@ -179,13 +179,13 @@ v 1
lists1 = [ foo
1 v
, bar
-2 1 v
+1 v
, [ blah
2 1 v
, blah
-2 3 1 v
+1 2 v
, blah ]
-2 1 v
+2 v 1
]
v 1 2
@@ -209,13 +209,13 @@ v 1
tuples1 = ( foo
1 v
, bar
-2 1 v
+1 v
, ( blah
2 1 v
, blah
-2 3 1 v
+1 2 v
, blah )
-2 1 v
+2 v 1
)
v 1 2
diff --git a/test/src/indentation.hs.reindent b/test/src/indentation.hs.reindent
index f764170..3a82c98 100644
--- a/test/src/indentation.hs.reindent
+++ b/test/src/indentation.hs.reindent
@@ -46,7 +46,7 @@ v 1 2 3
sideeffect' blah
v 1 3 2
let baz = blah blah
-3 2 1 4 v
+3 2 1 4 v
blah -- manual correction
v 3 2 4 1
gaz = blah
- [nongnu] elpa/haskell-tng-mode df5d2c5 122/385: better whitespace gap detection, (continued)
- [nongnu] elpa/haskell-tng-mode df5d2c5 122/385: better whitespace gap detection, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 3b7cd02 123/385: some todos, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode fc4d3e3 127/385: compilation mode supports multiline error spans, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 88bad65 128/385: visually distinguish compile errors and warnings, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 9dc0abc 129/385: alt indentation levels should be ordered, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 879fc34 130/385: thots, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 1009082 140/385: append indentation test, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 88b17d4 137/385: started indentation rules, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode cf22f3a 138/385: simplify indentation testing, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode a0f777e 166/385: Revert "duplicate ; tokens to be used as terminators", ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 2320b89 167/385: alternative to smie-rules-* in :elem and :list-intro,
ELPA Syncer <=
- [nongnu] elpa/haskell-tng-mode 60d8c20 149/385: indent "case of", ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 7f9afd2 159/385: list indentation, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 6e70344 165/385: duplicate ; tokens to be used as terminators, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode ab5252b 169/385: rearrange alts, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode e1aba33 168/385: fix a layout bug in lists and records, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 2c6c772 150/385: better WLDO indentation, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 3f54673 155/385: don't fontify toplevel, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 32175c0 158/385: thots, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 8c2e3f7 157/385: better symid indentation, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode bc5c827 160/385: tuple indentation, ELPA Syncer, 2021/10/06