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)
This commit is contained in:
parent
7c767ec781
commit
db1eb8a282
4 changed files with 156 additions and 44 deletions
|
@ -528,6 +528,16 @@ grep-find-toggle-abbreviation}. To disable this abbreviation of the
|
|||
shell commands, customize the option @code{grep-find-abbreviate} to a
|
||||
@code{nil} value.
|
||||
|
||||
@findex grep-change-to-grep-edit-mode
|
||||
@cindex Grep Edit mode
|
||||
@cindex mode, Grep Edit
|
||||
Typing @kbd{e} in the @file{*grep*} buffer makes the buffer writiable
|
||||
and enters the Grep Edit mode. Similar to Occur Edit mode (@pxref{Other
|
||||
Repeating Search}), you can edit the matching lines reported by
|
||||
@code{grep} and have those changes reflected in the buffer visiting the
|
||||
originating file. Type @kbd{C-c C-c} to leave the Grep Edit mode and
|
||||
return to the Grep mode.
|
||||
|
||||
@node Flymake
|
||||
@section Finding Syntax Errors On The Fly
|
||||
@cindex checking syntax
|
||||
|
|
9
etc/NEWS
9
etc/NEWS
|
@ -328,6 +328,15 @@ fontifying them, which can be slow for remote directories. Setting
|
|||
'dired-check-symlinks' to nil disables these checks. Defaults to t, can
|
||||
be set as a connection-local variable.
|
||||
|
||||
** Grep
|
||||
|
||||
+++
|
||||
*** Grep results can be edited to reflect changes in the originating file.
|
||||
Like Occur Edit mode, typing 'e' in the '*grep*' buffer will now make
|
||||
the 'grep' results editable. The edits will be reflected in the buffer
|
||||
visiting the originating file. Typing 'C-c C-c' will leave the Grep
|
||||
Edit mode.
|
||||
|
||||
|
||||
* New Modes and Packages in Emacs 31.1
|
||||
|
||||
|
|
|
@ -2855,6 +2855,53 @@ as a last resort."
|
|||
(current-buffer)
|
||||
(next-error-find-buffer avoid-current 'compilation-buffer-internal-p)))
|
||||
|
||||
(defun compilation--update-markers (loc marker screen-columns first-column)
|
||||
"Update markers in LOC, and set MARKER to location pointed by LOC.
|
||||
SCREEN-COLUMNS and FIRST-COLUMN are the value of
|
||||
`compilation-error-screen-columns' and `compilation-first-column' to use
|
||||
if they are not set buffer-locally in the target buffer."
|
||||
(with-current-buffer
|
||||
(if (bufferp (caar (compilation--loc->file-struct loc)))
|
||||
(caar (compilation--loc->file-struct loc))
|
||||
(apply #'compilation-find-file
|
||||
marker
|
||||
(caar (compilation--loc->file-struct loc))
|
||||
(cadr (car (compilation--loc->file-struct loc)))
|
||||
(compilation--file-struct->formats
|
||||
(compilation--loc->file-struct loc))))
|
||||
(let ((screen-columns
|
||||
;; Obey the compilation-error-screen-columns of the target
|
||||
;; buffer if its major mode set it buffer-locally.
|
||||
(if (local-variable-p 'compilation-error-screen-columns)
|
||||
compilation-error-screen-columns screen-columns))
|
||||
(compilation-first-column
|
||||
(if (local-variable-p 'compilation-first-column)
|
||||
compilation-first-column first-column))
|
||||
(last 1))
|
||||
(save-restriction
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
;; Treat file's found lines in forward order, 1 by 1.
|
||||
(dolist (line (reverse (cddr (compilation--loc->file-struct loc))))
|
||||
(when (car line) ; else this is a filename without a line#
|
||||
(compilation-beginning-of-line (- (car line) last -1))
|
||||
(setq last (car line)))
|
||||
;; Treat line's found columns and store/update a marker for each.
|
||||
(dolist (col (cdr line))
|
||||
(if (compilation--loc->col col)
|
||||
(if (eq (compilation--loc->col col) -1)
|
||||
;; Special case for range end.
|
||||
(end-of-line)
|
||||
(compilation-move-to-column (compilation--loc->col col)
|
||||
screen-columns))
|
||||
(beginning-of-line)
|
||||
(skip-chars-forward " \t"))
|
||||
(if (compilation--loc->marker col)
|
||||
(set-marker (compilation--loc->marker col) (point))
|
||||
(setf (compilation--loc->marker col) (point-marker)))
|
||||
;; (setf (compilation--loc->timestamp col) timestamp)
|
||||
))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun compilation-next-error-function (n &optional reset)
|
||||
"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."
|
|||
(setq compilation-current-error nil))
|
||||
(let* ((screen-columns compilation-error-screen-columns)
|
||||
(first-column compilation-first-column)
|
||||
(last 1)
|
||||
(msg (compilation-next-error (or n 1) nil
|
||||
(or compilation-current-error
|
||||
compilation-messages-start
|
||||
|
@ -2876,9 +2922,9 @@ This is the value of `next-error-function' in Compilation buffers."
|
|||
(user-error "No next error"))
|
||||
(setq compilation-current-error (point-marker)
|
||||
overlay-arrow-position
|
||||
(if (bolp)
|
||||
compilation-current-error
|
||||
(copy-marker (line-beginning-position))))
|
||||
(if (bolp)
|
||||
compilation-current-error
|
||||
(copy-marker (line-beginning-position))))
|
||||
;; If loc contains no marker, no error in that file has been visited.
|
||||
;; If the marker is invalid the buffer has been killed.
|
||||
;; So, recalculate all markers for that file.
|
||||
|
@ -2895,46 +2941,7 @@ This is the value of `next-error-function' in Compilation buffers."
|
|||
;; (equal (compilation--loc->timestamp loc)
|
||||
;; (setq timestamp compilation-buffer-modtime)))
|
||||
)
|
||||
(with-current-buffer
|
||||
(if (bufferp (caar (compilation--loc->file-struct loc)))
|
||||
(caar (compilation--loc->file-struct loc))
|
||||
(apply #'compilation-find-file
|
||||
marker
|
||||
(caar (compilation--loc->file-struct loc))
|
||||
(cadr (car (compilation--loc->file-struct loc)))
|
||||
(compilation--file-struct->formats
|
||||
(compilation--loc->file-struct loc))))
|
||||
(let ((screen-columns
|
||||
;; Obey the compilation-error-screen-columns of the target
|
||||
;; buffer if its major mode set it buffer-locally.
|
||||
(if (local-variable-p 'compilation-error-screen-columns)
|
||||
compilation-error-screen-columns screen-columns))
|
||||
(compilation-first-column
|
||||
(if (local-variable-p 'compilation-first-column)
|
||||
compilation-first-column first-column)))
|
||||
(save-restriction
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
;; Treat file's found lines in forward order, 1 by 1.
|
||||
(dolist (line (reverse (cddr (compilation--loc->file-struct loc))))
|
||||
(when (car line) ; else this is a filename without a line#
|
||||
(compilation-beginning-of-line (- (car line) last -1))
|
||||
(setq last (car line)))
|
||||
;; Treat line's found columns and store/update a marker for each.
|
||||
(dolist (col (cdr line))
|
||||
(if (compilation--loc->col col)
|
||||
(if (eq (compilation--loc->col col) -1)
|
||||
;; Special case for range end.
|
||||
(end-of-line)
|
||||
(compilation-move-to-column (compilation--loc->col col)
|
||||
screen-columns))
|
||||
(beginning-of-line)
|
||||
(skip-chars-forward " \t"))
|
||||
(if (compilation--loc->marker col)
|
||||
(set-marker (compilation--loc->marker col) (point))
|
||||
(setf (compilation--loc->marker col) (point-marker)))
|
||||
;; (setf (compilation--loc->timestamp col) timestamp)
|
||||
))))))
|
||||
(compilation--update-markers loc marker screen-columns first-column))
|
||||
(compilation-goto-locus marker (compilation--loc->marker loc)
|
||||
(compilation--loc->marker end-loc))
|
||||
(setf (compilation--loc->visited loc) t)))
|
||||
|
|
|
@ -310,6 +310,8 @@ See `compilation-error-screen-columns'."
|
|||
(define-key map "}" #'compilation-next-file)
|
||||
(define-key map "\t" #'compilation-next-error)
|
||||
(define-key map [backtab] #'compilation-previous-error)
|
||||
|
||||
(define-key map "e" #'grep-change-to-grep-edit-mode)
|
||||
map)
|
||||
"Keymap for grep buffers.
|
||||
`compilation-minor-mode-map' is a cdr of this.")
|
||||
|
@ -1052,6 +1054,90 @@ list is empty)."
|
|||
command-args)
|
||||
#'grep-mode))
|
||||
|
||||
(defun grep-edit--prepare-buffer ()
|
||||
"Mark relevant regions read-only, and add relevant occur text-properties."
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let ((inhibit-read-only t)
|
||||
(dummy (make-marker))
|
||||
match)
|
||||
(while (setq match (text-property-search-forward 'compilation-annotation))
|
||||
(add-text-properties (prop-match-beginning match) (prop-match-end match)
|
||||
'(read-only t)))
|
||||
(goto-char (point-min))
|
||||
(while (setq match (text-property-search-forward 'compilation-message))
|
||||
(add-text-properties (prop-match-beginning match) (prop-match-end match)
|
||||
'(read-only t occur-prefix t))
|
||||
(let ((loc (compilation--message->loc (prop-match-value match)))
|
||||
m)
|
||||
;; Update the markers if necessary.
|
||||
(unless (and (compilation--loc->marker loc)
|
||||
(marker-buffer (compilation--loc->marker loc)))
|
||||
(compilation--update-markers loc dummy compilation-error-screen-columns compilation-first-column))
|
||||
(setq m (compilation--loc->marker loc))
|
||||
(add-text-properties (prop-match-beginning match)
|
||||
(or (next-single-property-change
|
||||
(prop-match-end match)
|
||||
'compilation-message)
|
||||
(1+ (pos-eol)))
|
||||
`(occur-target ((,m . ,m)))))))))
|
||||
|
||||
(defvar grep-edit-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map text-mode-map)
|
||||
(define-key map (kbd "C-c C-c") #'grep-edit-save-changes)
|
||||
map)
|
||||
"Keymap for `grep-edit-mode'.")
|
||||
|
||||
(defvar grep-edit-mode-hook nil
|
||||
"Hooks run when changing to Grep-Edit mode.")
|
||||
|
||||
(defun grep-edit-mode ()
|
||||
"Major mode for editing *grep* buffers.
|
||||
In this mode, changes to the *grep* buffer are applied to the
|
||||
originating files.
|
||||
\\<grep-edit-mode-map>
|
||||
Type \\[grep-edit-save-changes] to exit Grep-Edit mode, return to Grep
|
||||
mode.
|
||||
|
||||
The only editable texts in a Grep-Edit buffer are the match results."
|
||||
(interactive)
|
||||
(error "This mode can be enabled only by `grep-change-to-grep-edit-mode'"))
|
||||
(put 'grep-edit-mode 'mode-class 'special)
|
||||
|
||||
(defun grep-change-to-grep-edit-mode ()
|
||||
"Switch to `grep-edit-mode' to edit *grep* buffer."
|
||||
(interactive)
|
||||
(unless (derived-mode-p 'grep-mode)
|
||||
(error "Not a Grep buffer"))
|
||||
(when (get-buffer-process (current-buffer))
|
||||
(error "Cannot switch when grep is running"))
|
||||
(use-local-map grep-edit-mode-map)
|
||||
(grep-edit--prepare-buffer)
|
||||
(setq buffer-read-only nil)
|
||||
(setq major-mode 'grep-edit-mode)
|
||||
(setq mode-name "Grep-Edit")
|
||||
(buffer-enable-undo)
|
||||
(set-buffer-modified-p nil)
|
||||
(setq buffer-undo-list nil)
|
||||
(add-hook 'after-change-functions #'occur-after-change-function nil t)
|
||||
(run-mode-hooks 'grep-edit-mode-hook)
|
||||
(message "Editing: \\[grep-edit-save-changes] to return to Grep mode"))
|
||||
|
||||
(defun grep-edit-save-changes ()
|
||||
"Switch back to Grep mode."
|
||||
(interactive)
|
||||
(unless (derived-mode-p 'grep-edit-mode)
|
||||
(error "Not a Grep-Edit buffer"))
|
||||
(remove-hook 'after-change-functions #'occur-after-change-function t)
|
||||
(use-local-map grep-mode-map)
|
||||
(setq buffer-read-only t)
|
||||
(setq major-mode 'grep-mode)
|
||||
(setq mode-name "Grep")
|
||||
(force-mode-line-update)
|
||||
(buffer-disable-undo)
|
||||
(setq buffer-undo-list t)
|
||||
(message "Switching to Grep mode"))
|
||||
|
||||
;;;###autoload
|
||||
(defun grep-find (command-args)
|
||||
|
|
Loading…
Reference in a new issue