aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVisuwesh2024-09-09 20:08:04 +0530
committerEli Zaretskii2024-09-14 12:41:29 +0300
commitdb1eb8a282c1832fd34be049e80dcb1a3b59ade2 (patch)
treee0aed9c7549083dfad9a35bceb651082befbcc21
parent7c767ec781fdba02d073b79113b1d8d89548bb08 (diff)
downloademacs-db1eb8a282c1832fd34be049e80dcb1a3b59ade2.tar.gz
emacs-db1eb8a282c1832fd34be049e80dcb1a3b59ade2.zip
Make the *grep* buffer editable
* lisp/progmodes/compile.el (compilation--update-markers): Factor out function... (compilation-next-error-function): ...from here. Adjust to use the above. * lisp/progmodes/grep.el (grep-edit--prepare-buffer) (grep-edit-mode-map, grep-edit-mode-hook, grep-edit-mode) (grep-change-to-grep-edit-mode, grep-edit-save-changes): Add new 'grep-edit-mode' to make the grep results editable like in 'occur-edit-mode' by using the 'occur' framework. (grep-mode-map): Bind 'e' to the new command 'grep-change-to-grep-edit-mode'. * doc/emacs/building.texi (Grep Searching): Update Info manual to include the above command. * etc/NEWS: Announce the change. (Bug#70820)
-rw-r--r--doc/emacs/building.texi10
-rw-r--r--etc/NEWS9
-rw-r--r--lisp/progmodes/compile.el95
-rw-r--r--lisp/progmodes/grep.el86
4 files changed, 156 insertions, 44 deletions
diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi
index bb03d8cf325..4b2f1ed0649 100644
--- a/doc/emacs/building.texi
+++ b/doc/emacs/building.texi
@@ -528,6 +528,16 @@ grep-find-toggle-abbreviation}. To disable this abbreviation of the
528shell commands, customize the option @code{grep-find-abbreviate} to a 528shell commands, customize the option @code{grep-find-abbreviate} to a
529@code{nil} value. 529@code{nil} value.
530 530
531@findex grep-change-to-grep-edit-mode
532@cindex Grep Edit mode
533@cindex mode, Grep Edit
534 Typing @kbd{e} in the @file{*grep*} buffer makes the buffer writiable
535and enters the Grep Edit mode. Similar to Occur Edit mode (@pxref{Other
536Repeating Search}), you can edit the matching lines reported by
537@code{grep} and have those changes reflected in the buffer visiting the
538originating file. Type @kbd{C-c C-c} to leave the Grep Edit mode and
539return to the Grep mode.
540
531@node Flymake 541@node Flymake
532@section Finding Syntax Errors On The Fly 542@section Finding Syntax Errors On The Fly
533@cindex checking syntax 543@cindex checking syntax
diff --git a/etc/NEWS b/etc/NEWS
index b0d559b2b3f..b2df02b53e2 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -328,6 +328,15 @@ fontifying them, which can be slow for remote directories. Setting
328'dired-check-symlinks' to nil disables these checks. Defaults to t, can 328'dired-check-symlinks' to nil disables these checks. Defaults to t, can
329be set as a connection-local variable. 329be set as a connection-local variable.
330 330
331** Grep
332
333+++
334*** Grep results can be edited to reflect changes in the originating file.
335Like Occur Edit mode, typing 'e' in the '*grep*' buffer will now make
336the 'grep' results editable. The edits will be reflected in the buffer
337visiting the originating file. Typing 'C-c C-c' will leave the Grep
338Edit mode.
339
331 340
332* New Modes and Packages in Emacs 31.1 341* New Modes and Packages in Emacs 31.1
333 342
diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el
index d2e74aa44a6..a78ac1b6462 100644
--- a/lisp/progmodes/compile.el
+++ b/lisp/progmodes/compile.el
@@ -2855,6 +2855,53 @@ as a last resort."
2855 (current-buffer) 2855 (current-buffer)
2856 (next-error-find-buffer avoid-current 'compilation-buffer-internal-p))) 2856 (next-error-find-buffer avoid-current 'compilation-buffer-internal-p)))
2857 2857
2858(defun compilation--update-markers (loc marker screen-columns first-column)
2859 "Update markers in LOC, and set MARKER to location pointed by LOC.
2860SCREEN-COLUMNS and FIRST-COLUMN are the value of
2861`compilation-error-screen-columns' and `compilation-first-column' to use
2862if they are not set buffer-locally in the target buffer."
2863 (with-current-buffer
2864 (if (bufferp (caar (compilation--loc->file-struct loc)))
2865 (caar (compilation--loc->file-struct loc))
2866 (apply #'compilation-find-file
2867 marker
2868 (caar (compilation--loc->file-struct loc))
2869 (cadr (car (compilation--loc->file-struct loc)))
2870 (compilation--file-struct->formats
2871 (compilation--loc->file-struct loc))))
2872 (let ((screen-columns
2873 ;; Obey the compilation-error-screen-columns of the target
2874 ;; buffer if its major mode set it buffer-locally.
2875 (if (local-variable-p 'compilation-error-screen-columns)
2876 compilation-error-screen-columns screen-columns))
2877 (compilation-first-column
2878 (if (local-variable-p 'compilation-first-column)
2879 compilation-first-column first-column))
2880 (last 1))
2881 (save-restriction
2882 (widen)
2883 (goto-char (point-min))
2884 ;; Treat file's found lines in forward order, 1 by 1.
2885 (dolist (line (reverse (cddr (compilation--loc->file-struct loc))))
2886 (when (car line) ; else this is a filename without a line#
2887 (compilation-beginning-of-line (- (car line) last -1))
2888 (setq last (car line)))
2889 ;; Treat line's found columns and store/update a marker for each.
2890 (dolist (col (cdr line))
2891 (if (compilation--loc->col col)
2892 (if (eq (compilation--loc->col col) -1)
2893 ;; Special case for range end.
2894 (end-of-line)
2895 (compilation-move-to-column (compilation--loc->col col)
2896 screen-columns))
2897 (beginning-of-line)
2898 (skip-chars-forward " \t"))
2899 (if (compilation--loc->marker col)
2900 (set-marker (compilation--loc->marker col) (point))
2901 (setf (compilation--loc->marker col) (point-marker)))
2902 ;; (setf (compilation--loc->timestamp col) timestamp)
2903 ))))))
2904
2858;;;###autoload 2905;;;###autoload
2859(defun compilation-next-error-function (n &optional reset) 2906(defun compilation-next-error-function (n &optional reset)
2860 "Advance to the next error message and visit the file where the error was. 2907 "Advance to the next error message and visit the file where the error was.
@@ -2864,7 +2911,6 @@ This is the value of `next-error-function' in Compilation buffers."
2864 (setq compilation-current-error nil)) 2911 (setq compilation-current-error nil))
2865 (let* ((screen-columns compilation-error-screen-columns) 2912 (let* ((screen-columns compilation-error-screen-columns)
2866 (first-column compilation-first-column) 2913 (first-column compilation-first-column)
2867 (last 1)
2868 (msg (compilation-next-error (or n 1) nil 2914 (msg (compilation-next-error (or n 1) nil
2869 (or compilation-current-error 2915 (or compilation-current-error
2870 compilation-messages-start 2916 compilation-messages-start
@@ -2876,9 +2922,9 @@ This is the value of `next-error-function' in Compilation buffers."
2876 (user-error "No next error")) 2922 (user-error "No next error"))
2877 (setq compilation-current-error (point-marker) 2923 (setq compilation-current-error (point-marker)
2878 overlay-arrow-position 2924 overlay-arrow-position
2879 (if (bolp) 2925 (if (bolp)
2880 compilation-current-error 2926 compilation-current-error
2881 (copy-marker (line-beginning-position)))) 2927 (copy-marker (line-beginning-position))))
2882 ;; If loc contains no marker, no error in that file has been visited. 2928 ;; If loc contains no marker, no error in that file has been visited.
2883 ;; If the marker is invalid the buffer has been killed. 2929 ;; If the marker is invalid the buffer has been killed.
2884 ;; So, recalculate all markers for that file. 2930 ;; So, recalculate all markers for that file.
@@ -2895,46 +2941,7 @@ This is the value of `next-error-function' in Compilation buffers."
2895 ;; (equal (compilation--loc->timestamp loc) 2941 ;; (equal (compilation--loc->timestamp loc)
2896 ;; (setq timestamp compilation-buffer-modtime))) 2942 ;; (setq timestamp compilation-buffer-modtime)))
2897 ) 2943 )
2898 (with-current-buffer 2944 (compilation--update-markers loc marker screen-columns first-column))
2899 (if (bufferp (caar (compilation--loc->file-struct loc)))
2900 (caar (compilation--loc->file-struct loc))
2901 (apply #'compilation-find-file
2902 marker
2903 (caar (compilation--loc->file-struct loc))
2904 (cadr (car (compilation--loc->file-struct loc)))
2905 (compilation--file-struct->formats
2906 (compilation--loc->file-struct loc))))
2907 (let ((screen-columns
2908 ;; Obey the compilation-error-screen-columns of the target
2909 ;; buffer if its major mode set it buffer-locally.
2910 (if (local-variable-p 'compilation-error-screen-columns)
2911 compilation-error-screen-columns screen-columns))
2912 (compilation-first-column
2913 (if (local-variable-p 'compilation-first-column)
2914 compilation-first-column first-column)))
2915 (save-restriction
2916 (widen)
2917 (goto-char (point-min))
2918 ;; Treat file's found lines in forward order, 1 by 1.
2919 (dolist (line (reverse (cddr (compilation--loc->file-struct loc))))
2920 (when (car line) ; else this is a filename without a line#
2921 (compilation-beginning-of-line (- (car line) last -1))
2922 (setq last (car line)))
2923 ;; Treat line's found columns and store/update a marker for each.
2924 (dolist (col (cdr line))
2925 (if (compilation--loc->col col)
2926 (if (eq (compilation--loc->col col) -1)
2927 ;; Special case for range end.
2928 (end-of-line)
2929 (compilation-move-to-column (compilation--loc->col col)
2930 screen-columns))
2931 (beginning-of-line)
2932 (skip-chars-forward " \t"))
2933 (if (compilation--loc->marker col)
2934 (set-marker (compilation--loc->marker col) (point))
2935 (setf (compilation--loc->marker col) (point-marker)))
2936 ;; (setf (compilation--loc->timestamp col) timestamp)
2937 ))))))
2938 (compilation-goto-locus marker (compilation--loc->marker loc) 2945 (compilation-goto-locus marker (compilation--loc->marker loc)
2939 (compilation--loc->marker end-loc)) 2946 (compilation--loc->marker end-loc))
2940 (setf (compilation--loc->visited loc) t))) 2947 (setf (compilation--loc->visited loc) t)))
diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el
index d2d0baa235c..54006560224 100644
--- a/lisp/progmodes/grep.el
+++ b/lisp/progmodes/grep.el
@@ -310,6 +310,8 @@ See `compilation-error-screen-columns'."
310 (define-key map "}" #'compilation-next-file) 310 (define-key map "}" #'compilation-next-file)
311 (define-key map "\t" #'compilation-next-error) 311 (define-key map "\t" #'compilation-next-error)
312 (define-key map [backtab] #'compilation-previous-error) 312 (define-key map [backtab] #'compilation-previous-error)
313
314 (define-key map "e" #'grep-change-to-grep-edit-mode)
313 map) 315 map)
314 "Keymap for grep buffers. 316 "Keymap for grep buffers.
315`compilation-minor-mode-map' is a cdr of this.") 317`compilation-minor-mode-map' is a cdr of this.")
@@ -1052,6 +1054,90 @@ list is empty)."
1052 command-args) 1054 command-args)
1053 #'grep-mode)) 1055 #'grep-mode))
1054 1056
1057(defun grep-edit--prepare-buffer ()
1058 "Mark relevant regions read-only, and add relevant occur text-properties."
1059 (save-excursion
1060 (goto-char (point-min))
1061 (let ((inhibit-read-only t)
1062 (dummy (make-marker))
1063 match)
1064 (while (setq match (text-property-search-forward 'compilation-annotation))
1065 (add-text-properties (prop-match-beginning match) (prop-match-end match)
1066 '(read-only t)))
1067 (goto-char (point-min))
1068 (while (setq match (text-property-search-forward 'compilation-message))
1069 (add-text-properties (prop-match-beginning match) (prop-match-end match)
1070 '(read-only t occur-prefix t))
1071 (let ((loc (compilation--message->loc (prop-match-value match)))
1072 m)
1073 ;; Update the markers if necessary.
1074 (unless (and (compilation--loc->marker loc)
1075 (marker-buffer (compilation--loc->marker loc)))
1076 (compilation--update-markers loc dummy compilation-error-screen-columns compilation-first-column))
1077 (setq m (compilation--loc->marker loc))
1078 (add-text-properties (prop-match-beginning match)
1079 (or (next-single-property-change
1080 (prop-match-end match)
1081 'compilation-message)
1082 (1+ (pos-eol)))
1083 `(occur-target ((,m . ,m)))))))))
1084
1085(defvar grep-edit-mode-map
1086 (let ((map (make-sparse-keymap)))
1087 (set-keymap-parent map text-mode-map)
1088 (define-key map (kbd "C-c C-c") #'grep-edit-save-changes)
1089 map)
1090 "Keymap for `grep-edit-mode'.")
1091
1092(defvar grep-edit-mode-hook nil
1093 "Hooks run when changing to Grep-Edit mode.")
1094
1095(defun grep-edit-mode ()
1096 "Major mode for editing *grep* buffers.
1097In this mode, changes to the *grep* buffer are applied to the
1098originating files.
1099\\<grep-edit-mode-map>
1100Type \\[grep-edit-save-changes] to exit Grep-Edit mode, return to Grep
1101mode.
1102
1103The only editable texts in a Grep-Edit buffer are the match results."
1104 (interactive)
1105 (error "This mode can be enabled only by `grep-change-to-grep-edit-mode'"))
1106(put 'grep-edit-mode 'mode-class 'special)
1107
1108(defun grep-change-to-grep-edit-mode ()
1109 "Switch to `grep-edit-mode' to edit *grep* buffer."
1110 (interactive)
1111 (unless (derived-mode-p 'grep-mode)
1112 (error "Not a Grep buffer"))
1113 (when (get-buffer-process (current-buffer))
1114 (error "Cannot switch when grep is running"))
1115 (use-local-map grep-edit-mode-map)
1116 (grep-edit--prepare-buffer)
1117 (setq buffer-read-only nil)
1118 (setq major-mode 'grep-edit-mode)
1119 (setq mode-name "Grep-Edit")
1120 (buffer-enable-undo)
1121 (set-buffer-modified-p nil)
1122 (setq buffer-undo-list nil)
1123 (add-hook 'after-change-functions #'occur-after-change-function nil t)
1124 (run-mode-hooks 'grep-edit-mode-hook)
1125 (message "Editing: \\[grep-edit-save-changes] to return to Grep mode"))
1126
1127(defun grep-edit-save-changes ()
1128 "Switch back to Grep mode."
1129 (interactive)
1130 (unless (derived-mode-p 'grep-edit-mode)
1131 (error "Not a Grep-Edit buffer"))
1132 (remove-hook 'after-change-functions #'occur-after-change-function t)
1133 (use-local-map grep-mode-map)
1134 (setq buffer-read-only t)
1135 (setq major-mode 'grep-mode)
1136 (setq mode-name "Grep")
1137 (force-mode-line-update)
1138 (buffer-disable-undo)
1139 (setq buffer-undo-list t)
1140 (message "Switching to Grep mode"))
1055 1141
1056;;;###autoload 1142;;;###autoload
1057(defun grep-find (command-args) 1143(defun grep-find (command-args)