From 0aa9f478e962e3e2d146871fa37267709404c52e Mon Sep 17 00:00:00 2001 From: akater Date: Thu, 28 Oct 2021 23:53:11 +0200 Subject: [PATCH] Indent cl-flet-like forms correctly in incomplete expressions * lisp/emacs-lisp/lisp-mode.el (lisp--local-defform-body-p): Support incomplete sexps * test/lisp/progmodes/elisp-mode-resources/flet.erts: Add tests for incomplete sexps (bug#9622). --- lisp/emacs-lisp/lisp-mode.el | 74 +++++------ .../progmodes/elisp-mode-resources/flet.erts | 121 ++++++++++++++++++ 2 files changed, 153 insertions(+), 42 deletions(-) diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 5dfb1fae356..15afdef0252 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -1114,56 +1114,46 @@ is the buffer position of the start of the containing expression." STATE is the `parse-partial-sexp' state for current position." (when-let ((start-of-innermost-containing-list (nth 1 state))) (let* ((parents (nth 9 state)) - (second-cons-after (cddr parents)) - second-order-parent) + (first-cons-after (cdr parents)) + (second-cons-after (cdr first-cons-after)) + first-order-parent second-order-parent) (while second-cons-after (when (= start-of-innermost-containing-list (car second-cons-after)) - (setq second-order-parent (car parents) + (setq second-order-parent (pop parents) + first-order-parent (pop parents) ;; Leave the loop. second-cons-after nil)) (pop second-cons-after) (pop parents)) (when second-order-parent - (save-excursion - (goto-char (1+ second-order-parent)) - (and (when-let ((head (ignore-errors - ;; FIXME: This does not distinguish - ;; between reading nil and a read error. - ;; We don't care but still, better fix this. - (read (current-buffer))))) - (memq head '( cl-flet cl-labels cl-macrolet cl-flet* - cl-symbol-macrolet))) - ;; Now we must check that we are - ;; in the second element of the flet-like form. - ;; It would be easier if `parse-partial-sexp' also recorded - ;; relative positions of subsexps in supersexps - ;; but it doesn't so we check manually. - ;; - ;; First, we must be looking at list now. - (ignore-errors (when (= (scan-lists (point) 1 0) - (scan-sexps (point) 1)) - ;; Looking at list; descend into it: - (down-list 1) - t)) - ;; In Wishful Lisp, the following form would be - ;; (cl-member start-of-innermost-containing-list - ;; (points-at-beginning-of-lists-at-this-level) - ;; :test #'=) - (cl-loop - with pos = (ignore-errors - ;; The first local definition may be indented - ;; with whitespace following open paren. - (goto-char (scan-lists (point) 1 0)) - (goto-char (scan-lists (point) -1 0)) - (point)) - while pos - do (if (= start-of-innermost-containing-list pos) - (cl-return t) - (setq pos (ignore-errors - (goto-char (scan-lists (point) 2 0)) - (goto-char (scan-lists (point) -1 0)) - (point))))))))))) + (let (local-definitions-starting-point) + (and (save-excursion + (goto-char (1+ second-order-parent)) + (when-let ((head (ignore-errors + ;; FIXME: This does not distinguish + ;; between reading nil and a read error. + ;; We don't care but still, better fix this. + (read (current-buffer))))) + (when (memq head '( cl-flet cl-labels cl-macrolet cl-flet* + cl-symbol-macrolet)) + ;; In what follows, we rely on (point) returning non-nil. + (setq local-definitions-starting-point + (progn + (parse-partial-sexp + (point) first-order-parent nil + ;; From docstring of `parse-partial-sexp': + ;; Fourth arg non-nil means stop + ;; when we come to any character + ;; that starts a sexp. + t) + (point)))))) + (ignore-errors + ;; We rely on `backward-up-list' working + ;; even when sexp is incomplete “to the right”. + (backward-up-list 2) + t) + (= local-definitions-starting-point (point)))))))) (defun lisp-indent-function (indent-point state) "This function is the normal value of the variable `lisp-indent-function'. diff --git a/test/lisp/progmodes/elisp-mode-resources/flet.erts b/test/lisp/progmodes/elisp-mode-resources/flet.erts index 447cf08cc25..7c4a0f304e9 100644 --- a/test/lisp/progmodes/elisp-mode-resources/flet.erts +++ b/test/lisp/progmodes/elisp-mode-resources/flet.erts @@ -220,3 +220,124 @@ Name: flet15 h i))) =-=-= + +Name: flet-indentation-incomplete-sexp-no-side-effects-1 +Code: (lambda () (emacs-lisp-mode) (setq indent-tabs-mode nil) (newline nil t)) +Point-Char: | + +=-= +(let ((x (and y| +=-= +(let ((x (and y + | +=-=-= + +Name: flet-indentation-incomplete-sexp-no-side-effects-2 + +=-= +(let ((x| +=-= +(let ((x + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-whitespace-1 +Point-Char: | + +=-= +(cl-flet((f (x)| +=-= +(cl-flet((f (x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-whitespace-2 +Point-Char: | + +=-= +(cl-flet((f(x)| +=-= +(cl-flet((f(x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-whitespace-3 + +=-= +(cl-flet ((f(x)| +=-= +(cl-flet ((f(x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-whitespace-4 + +=-= +(cl-flet( (f (x)| +=-= +(cl-flet( (f (x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-whitespace-5 + +=-= +(cl-flet( (f(x)| +=-= +(cl-flet( (f(x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-and-excessive-whitespace-1 + +=-= +(cl-flet((f (x)| +=-= +(cl-flet((f (x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-and-excessive-whitespace-2 + +=-= +(cl-flet ((f(x)| +=-= +(cl-flet ((f(x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-and-excessive-whitespace-3 + +=-= +(cl-flet( (f (x)| +=-= +(cl-flet( (f (x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-and-excessive-whitespace-4 + +=-= +(cl-flet( (f (x)| +=-= +(cl-flet( (f (x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-and-excessive-whitespace-5 + +=-= +(cl-flet( (f (x)| +=-= +(cl-flet( (f (x) + | +=-=-= + +Name: flet-indentation-incomplete-sexp-missing-and-excessive-whitespace-6 + +=-= +(cl-flet( (f(x)| +=-= +(cl-flet( (f(x) + | +=-=-= -- 2.39.2