From 2a6416a26c3b7e11340a2b296dbbd9c1d8a51ca7 Mon Sep 17 00:00:00 2001 From: Mauro Aranda Date: Sat, 16 Sep 2023 18:15:40 -0300 Subject: [PATCH] Fix indentation in perl-mode (Bug#35925) * lisp/progmodes/perl-mode.el (perl--format-regexp): New defconst. (perl--end-of-format-p): New function. (perl-continuation-line-p): Use it. (perl-calculate-indent): Use it. Make the lines of the formlist stay at column 0. * test/lisp/progmodes/cperl-mode-resources/cperl-bug-35925.pl: New test file. * test/lisp/progmodes/cperl-mode-tests.el (cperl-test-bug-35925): New test. --- lisp/progmodes/perl-mode.el | 30 ++++++++++++---- .../cperl-mode-resources/cperl-bug-35925.pl | 36 +++++++++++++++++++ test/lisp/progmodes/cperl-mode-tests.el | 14 ++++++++ 3 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 test/lisp/progmodes/cperl-mode-resources/cperl-bug-35925.pl diff --git a/lisp/progmodes/perl-mode.el b/lisp/progmodes/perl-mode.el index 040ef187e97..b8d811baf0d 100644 --- a/lisp/progmodes/perl-mode.el +++ b/lisp/progmodes/perl-mode.el @@ -223,7 +223,10 @@ perl-quote-like-pairs "\\|=>" "\\|[?:.,;|&*=!~({[]" "\\|[^-+][-+]" ;Bug#42168: `+' is intro but `++' isn't! - "\\|\\(^\\)\\)[ \t\n]*"))) + "\\|\\(^\\)\\)[ \t\n]*")) + + (defconst perl--format-regexp "^[ \t]*format.*=[ \t]*\\(\n\\)" + "Regexp to match the start of a format declaration.")) (defun perl-syntax-propertize-function (start end) (let ((case-fold-search nil)) @@ -252,7 +255,7 @@ perl-syntax-propertize-function ;; Handle funny names like $DB'stop. ("\\$ ?{?\\^?[_[:alpha:]][_[:alnum:]]*\\('\\)[_[:alpha:]]" (1 "_")) ;; format statements - ("^[ \t]*format.*=[ \t]*\\(\n\\)" + (perl--format-regexp (1 (prog1 "\"" (perl-syntax-propertize-special-constructs end)))) ;; Propertize perl prototype chars `$%&*;+@\[]' as punctuation ;; in `sub' arg-specs like `sub myfun ($)' and `sub ($)'. But @@ -946,6 +949,17 @@ perl-indent-line (goto-char (- (point-max) pos))) shift-amt)) +(defun perl--end-of-format-p () + "Non-nil if point is at the end of a format declaration, skipping whitespace." + (save-excursion + (skip-chars-backward " \t\n") + (beginning-of-line) + (when-let ((comm (and (looking-at "^\\.$") + (nth 8 (syntax-ppss))))) + (goto-char comm) + (beginning-of-line) + (looking-at perl--format-regexp)))) + (defun perl-continuation-line-p () "Move to end of previous line and return non-nil if continued." ;; Statement level. Is it a continuation or a new statement? @@ -959,7 +973,8 @@ perl-continuation-line-p (beginning-of-line) (perl-backward-to-noncomment)) ;; Now we get the answer. - (unless (memq (preceding-char) '(?\; ?\} ?\{)) + (unless (or (memq (preceding-char) '(?\; ?\} ?\{)) + (perl--end-of-format-p)) (preceding-char))) (defun perl-hanging-paren-p () @@ -999,7 +1014,9 @@ perl-calculate-indent (state (syntax-ppss)) (containing-sexp (nth 1 state)) ;; Don't auto-indent in a quoted string or a here-document. - (unindentable (or (nth 3 state) (eq 2 (nth 7 state))))) + (unindentable (or (nth 3 state) (eq 2 (nth 7 state)))) + (format (and (nth 3 state) + (char-equal (nth 3 state) ?\n)))) (when (and (eq t (nth 3 state)) (save-excursion (goto-char (nth 8 state)) @@ -1009,7 +1026,7 @@ perl-calculate-indent (setq unindentable nil) (setq containing-sexp (nth 8 state))) (cond - (unindentable 'noindent) + (unindentable (if format 0 'noindent)) ((null containing-sexp) ; Line is at top level. (skip-chars-forward " \t\f") (if (memq (following-char) @@ -1018,7 +1035,8 @@ perl-calculate-indent ;; indent a little if this is a continuation line (perl-backward-to-noncomment) (if (or (bobp) - (memq (preceding-char) '(?\; ?\}))) + (memq (preceding-char) '(?\; ?\})) + (perl--end-of-format-p)) 0 perl-continued-statement-offset))) ((/= (char-after containing-sexp) ?{) ;; line is expression, not statement: diff --git a/test/lisp/progmodes/cperl-mode-resources/cperl-bug-35925.pl b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-35925.pl new file mode 100644 index 00000000000..e3f96241ab7 --- /dev/null +++ b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-35925.pl @@ -0,0 +1,36 @@ +# This resource file can be run with cperl--run-testcases from +# cperl-tests.el and works with both perl-mode and cperl-mode. + +# -------- Bug#35925: input ------- +format FH = +@### @.### @### +42, 3.1415, 0 +. +write FH; + +# -------- Bug#35925: expected output ------- +format FH = +@### @.### @### +42, 3.1415, 0 +. +write FH; + +# -------- Bug#35925: end ------- + +# -------- format not as top-level: input ------- +foo: { + format STDOUT = +^<<<< +$foo +. +write; +} +# -------- format not as top-level: expected output ------- +foo: { + format STDOUT = +^<<<< +$foo +. + write; +} +# -------- format not as top-level: end ------- diff --git a/test/lisp/progmodes/cperl-mode-tests.el b/test/lisp/progmodes/cperl-mode-tests.el index 87d8ffa2d8d..172adfc79dd 100644 --- a/test/lisp/progmodes/cperl-mode-tests.el +++ b/test/lisp/progmodes/cperl-mode-tests.el @@ -1139,6 +1139,20 @@ cperl-test-bug-30393 (cperl-indent-command) (forward-line 1)))) +(ert-deftest cperl-test-bug-35925 () + "Check that indentation is correct after a terminating format declaration." + (cperl-set-style "PBP") ; Make cperl-mode use the same settings as perl-mode. + (cperl--run-test-cases + (ert-resource-file "cperl-bug-35925.pl") + (let ((tab-function + (if (equal cperl-test-mode 'perl-mode) + #'indent-for-tab-command + #'cperl-indent-command))) + (goto-char (point-max)) + (forward-line -2) + (funcall tab-function))) + (cperl-set-style-back)) + (ert-deftest cperl-test-bug-37127 () "Verify that closing a paren in a regex goes without a message. Also check that the message is issued if the regex terminator is -- 2.34.1