master 8b8aa7ced6b: Delete empty local-variable block ad prop line
Eli Zaretskii <[email protected]> Thu, 30 Jul 2026 10:39:26 -0400 (EDT)
| Newsgroups | gmane.emacs.diffs |
|---|---|
| Message-ID | <[email protected]> |
branch: master commit 8b8aa7ced6ba8b85e8aa057c592c85d5aa16ebf9 Author: Daniel Mendler <[email protected]> Commit: Eli Zaretskii <[email protected]> Delete empty local-variable block ad prop line Delete local variable block or prop line when it became empty after deleting a variable. (Bug#81516) * lisp/files-x.el (modify-file-local-variable) (modify-file-local-variable-prop-line): Delete empty block or prop-line. * test/lisp/files-x-tests.el (files-x-test-add-file-local-variable) (files-x-test-add-file-local-variable-prop-line) (files-x-test-delete-file-local-variable) (files-x-test-delete-file-local-variable-prop-line): New tests. --- lisp/files-x.el | 43 ++++++++++- test/lisp/files-x-tests.el | 189 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 4 deletions(-) diff --git a/lisp/files-x.el b/lisp/files-x.el index 0d8c1d96a9f..4aee38b856e 100644 --- a/lisp/files-x.el +++ b/lisp/files-x.el @@ -201,7 +201,8 @@ the user how to make the new value take effect." (match-beginning 0))) (suffix (buffer-substring (point) (line-end-position))) (prefix-re (concat "^" (regexp-quote prefix))) - (suffix-re (concat (regexp-quote suffix) "$"))) + (suffix-re (concat (regexp-quote suffix) "$")) + (deleted nil)) ;; Find or add missing "End:". (forward-line 1) @@ -224,7 +225,17 @@ the user how to make the new value take effect." (while (re-search-forward (format "%s%S:.*%s" prefix-re variable suffix-re) end t) (delete-region (match-beginning 0) (1+ (match-end 0))) - (setq replaced-pos (point))))) + (setq replaced-pos (point) + deleted t)) + ;; Delete empty local variables block. + (when (and (eq op 'delete) deleted (= beg end)) + (save-excursion + (delete-region (progn (goto-char beg) + (forward-line -1) + (point)) + (progn (goto-char end) + (forward-line 1) + (point))))))) ;; Add a new variable/value pair. Add `mode' to the start, add new ;; variable to the end, and add a replaced variable to its last location. @@ -295,7 +306,7 @@ from the -*- line ignoring the input argument VALUE. If optional variable INTERACTIVE is non-nil, display a message telling the user how to make the new value take effect." (catch 'exit - (let ((beg (point)) end replaced-pos) + (let ((beg (point)) end replaced-pos deleted) (unless enable-local-variables (throw 'exit (message "File-local variables are disabled"))) @@ -357,6 +368,7 @@ the user how to make the new value take effect." ;; Replace or delete MODENAME (progn (when (member op '(add-or-replace delete)) + (setq deleted t) (delete-region (match-beginning 1) (match-end 1))) (when (eq op 'add-or-replace) (goto-char (match-beginning 1)) @@ -387,7 +399,8 @@ the user how to make the new value take effect." (skip-chars-forward " \t;") (when (eq key variable) (delete-region (match-beginning 0) (point)) - (setq replaced-pos (point))))))) + (setq replaced-pos (point) + deleted t)))))) ;; Add a new variable/value pair. Add `mode' to the start, add new ;; variable to the end, and add a replaced variable to its last location. (when (eq op 'add-or-replace) @@ -406,6 +419,28 @@ the user how to make the new value take effect." (insert (format "%S: %S;" variable value)) (unless (eq (char-after) ?\s) (insert " "))))) + ;; Delete empty prop-line. + (when (and (eq op 'delete) deleted (= beg end)) + (save-excursion + ;; First delete -*- -*- + (delete-region + (progn + (goto-char beg) + (search-backward "-*-" (line-beginning-position)) + (point)) + (progn + (goto-char end) + (search-forward "-*-" (line-end-position)) + (point))) + ;; And then delete line if comment is empty. + (goto-char (line-beginning-position)) + (when (looking-at-p + (concat + "^[ \t]*\\(?:" (regexp-quote (or comment-start ";")) + "\\)+[ \t]*" (regexp-quote (or comment-end "")) + "[ \t]*$")) + (delete-region (point) (progn (forward-line 1) (point)))))) + (when interactive (modify-file-local-variable-message variable value op))))) diff --git a/test/lisp/files-x-tests.el b/test/lisp/files-x-tests.el index 31633df073e..e96b61a3944 100644 --- a/test/lisp/files-x-tests.el +++ b/test/lisp/files-x-tests.el @@ -638,5 +638,194 @@ or `executable-find', their default value must be used nonetheless." `(connection-local-profile-alist ',clpa now) `(connection-local-criteria-alist ',clca now))))) +(ert-deftest files-x-test-add-file-local-variable () + "Test adding and replacing file local variables." + ;; Simple adding + (with-temp-buffer + (add-file-local-variable 'foo 1) + (add-file-local-variable 'bar 2) + (should (equal (buffer-string) + "\n;; Local Variables:\n;; foo: 1\n;; bar: 2\n;; End:\n"))) + ;; Adding in c-mode + (with-temp-buffer + (c-mode) + (setq-local comment-start "/* " comment-end " */") + (add-file-local-variable 'foo 1) + (add-file-local-variable 'bar 2) + (should (equal (buffer-string) + (concat "\n/* Local Variables: */\n" + "/* foo: 1 */\n" + "/* bar: 2 */\n" + "/* End: */\n")))) + ;; Replacing + (with-temp-buffer + (add-file-local-variable 'foo 1) + (add-file-local-variable 'bar 2) + (add-file-local-variable 'foo t) + (add-file-local-variable 'bar nil) + (should (equal (buffer-string) + "\n;; Local Variables:\n;; foo: t\n;; bar: nil\n;; End:\n"))) + ;; Replacing with prefix/suffix + (with-temp-buffer + (insert ";; PRE Local Variables: SUF\n" + ";; PRE foo: t SUF\n" + ";; PRE bar: nil SUF\n" + ";; PRE End: SUF\n") + (add-file-local-variable 'foo t) + (add-file-local-variable 'bar nil) + (should (equal (buffer-string) + (concat ";; PRE Local Variables: SUF\n" + ";; PRE foo: t SUF\n" + ";; PRE bar: nil SUF\n" + ";; PRE End: SUF\n"))))) + +(ert-deftest files-x-test-delete-file-local-variable () + "Test deleting file local variables." + ;; Simple deleting + (with-temp-buffer + (setq-local comment-start ";;" comment-end "") + (insert "BEFORE\n" + ";; Local Variables:\n" + ";; foo: t\n" + ";; bar: nil\n" + ";; End:\n" + "AFTER") + (delete-file-local-variable 'foo) + (should (equal (buffer-string) + (concat "BEFORE\n" + ";; Local Variables:\n" + ";; bar: nil\n" + ";; End:\n" + "AFTER"))) + (delete-file-local-variable 'bar) + (should (equal (buffer-string) "BEFORE\nAFTER"))) + ;; Deleting in c-mode + (with-temp-buffer + (c-mode) + (setq-local comment-start "/* " comment-end " */") + (insert "BEFORE\n" + "/* Local Variables: */\n" + "/* foo: t */\n" + "/* bar: nil */\n" + "/* End: */\n" + "AFTER") + (delete-file-local-variable 'foo) + (should (equal (buffer-string) + (concat "BEFORE\n" + "/* Local Variables: */\n" + "/* bar: nil */\n" + "/* End: */\n" + "AFTER"))) + (delete-file-local-variable 'bar) + (should (equal (buffer-string) "BEFORE\nAFTER"))) + ;; Ensure that other blocks are untouched + (with-temp-buffer + (setq-local comment-start ";;" comment-end "") + (insert ";; Local Variables:\n" + ";; foo: t\n" + ";; End:\n" + ";; Local Variables:\n" + ";; PRESERVE\n" + ";; End:\n") + (delete-file-local-variable 'foo) + (should (equal (buffer-string) + ";; Local Variables:\n;; PRESERVE\n;; End:\n"))) + ;; Deleting with prefix/suffix + (with-temp-buffer + (setq-local comment-start ";;" comment-end "") + (insert "BEFORE\n" + ";; PRE Local Variables: SUF\n" + ";; PRE foo: t SUF\n" + ";; PRE bar: nil SUF\n" + ";; PRE End: SUF\n" + "AFTER") + (delete-file-local-variable 'foo) + (should (equal (buffer-string) + (concat "BEFORE\n" + ";; PRE Local Variables: SUF\n" + ";; PRE bar: nil SUF\n" + ";; PRE End: SUF\n" + "AFTER"))) + (delete-file-local-variable 'bar) + (should (equal (buffer-string) "BEFORE\nAFTER")))) + +(ert-deftest files-x-test-add-file-local-variable-prop-line () + "Test adding and replacing file local variables in the prop-line." + ;; Simple adding + (with-temp-buffer + (emacs-lisp-mode) + (add-file-local-variable-prop-line 'foo 1) + (add-file-local-variable-prop-line 'bar 2) + (should (equal (buffer-string) ";; -*- foo: 1; bar: 2; -*-\n"))) + ;; Adding in c-mode + (with-temp-buffer + (c-mode) + (setq-local comment-start "/* " comment-end " */") + (add-file-local-variable-prop-line 'foo 1) + (add-file-local-variable-prop-line 'bar 2) + (should (equal (buffer-string) "/* -*- foo: 1; bar: 2; -*- */\n"))) + ;; Adding to mode variable + (with-temp-buffer + (emacs-lisp-mode) + (insert ";; -*- org-mode -*-\n") + (add-file-local-variable-prop-line 'foo 1) + (should (equal (buffer-string) ";; -*- mode: org-mode; foo: 1; -*-\n"))) + ;; Simple replacing + (with-temp-buffer + (emacs-lisp-mode) + (add-file-local-variable-prop-line 'foo 1) + (add-file-local-variable-prop-line 'bar 2) + (add-file-local-variable-prop-line 'foo t) + (add-file-local-variable-prop-line 'bar nil) + (should (equal (buffer-string) ";; -*- foo: t; bar: nil; -*-\n"))) + ;; Replacing with prefix/suffix + (with-temp-buffer + (emacs-lisp-mode) + (insert ";; PRE -*- foo: 1; bar: 2; -*- SUF\n") + (add-file-local-variable-prop-line 'foo t) + (add-file-local-variable-prop-line 'bar nil) + (should (equal (buffer-string) + ";; PRE -*- foo: t; bar: nil; -*- SUF\n")))) + +(ert-deftest files-x-test-delete-file-local-variable-prop-line () + "Test deleting file local variables in prop-line." + ;; Simple deleting + (with-temp-buffer + (emacs-lisp-mode) + (insert ";; -*- foo: t; bar: nil -*-\n") + (delete-file-local-variable-prop-line 'foo) + (should (equal (buffer-string) ";; -*- bar: nil -*-\n")) + (delete-file-local-variable-prop-line 'bar) + (should (equal (buffer-string) ""))) + ;; Deleting mode variable + (with-temp-buffer + (emacs-lisp-mode) + (insert ";; -*- org-mode -*-\n") + (delete-file-local-variable-prop-line 'mode) + (should (equal (buffer-string) ""))) + ;; Deleting in c-mode + (with-temp-buffer + (c-mode) + (setq-local comment-start "/* " comment-end " */") + (insert "/* -*- foo: t; bar: nil -*- */\n") + (delete-file-local-variable-prop-line 'foo) + (should (equal (buffer-string) "/* -*- bar: nil -*- */\n")) + (delete-file-local-variable-prop-line 'bar) + (should (equal (buffer-string) ""))) + ;; Ensure that other prop lines are untouched + (with-temp-buffer + (emacs-lisp-mode) + (insert ";; -*- foo: t -*-\n;; -*- -*-") + (delete-file-local-variable-prop-line 'foo) + (should (equal (buffer-string) ";; -*- -*-"))) + ;; Deleting with prefix/suffix + (with-temp-buffer + (emacs-lisp-mode) + (insert ";; PRE -*- foo: t; bar: nil -*- SUF\n") + (delete-file-local-variable-prop-line 'foo) + (should (equal (buffer-string) ";; PRE -*- bar: nil -*- SUF\n")) + (delete-file-local-variable-prop-line 'bar) + (should (equal (buffer-string) ";; PRE SUF\n")))) + (provide 'files-x-tests) ;;; files-x-tests.el ends here