diff options
| author | Stefan Monnier | 2018-07-10 22:52:21 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2018-07-10 22:52:21 -0400 |
| commit | f8b1e40fb63b0a6bc6692cc0bc84e5f5e65c2644 (patch) | |
| tree | 4aaafe66b5cbfebf4415cbe4d35a10dc44e1ec05 | |
| parent | 1d7151e98e9da5eeb4e341cfdb7d1f4462dc5b70 (diff) | |
| download | emacs-f8b1e40fb63b0a6bc6692cc0bc84e5f5e65c2644.tar.gz emacs-f8b1e40fb63b0a6bc6692cc0bc84e5f5e65c2644.zip | |
* lisp/vc/diff-mode.el: Perform hunk refinement from font-lock
Remove redundant :group arguments.
(diff-font-lock-refine): New var.
(diff--refine-hunk): New function, extracted from diff-refine-hunk.
(diff-refine-hunk): Use it.
(diff--font-lock-refine--refresh): New function.
(diff--font-lock-refined): New function.
(diff-font-lock-keywords): Use it.
| -rw-r--r-- | lisp/vc/diff-mode.el | 206 |
1 files changed, 115 insertions, 91 deletions
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index e88ccece415..ffbd9e5479a 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el | |||
| @@ -66,14 +66,12 @@ | |||
| 66 | 66 | ||
| 67 | (defcustom diff-default-read-only nil | 67 | (defcustom diff-default-read-only nil |
| 68 | "If non-nil, `diff-mode' buffers default to being read-only." | 68 | "If non-nil, `diff-mode' buffers default to being read-only." |
| 69 | :type 'boolean | 69 | :type 'boolean) |
| 70 | :group 'diff-mode) | ||
| 71 | 70 | ||
| 72 | (defcustom diff-jump-to-old-file nil | 71 | (defcustom diff-jump-to-old-file nil |
| 73 | "Non-nil means `diff-goto-source' jumps to the old file. | 72 | "Non-nil means `diff-goto-source' jumps to the old file. |
| 74 | Else, it jumps to the new file." | 73 | Else, it jumps to the new file." |
| 75 | :type 'boolean | 74 | :type 'boolean) |
| 76 | :group 'diff-mode) | ||
| 77 | 75 | ||
| 78 | (defcustom diff-update-on-the-fly t | 76 | (defcustom diff-update-on-the-fly t |
| 79 | "Non-nil means hunk headers are kept up-to-date on-the-fly. | 77 | "Non-nil means hunk headers are kept up-to-date on-the-fly. |
| @@ -82,19 +80,21 @@ need to be kept consistent with the actual diff. This can | |||
| 82 | either be done on the fly (but this sometimes interacts poorly with the | 80 | either be done on the fly (but this sometimes interacts poorly with the |
| 83 | undo mechanism) or whenever the file is written (can be slow | 81 | undo mechanism) or whenever the file is written (can be slow |
| 84 | when editing big diffs)." | 82 | when editing big diffs)." |
| 85 | :type 'boolean | 83 | :type 'boolean) |
| 86 | :group 'diff-mode) | ||
| 87 | 84 | ||
| 88 | (defcustom diff-advance-after-apply-hunk t | 85 | (defcustom diff-advance-after-apply-hunk t |
| 89 | "Non-nil means `diff-apply-hunk' will move to the next hunk after applying." | 86 | "Non-nil means `diff-apply-hunk' will move to the next hunk after applying." |
| 90 | :type 'boolean | 87 | :type 'boolean) |
| 91 | :group 'diff-mode) | ||
| 92 | 88 | ||
| 93 | (defcustom diff-mode-hook nil | 89 | (defcustom diff-mode-hook nil |
| 94 | "Run after setting up the `diff-mode' major mode." | 90 | "Run after setting up the `diff-mode' major mode." |
| 95 | :type 'hook | 91 | :type 'hook |
| 96 | :options '(diff-delete-empty-files diff-make-unified) | 92 | :options '(diff-delete-empty-files diff-make-unified)) |
| 97 | :group 'diff-mode) | 93 | |
| 94 | (defcustom diff-font-lock-refine t | ||
| 95 | "If non-nil, font-lock highlighting includes hunk refinement." | ||
| 96 | :version "27.1" | ||
| 97 | :type 'boolean) | ||
| 98 | 98 | ||
| 99 | (defvar diff-vc-backend nil | 99 | (defvar diff-vc-backend nil |
| 100 | "The VC backend that created the current Diff buffer, if any.") | 100 | "The VC backend that created the current Diff buffer, if any.") |
| @@ -207,8 +207,7 @@ when editing big diffs)." | |||
| 207 | 207 | ||
| 208 | (defcustom diff-minor-mode-prefix "\C-c=" | 208 | (defcustom diff-minor-mode-prefix "\C-c=" |
| 209 | "Prefix key for `diff-minor-mode' commands." | 209 | "Prefix key for `diff-minor-mode' commands." |
| 210 | :type '(choice (string "\e") (string "C-c=") string) | 210 | :type '(choice (string "\e") (string "C-c=") string)) |
| 211 | :group 'diff-mode) | ||
| 212 | 211 | ||
| 213 | (easy-mmode-defmap diff-minor-mode-map | 212 | (easy-mmode-defmap diff-minor-mode-map |
| 214 | `((,diff-minor-mode-prefix . ,diff-mode-shared-map)) | 213 | `((,diff-minor-mode-prefix . ,diff-mode-shared-map)) |
| @@ -238,8 +237,7 @@ well." | |||
| 238 | (((class color)) | 237 | (((class color)) |
| 239 | :foreground "blue1" :weight bold) | 238 | :foreground "blue1" :weight bold) |
| 240 | (t :weight bold)) | 239 | (t :weight bold)) |
| 241 | "`diff-mode' face inherited by hunk and index header faces." | 240 | "`diff-mode' face inherited by hunk and index header faces.") |
| 242 | :group 'diff-mode) | ||
| 243 | 241 | ||
| 244 | (defface diff-file-header | 242 | (defface diff-file-header |
| 245 | '((((class color) (min-colors 88) (background light)) | 243 | '((((class color) (min-colors 88) (background light)) |
| @@ -249,18 +247,15 @@ well." | |||
| 249 | (((class color)) | 247 | (((class color)) |
| 250 | :foreground "cyan" :weight bold) | 248 | :foreground "cyan" :weight bold) |
| 251 | (t :weight bold)) ; :height 1.3 | 249 | (t :weight bold)) ; :height 1.3 |
| 252 | "`diff-mode' face used to highlight file header lines." | 250 | "`diff-mode' face used to highlight file header lines.") |
| 253 | :group 'diff-mode) | ||
| 254 | 251 | ||
| 255 | (defface diff-index | 252 | (defface diff-index |
| 256 | '((t :inherit diff-file-header)) | 253 | '((t :inherit diff-file-header)) |
| 257 | "`diff-mode' face used to highlight index header lines." | 254 | "`diff-mode' face used to highlight index header lines.") |
| 258 | :group 'diff-mode) | ||
| 259 | 255 | ||
| 260 | (defface diff-hunk-header | 256 | (defface diff-hunk-header |
| 261 | '((t :inherit diff-header)) | 257 | '((t :inherit diff-header)) |
| 262 | "`diff-mode' face used to highlight hunk header lines." | 258 | "`diff-mode' face used to highlight hunk header lines.") |
| 263 | :group 'diff-mode) | ||
| 264 | 259 | ||
| 265 | (defface diff-removed | 260 | (defface diff-removed |
| 266 | '((default | 261 | '((default |
| @@ -271,8 +266,7 @@ well." | |||
| 271 | :background "#553333") | 266 | :background "#553333") |
| 272 | (((class color)) | 267 | (((class color)) |
| 273 | :foreground "red")) | 268 | :foreground "red")) |
| 274 | "`diff-mode' face used to highlight removed lines." | 269 | "`diff-mode' face used to highlight removed lines.") |
| 275 | :group 'diff-mode) | ||
| 276 | 270 | ||
| 277 | (defface diff-added | 271 | (defface diff-added |
| 278 | '((default | 272 | '((default |
| @@ -283,40 +277,34 @@ well." | |||
| 283 | :background "#335533") | 277 | :background "#335533") |
| 284 | (((class color)) | 278 | (((class color)) |
| 285 | :foreground "green")) | 279 | :foreground "green")) |
| 286 | "`diff-mode' face used to highlight added lines." | 280 | "`diff-mode' face used to highlight added lines.") |
| 287 | :group 'diff-mode) | ||
| 288 | 281 | ||
| 289 | (defface diff-changed | 282 | (defface diff-changed |
| 290 | '((t nil)) | 283 | '((t nil)) |
| 291 | "`diff-mode' face used to highlight changed lines." | 284 | "`diff-mode' face used to highlight changed lines." |
| 292 | :version "25.1" | 285 | :version "25.1") |
| 293 | :group 'diff-mode) | ||
| 294 | 286 | ||
| 295 | (defface diff-indicator-removed | 287 | (defface diff-indicator-removed |
| 296 | '((t :inherit diff-removed)) | 288 | '((t :inherit diff-removed)) |
| 297 | "`diff-mode' face used to highlight indicator of removed lines (-, <)." | 289 | "`diff-mode' face used to highlight indicator of removed lines (-, <)." |
| 298 | :group 'diff-mode | ||
| 299 | :version "22.1") | 290 | :version "22.1") |
| 300 | (defvar diff-indicator-removed-face 'diff-indicator-removed) | 291 | (defvar diff-indicator-removed-face 'diff-indicator-removed) |
| 301 | 292 | ||
| 302 | (defface diff-indicator-added | 293 | (defface diff-indicator-added |
| 303 | '((t :inherit diff-added)) | 294 | '((t :inherit diff-added)) |
| 304 | "`diff-mode' face used to highlight indicator of added lines (+, >)." | 295 | "`diff-mode' face used to highlight indicator of added lines (+, >)." |
| 305 | :group 'diff-mode | ||
| 306 | :version "22.1") | 296 | :version "22.1") |
| 307 | (defvar diff-indicator-added-face 'diff-indicator-added) | 297 | (defvar diff-indicator-added-face 'diff-indicator-added) |
| 308 | 298 | ||
| 309 | (defface diff-indicator-changed | 299 | (defface diff-indicator-changed |
| 310 | '((t :inherit diff-changed)) | 300 | '((t :inherit diff-changed)) |
| 311 | "`diff-mode' face used to highlight indicator of changed lines." | 301 | "`diff-mode' face used to highlight indicator of changed lines." |
| 312 | :group 'diff-mode | ||
| 313 | :version "22.1") | 302 | :version "22.1") |
| 314 | (defvar diff-indicator-changed-face 'diff-indicator-changed) | 303 | (defvar diff-indicator-changed-face 'diff-indicator-changed) |
| 315 | 304 | ||
| 316 | (defface diff-function | 305 | (defface diff-function |
| 317 | '((t :inherit diff-header)) | 306 | '((t :inherit diff-header)) |
| 318 | "`diff-mode' face used to highlight function names produced by \"diff -p\"." | 307 | "`diff-mode' face used to highlight function names produced by \"diff -p\".") |
| 319 | :group 'diff-mode) | ||
| 320 | 308 | ||
| 321 | (defface diff-context | 309 | (defface diff-context |
| 322 | '((((class color grayscale) (min-colors 88) (background light)) | 310 | '((((class color grayscale) (min-colors 88) (background light)) |
| @@ -324,13 +312,11 @@ well." | |||
| 324 | (((class color grayscale) (min-colors 88) (background dark)) | 312 | (((class color grayscale) (min-colors 88) (background dark)) |
| 325 | :foreground "#dddddd")) | 313 | :foreground "#dddddd")) |
| 326 | "`diff-mode' face used to highlight context and other side-information." | 314 | "`diff-mode' face used to highlight context and other side-information." |
| 327 | :version "25.1" | 315 | :version "25.1") |
| 328 | :group 'diff-mode) | ||
| 329 | 316 | ||
| 330 | (defface diff-nonexistent | 317 | (defface diff-nonexistent |
| 331 | '((t :inherit diff-file-header)) | 318 | '((t :inherit diff-file-header)) |
| 332 | "`diff-mode' face used to highlight nonexistent files in recursive diffs." | 319 | "`diff-mode' face used to highlight nonexistent files in recursive diffs.") |
| 333 | :group 'diff-mode) | ||
| 334 | 320 | ||
| 335 | (defconst diff-yank-handler '(diff-yank-function)) | 321 | (defconst diff-yank-handler '(diff-yank-function)) |
| 336 | (defun diff-yank-function (text) | 322 | (defun diff-yank-function (text) |
| @@ -409,7 +395,8 @@ and the face `diff-added' for added lines.") | |||
| 409 | ("^\\(#\\)\\(.*\\)" | 395 | ("^\\(#\\)\\(.*\\)" |
| 410 | (1 font-lock-comment-delimiter-face) | 396 | (1 font-lock-comment-delimiter-face) |
| 411 | (2 font-lock-comment-face)) | 397 | (2 font-lock-comment-face)) |
| 412 | ("^[^-=+*!<>#].*\n" (0 'diff-context)))) | 398 | ("^[^-=+*!<>#].*\n" (0 'diff-context)) |
| 399 | (,#'diff--font-lock-refined))) | ||
| 413 | 400 | ||
| 414 | (defconst diff-font-lock-defaults | 401 | (defconst diff-font-lock-defaults |
| 415 | '(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil))) | 402 | '(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil))) |
| @@ -1964,8 +1951,7 @@ For use in `add-log-current-defun-function'." | |||
| 1964 | (((class color) (min-colors 88) (background dark)) | 1951 | (((class color) (min-colors 88) (background dark)) |
| 1965 | :background "#aaaa22") | 1952 | :background "#aaaa22") |
| 1966 | (t :inverse-video t)) | 1953 | (t :inverse-video t)) |
| 1967 | "Face used for char-based changes shown by `diff-refine-hunk'." | 1954 | "Face used for char-based changes shown by `diff-refine-hunk'.") |
| 1968 | :group 'diff-mode) | ||
| 1969 | 1955 | ||
| 1970 | (defface diff-refine-removed | 1956 | (defface diff-refine-removed |
| 1971 | '((default | 1957 | '((default |
| @@ -1975,7 +1961,6 @@ For use in `add-log-current-defun-function'." | |||
| 1975 | (((class color) (min-colors 88) (background dark)) | 1961 | (((class color) (min-colors 88) (background dark)) |
| 1976 | :background "#aa2222")) | 1962 | :background "#aa2222")) |
| 1977 | "Face used for removed characters shown by `diff-refine-hunk'." | 1963 | "Face used for removed characters shown by `diff-refine-hunk'." |
| 1978 | :group 'diff-mode | ||
| 1979 | :version "24.3") | 1964 | :version "24.3") |
| 1980 | 1965 | ||
| 1981 | (defface diff-refine-added | 1966 | (defface diff-refine-added |
| @@ -1986,7 +1971,6 @@ For use in `add-log-current-defun-function'." | |||
| 1986 | (((class color) (min-colors 88) (background dark)) | 1971 | (((class color) (min-colors 88) (background dark)) |
| 1987 | :background "#22aa22")) | 1972 | :background "#22aa22")) |
| 1988 | "Face used for added characters shown by `diff-refine-hunk'." | 1973 | "Face used for added characters shown by `diff-refine-hunk'." |
| 1989 | :group 'diff-mode | ||
| 1990 | :version "24.3") | 1974 | :version "24.3") |
| 1991 | 1975 | ||
| 1992 | (defun diff-refine-preproc () | 1976 | (defun diff-refine-preproc () |
| @@ -2013,59 +1997,99 @@ Return new point, if it was moved." | |||
| 2013 | (defun diff-refine-hunk () | 1997 | (defun diff-refine-hunk () |
| 2014 | "Highlight changes of hunk at point at a finer granularity." | 1998 | "Highlight changes of hunk at point at a finer granularity." |
| 2015 | (interactive) | 1999 | (interactive) |
| 2016 | (require 'smerge-mode) | ||
| 2017 | (when (diff--some-hunks-p) | 2000 | (when (diff--some-hunks-p) |
| 2018 | (save-excursion | 2001 | (save-excursion |
| 2019 | (diff-beginning-of-hunk t) | 2002 | (let ((beg (diff-beginning-of-hunk t)) |
| 2020 | (let* ((start (point)) | 2003 | ;; Be careful to start from the hunk header so diff-end-of-hunk |
| 2021 | (style (diff-hunk-style)) ;Skips the hunk header as well. | 2004 | ;; gets to read the hunk header's line info. |
| 2022 | (beg (point)) | 2005 | (end (progn (diff-end-of-hunk) (point)))) |
| 2023 | (props-c '((diff-mode . fine) (face diff-refine-changed))) | 2006 | (diff--refine-hunk beg end))))) |
| 2024 | (props-r '((diff-mode . fine) (face diff-refine-removed))) | 2007 | |
| 2025 | (props-a '((diff-mode . fine) (face diff-refine-added))) | 2008 | (defun diff--refine-hunk (start end) |
| 2026 | ;; Be careful to go back to `start' so diff-end-of-hunk gets | 2009 | (require 'smerge-mode) |
| 2027 | ;; to read the hunk header's line info. | 2010 | (goto-char start) |
| 2028 | (end (progn (goto-char start) (diff-end-of-hunk) (point)))) | 2011 | (let* ((style (diff-hunk-style)) ;Skips the hunk header as well. |
| 2029 | 2012 | (beg (point)) | |
| 2030 | (remove-overlays beg end 'diff-mode 'fine) | 2013 | (props-c '((diff-mode . fine) (face . diff-refine-changed))) |
| 2031 | 2014 | (props-r '((diff-mode . fine) (face . diff-refine-removed))) | |
| 2032 | (goto-char beg) | 2015 | (props-a '((diff-mode . fine) (face . diff-refine-added)))) |
| 2033 | (pcase style | 2016 | |
| 2034 | (`unified | 2017 | (remove-overlays beg end 'diff-mode 'fine) |
| 2035 | (while (re-search-forward "^-" end t) | 2018 | |
| 2036 | (let ((beg-del (progn (beginning-of-line) (point))) | 2019 | (goto-char beg) |
| 2037 | beg-add end-add) | 2020 | (pcase style |
| 2038 | (when (and (diff--forward-while-leading-char ?- end) | 2021 | (`unified |
| 2039 | ;; Allow for "\ No newline at end of file". | 2022 | (while (re-search-forward "^-" end t) |
| 2040 | (progn (diff--forward-while-leading-char ?\\ end) | 2023 | (let ((beg-del (progn (beginning-of-line) (point))) |
| 2041 | (setq beg-add (point))) | 2024 | beg-add end-add) |
| 2042 | (diff--forward-while-leading-char ?+ end) | 2025 | (when (and (diff--forward-while-leading-char ?- end) |
| 2043 | (progn (diff--forward-while-leading-char ?\\ end) | 2026 | ;; Allow for "\ No newline at end of file". |
| 2044 | (setq end-add (point)))) | 2027 | (progn (diff--forward-while-leading-char ?\\ end) |
| 2045 | (smerge-refine-regions beg-del beg-add beg-add end-add | 2028 | (setq beg-add (point))) |
| 2046 | nil #'diff-refine-preproc props-r props-a))))) | 2029 | (diff--forward-while-leading-char ?+ end) |
| 2047 | (`context | 2030 | (progn (diff--forward-while-leading-char ?\\ end) |
| 2048 | (let* ((middle (save-excursion (re-search-forward "^---"))) | 2031 | (setq end-add (point)))) |
| 2049 | (other middle)) | 2032 | (smerge-refine-regions beg-del beg-add beg-add end-add |
| 2050 | (while (re-search-forward "^\\(?:!.*\n\\)+" middle t) | 2033 | nil #'diff-refine-preproc props-r props-a))))) |
| 2051 | (smerge-refine-regions (match-beginning 0) (match-end 0) | 2034 | (`context |
| 2052 | (save-excursion | 2035 | (let* ((middle (save-excursion (re-search-forward "^---"))) |
| 2053 | (goto-char other) | 2036 | (other middle)) |
| 2054 | (re-search-forward "^\\(?:!.*\n\\)+" end) | 2037 | (while (re-search-forward "^\\(?:!.*\n\\)+" middle t) |
| 2055 | (setq other (match-end 0)) | 2038 | (smerge-refine-regions (match-beginning 0) (match-end 0) |
| 2056 | (match-beginning 0)) | 2039 | (save-excursion |
| 2057 | other | 2040 | (goto-char other) |
| 2058 | (if diff-use-changed-face props-c) | 2041 | (re-search-forward "^\\(?:!.*\n\\)+" end) |
| 2059 | #'diff-refine-preproc | 2042 | (setq other (match-end 0)) |
| 2060 | (unless diff-use-changed-face props-r) | 2043 | (match-beginning 0)) |
| 2061 | (unless diff-use-changed-face props-a))))) | 2044 | other |
| 2062 | (_ ;; Normal diffs. | 2045 | (if diff-use-changed-face props-c) |
| 2063 | (let ((beg1 (1+ (point)))) | 2046 | #'diff-refine-preproc |
| 2064 | (when (re-search-forward "^---.*\n" end t) | 2047 | (unless diff-use-changed-face props-r) |
| 2065 | ;; It's a combined add&remove, so there's something to do. | 2048 | (unless diff-use-changed-face props-a))))) |
| 2066 | (smerge-refine-regions beg1 (match-beginning 0) | 2049 | (_ ;; Normal diffs. |
| 2067 | (match-end 0) end | 2050 | (let ((beg1 (1+ (point)))) |
| 2068 | nil #'diff-refine-preproc props-r props-a))))))))) | 2051 | (when (re-search-forward "^---.*\n" end t) |
| 2052 | ;; It's a combined add&remove, so there's something to do. | ||
| 2053 | (smerge-refine-regions beg1 (match-beginning 0) | ||
| 2054 | (match-end 0) end | ||
| 2055 | nil #'diff-refine-preproc props-r props-a))))))) | ||
| 2056 | |||
| 2057 | (defun diff--font-lock-refined (max) | ||
| 2058 | "Apply hunk refinement from font-lock." | ||
| 2059 | (when diff-font-lock-refine | ||
| 2060 | (when (get-char-property (point) 'diff--font-lock-refined) | ||
| 2061 | ;; Refinement works over a complete hunk, whereas font-lock limits itself | ||
| 2062 | ;; to highlighting smallish chunks between point..max, so we may be | ||
| 2063 | ;; called N times for a large hunk in which case we don't want to | ||
| 2064 | ;; rehighlight that hunk N times (especially since each highlighting | ||
| 2065 | ;; of a large hunk can itself take a long time, adding insult to injury). | ||
| 2066 | ;; So, after refining a hunk (including a failed attempt), we place an | ||
| 2067 | ;; overlay over the whole hunk to mark it as refined, to avoid redoing | ||
| 2068 | ;; the job redundantly when asked to highlight subsequent parts of the | ||
| 2069 | ;; same hunk. | ||
| 2070 | (goto-char (next-single-char-property-change | ||
| 2071 | (point) 'diff--font-lock-refined nil max))) | ||
| 2072 | (let* ((min (point)) | ||
| 2073 | (beg (or (ignore-errors (diff-beginning-of-hunk)) | ||
| 2074 | (ignore-errors (diff-hunk-next) (point)) | ||
| 2075 | max))) | ||
| 2076 | (while (< beg max) | ||
| 2077 | (let ((end | ||
| 2078 | (save-excursion (goto-char beg) (diff-end-of-hunk) (point)))) | ||
| 2079 | (if (< end min) (setq beg min)) | ||
| 2080 | (unless (or (< end beg) | ||
| 2081 | (get-char-property beg 'diff--font-lock-refined)) | ||
| 2082 | (diff--refine-hunk beg end) | ||
| 2083 | (let ((ol (make-overlay beg end))) | ||
| 2084 | (overlay-put ol 'diff--font-lock-refined t) | ||
| 2085 | (overlay-put ol 'evaporate t) | ||
| 2086 | (overlay-put ol 'modification-hooks | ||
| 2087 | '(diff--font-lock-refine--refresh)))) | ||
| 2088 | (goto-char (max beg end)) | ||
| 2089 | (setq beg (or (ignore-errors (diff-hunk-next) (point)) max))))))) | ||
| 2090 | |||
| 2091 | (defun diff--font-lock-refine--refresh (ol _after _beg _end &optional _len) | ||
| 2092 | (delete-overlay ol)) | ||
| 2069 | 2093 | ||
| 2070 | (defun diff-undo (&optional arg) | 2094 | (defun diff-undo (&optional arg) |
| 2071 | "Perform `undo', ignoring the buffer's read-only status." | 2095 | "Perform `undo', ignoring the buffer's read-only status." |