[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/tuareg b0a2547 2/7: Let declarative `and` begin a defun
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/tuareg b0a2547 2/7: Let declarative `and` begin a defun |
Date: |
Sat, 5 Jun 2021 12:57:16 -0400 (EDT) |
branch: elpa/tuareg
commit b0a2547c71716c766ab5eac39ea7c3cd22e9713d
Author: Mattias EngdegÄrd <mattiase@acm.org>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>
Let declarative `and` begin a defun
Since each `and` hitched to a `type` or declarative `let` produces a
definition, consider them defuns in their own right. Previously, an
entire `let`...`and`... or `type`...`and`... chain was a single defun,
which made movement-by-defun operations less useful.
To get an acceptable interactive latency, we memoise the
calls (smie-forward-sexp "and") and (smie-backward-sexp "and"),
since they are called repeatedly during movement-by-defun.
---
tuareg-tests.el | 89 ++++++++++++++++++++++++++++++++++++++
tuareg.el | 129 ++++++++++++++++++++++++++++++++++++++++----------------
2 files changed, 182 insertions(+), 36 deletions(-)
diff --git a/tuareg-tests.el b/tuareg-tests.el
index 70cb7a4..44dc525 100644
--- a/tuareg-tests.el
+++ b/tuareg-tests.el
@@ -77,4 +77,93 @@
;; case does not seem to be very well-defined.
)))
+(ert-deftest tuareg-chained-defun ()
+ ;; Check motion by defuns that are chained by "and".
+ (with-temp-buffer
+ (tuareg-mode)
+ (let (p0 p1 p2a p2b p3 p4 p5a p5b p6 p7 p8a p8b)
+ (insert "(* *)\n\n")
+ (setq p0 (point))
+ (insert "type t1 =\n"
+ " A\n")
+ (setq p1 (point))
+ (insert "and t2 =\n"
+ " B\n")
+ (setq p2a (point))
+ (insert "\n")
+ (setq p2b (point))
+ (insert "and t3 =\n"
+ " C\n")
+ (setq p3a (point))
+ (insert "\n")
+ (setq p3b (point))
+ (insert "let f1 x =\n"
+ " aa\n")
+ (setq p4 (point))
+ (insert "and f2 x =\n"
+ " bb\n")
+ (setq p5a (point))
+ (insert "\n")
+ (setq p5b (point))
+ (insert "and f3 x =\n"
+ " let ff1 y =\n"
+ " cc\n"
+ " and ff2 y = (\n")
+ (setq p6 (point))
+ (insert " qq ww) + dd\n"
+ " and ff3 y =\n"
+ " for i = 1 to 10 do\n"
+ " ee;\n")
+ (setq p7 (point))
+ (insert " ff;\n"
+ " done\n")
+ (setq p8a (point))
+ (insert "\n")
+ (setq p8b (point))
+ (insert "exception E\n")
+
+ ;; Walk backwards from the end.
+ (goto-char (point-max))
+ (beginning-of-defun)
+ (should (equal (point) p8b))
+ (beginning-of-defun)
+ (should (equal (point) p5b))
+ (beginning-of-defun)
+ (should (equal (point) p4))
+ (beginning-of-defun)
+ (should (equal (point) p3b))
+ (beginning-of-defun)
+ (should (equal (point) p2b))
+ (beginning-of-defun)
+ (should (equal (point) p1))
+ (beginning-of-defun)
+ (should (equal (point) p0))
+ (beginning-of-defun)
+ (should (equal (point) (point-min)))
+
+ ;; Walk forwards from the beginning.
+ (end-of-defun)
+ (should (equal (point) p1))
+ (end-of-defun)
+ (should (equal (point) p2a))
+ (end-of-defun)
+ (should (equal (point) p3a))
+ (end-of-defun)
+ (should (equal (point) p4))
+ (end-of-defun)
+ (should (equal (point) p5a))
+ (end-of-defun)
+ (should (equal (point) p8a))
+ (end-of-defun)
+ (should (equal (point) (point-max)))
+
+ ;; Jumps from inside a defun.
+ (goto-char p7)
+ (beginning-of-defun)
+ (should (equal (point) p5b))
+
+ (goto-char p6)
+ (end-of-defun)
+ (should (equal (point) p8a)))))
+
(provide 'tuareg-tests)
diff --git a/tuareg.el b/tuareg.el
index 341c3de..ad86866 100644
--- a/tuareg.el
+++ b/tuareg.el
@@ -2362,8 +2362,16 @@ Return a non-nil value if a comment was skipped."
(skip-chars-forward " \t;")
(while (tuareg--skip-forward-comment)))
-(defconst tuareg-starters-syms
- '("type" "d-let" "exception" "module" "class" "val" "external" "open"))
+(defvar-local tuareg-smie--forward-and-cache nil
+ "Alist memoising positions from (smie-forward-sexp \"and\").")
+
+(defvar-local tuareg-smie--backward-and-cache nil
+ "Alist memoising results from (smie-backward-sexp \"and\").")
+
+(defvar-local tuareg-smie--and-cache-tick nil
+ "Buffer-modification tick at which and-caches are valid.
+Applies to `tuareg-smie--forward-and-cache'
+and `tuareg-smie--backward-and-cache'.")
(defun tuareg-backward-beginning-of-defun ()
"Move the point backward to the beginning of a definition.
@@ -2375,45 +2383,94 @@ Return the token starting the phrase (`nil' if it is an
expression)."
(or (looking-at (rx symbol-start))
(/= (skip-syntax-forward "w_") 0)
(tuareg--skip-backward-comments-semicolon))))
- (let ((opoint (point))
- (td (smie-backward-sexp ";;"))) ; for expressions
- (cond
- ((and (car td) (member (nth 2 td) tuareg-starters-syms))
- (goto-char (nth 1 td))
- (nth 2 td))
- (t
- (goto-char opoint)
- (let ((tok nil))
- (while (let ((td (smie-backward-sexp 'halfsexp)))
- (cond
- ((and (car td) (member (nth 2 td) tuareg-starters-syms))
- (goto-char (nth 1 td))
- (setq tok (nth 2 td))
- nil)
- ((and (car td) (string= (nth 2 td) ";;"))
- nil)
- ((and (car td) (not (numberp (car td))))
- (unless (bobp)
- (goto-char (nth 1 td))
- ;; Make sure there is not a preceding ;;
- (let ((tok (tuareg-smie-backward-token)))
- (goto-char (nth 1 td))
- (not (string= tok ";;")))))
- (t t))))
- tok)))))
-
-(defun tuareg--skip-double-semicolon ()
- (tuareg-skip-blank-and-comments)
- (when (looking-at ";;[ \t\n]*")
- (goto-char (match-end 0))))
+ ;; We treat each "and" clause belonging to "d-let" or "type" as defuns
+ ;; in the own right since that is how programmers think about it.
+ (let* ((and-pos nil)
+ (ret-tok nil)
+ (tick (buffer-chars-modified-tick))
+ (cache-valid (eql tuareg-smie--and-cache-tick tick)))
+ (while
+ (and (not (bobp))
+ ;; Memoised call to (smie-backward-exp "and")
+ (let* ((cached
+ (and cache-valid
+ (assq (point) tuareg-smie--backward-and-cache)))
+ (td (if cached
+ (cdr cached)
+ (unless cache-valid
+ (setq tuareg-smie--forward-and-cache nil)
+ (setq tuareg-smie--backward-and-cache nil)
+ (setq tuareg-smie--and-cache-tick tick)
+ (setq cache-valid t))
+ (let* ((pt (point))
+ (r (smie-backward-sexp "and")))
+ (push (cons pt r)
+ tuareg-smie--backward-and-cache)
+ r))))
+ (and (nth 0 td)
+ (let ((tpos (nth 1 td))
+ (tok (nth 2 td)))
+ (cond
+ ;; Arrived at a token that always starts a defun.
+ ((member tok '("type" "d-let" "exception" "module"
+ "class" "val" "external" "open"))
+ (if (and and-pos (member tok '("d-let" "type")))
+ ;; Previously found "and" is the start of the
+ ;; defun: return it.
+ (progn
+ (goto-char and-pos)
+ (setq ret-tok "and"))
+ ;; This is the start of the defun.
+ (goto-char tpos)
+ (setq ret-tok tok))
+ nil)
+ ;; Arrived at "and": keep going backwards to find
+ ;; out whether it was the start of a defun.
+ ((equal tok "and")
+ (unless and-pos
+ (setq and-pos tpos))
+ (goto-char tpos)
+ t)
+ ;; Arrived at "let": keep going backwards.
+ ((equal tok "let")
+ ;; Any previous "and" was not the start of a defun.
+ (setq and-pos nil)
+ (goto-char tpos)
+ t)
+ ;; Other tokens not starting a defun: keep going.
+ ((member tok '(";;" "do" "downto" "to"))
+ (goto-char tpos)
+ t)
+ ;; Left bracket or similar: keep going.
+ ((not (numberp (nth 0 td)))
+ (goto-char tpos)
+ t)
+ ;; Something else: stop.
+ (t nil)))))))
+ ret-tok))
+
+(defun tuareg-smie--forward-sexp-and ()
+ "Memoised (smie-forward-sexp \"and\"), point motion only."
+ (let* ((tick (buffer-chars-modified-tick))
+ (cache-valid (eql tuareg-smie--and-cache-tick tick))
+ (cached (and cache-valid
+ (assq (point) tuareg-smie--forward-and-cache))))
+ (if cached
+ (goto-char (cdr cached))
+ (unless cache-valid
+ (setq tuareg-smie--forward-and-cache nil)
+ (setq tuareg-smie--backward-and-cache nil)
+ (setq tuareg-smie--and-cache-tick tick))
+ (let ((pt (point)))
+ (smie-forward-sexp "and")
+ (push (cons pt (point)) tuareg-smie--forward-and-cache)))))
(defun tuareg-end-of-defun ()
"Assuming that we are at the beginning of a definition, move to its end.
See variable `end-of-defun-function'."
(interactive)
- (let ((td (smie-forward-sexp ";;"))) ; for expressions
- (when (member (nth 2 td) tuareg-starters-syms)
- (smie-forward-sexp 'halfsexp)))
+ (tuareg-smie--forward-token) ; Skip the head token.
+ (tuareg-smie--forward-sexp-and)
(tuareg--skip-forward-comments-semicolon))
(defun tuareg-beginning-of-defun (&optional arg)
- [nongnu] elpa/tuareg updated (37a6730 -> 24c1a1a), ELPA Syncer, 2021/06/05
- [nongnu] elpa/tuareg 75c1ffc 1/7: Make beginning-of-defun (C-M-a) repeatable, ELPA Syncer, 2021/06/05
- [nongnu] elpa/tuareg b0a2547 2/7: Let declarative `and` begin a defun,
ELPA Syncer <=
- [nongnu] elpa/tuareg 0a501f7 5/7: Update list of Emacs versions for CI, ELPA Syncer, 2021/06/05
- [nongnu] elpa/tuareg cd86e73 4/7: Remove key binding for obsolete tuareg-narrow-to-phrase (bug#243), ELPA Syncer, 2021/06/05
- [nongnu] elpa/tuareg a0954c3 6/7: * tuareg-tests.el (tuareg-chained-defun): Fix warnings, ELPA Syncer, 2021/06/05
- [nongnu] elpa/tuareg 24c1a1a 7/7: * tuareg-tests.el (tuareg--lets): New macro, ELPA Syncer, 2021/06/05
- [nongnu] elpa/tuareg fa87a10 3/7: Put indentation tests in ERT, ELPA Syncer, 2021/06/05