;;; moccur-edit.el --- apply replaces to multiple files ;; -*- Mode: Emacs-Lisp -*- ;; $Id: moccur-edit.el,v 2.16 2008/08/01 09:32:18 akihisa Exp $ ;; Author: Matsushita Akihisa ;; Keywords: moccur edit ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation; either version 3, or (at ;; your option) any later version. ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; moccur-edit provides to edit moccur buffer of color-moccur.el and ;; to apply the changes to the file. ;; This file requires color-moccur.el ;; The latest version of these program can be downloaded from ;; http://www.bookshelf.jp/elc/color-moccur.el. ;;; Install: ;; Put this file into load-path'ed directory, and byte compile it if ;; desired. And put the following expression into your ~/.emacs. ;; ;; (require 'moccur-edit) ;; The latest version of this program can be downloaded from ;; http://www.bookshelf.jp/elc/moccur-edit.el ;;; Hint (.emacs) ;; Modified buffers are saved automatically. ;; Thanks request! ;; (defadvice moccur-edit-change-file ;; (after save-after-moccur-edit-buffer activate) ;; (save-buffer)) ;;; Usage: ;; You can start editing the names of the files by typing "C-c C-i" or ;; "C-x C-q". ;; Use C-c C-f when finished or C-c C-k to abort or C-c C-r to remove ;; the changes in the region. ;; History: ;; moccur-edit 1.0 was released to the net on 12/03/2002 ;;; Code: (require 'color-moccur) (defgroup moccur-edit nil "Customize moccur-edit" :group 'matching) (defface moccur-edit-face '((((class color) (background dark)) (:background "Pink" :bold t :foreground "Black")) (((class color) (background light)) (:background "ForestGreen" :bold t)) (t ())) "*Face used for the changed text on moccur buffer." :group 'moccur-edit) (defface moccur-edit-file-face '((((class color) (background dark)) (:background "gray30" :bold t)) (((class color) (background light)) (:background "ForestGreen" :bold t)) (t ())) "*Face used for the changed text on file buffer." :group 'moccur-edit) (defface moccur-edit-done-face '((((class color) (background dark)) (:foreground "gray30" :bold t)) (((class color) (background light)) (:foreground "ForestGreen" :bold t)) (t ())) "*Face used for the line on moccur buffer that can apply to file." :group 'moccur-edit) (defface moccur-edit-reject-face '((((class color) (background dark)) (:foreground "hot pink" :bold t)) (((class color) (background light)) (:foreground "red" :bold t)) (t ())) "*Face used for the line on moccur buffer that can not apply to file." :group 'moccur-edit) (defcustom moccur-edit-highlight-edited-text nil "*Non-nil means to highlight the edited text." :group 'moccur-edit :type 'boolean ) (defcustom moccur-edit-remove-overlays nil "*Non-nil means to remove overlays when moccur-quit is run." :group 'moccur-edit :type 'boolean ) (defcustom moccur-edit-remove-overlays-after-save-buffer t "*Non-nil means to remove overlays after save-buffer." :group 'moccur-edit :type 'boolean ) (defcustom moccur-query-when-buffer-read-only t "*Non-nil means query if read-only buffers should be made writable." :group 'moccur-edit :type 'boolean) (defvar moccur-edit-overlays nil) (defvar moccur-edit-file-overlays nil) (defvar moccur-edit-result-overlays nil) (make-local-variable 'moccur-edit-file-overlays) (defvar moccur-edit-change-face-flg nil) (defvar moccur-edit-old-content) (make-local-variable 'moccur-edit-old-content) (defun moccur-mode-edit-set-key () (define-key moccur-mode-map "r" 'moccur-edit-mode-in) (define-key moccur-mode-map "\C-x\C-q" 'moccur-edit-mode-in) (define-key moccur-mode-map "\C-c\C-i" 'moccur-edit-mode-in) (define-key moccur-ee-mode-map "r" 'moccur-edit-mode-in) (define-key moccur-ee-mode-map "\C-x\C-q" 'moccur-edit-mode-in) (define-key moccur-ee-mode-map "\C-c\C-i" 'moccur-edit-mode-in) ) (if moccur-mode-map (moccur-mode-edit-set-key)) (defun moccur-mode-change-face (beg end leng-before) (interactive) (let ((ov (overlays-in beg end)) (edit-ov nil) (exist-ovelays nil)) (if moccur-edit-change-face-flg (progn (save-excursion (while ov (if (overlay-get (car ov) 'moccur-edit) (setq exist-ovelays t)) (setq ov (cdr ov))) (if exist-ovelays () (setq edit-ov (make-overlay (line-beginning-position) (line-end-position))) (overlay-put edit-ov 'moccur-edit t) (overlay-put edit-ov 'face 'moccur-edit-face) (overlay-put edit-ov 'priority 0) (setq moccur-edit-overlays (cons edit-ov moccur-edit-overlays)) )))))) (defvar moccur-edit-buf "") (defvar moccur-edit-line "") (defvar moccur-edit-text "") (defun moccur-edit-get-info () (save-excursion (beginning-of-line) (when (re-search-backward "^[-+ ]*Buffer:" nil t) (if (re-search-forward "File (grep)" (line-end-position) t) (progn (if (re-search-forward ":[ ]+\\([^\r\n]+\\)$" (line-end-position) t) (setq moccur-edit-buf (cons "grep" (buffer-substring-no-properties (match-beginning 1) (match-end 1)) )) (setq moccur-edit-buf "grep"))) (beginning-of-line) (if (re-search-forward "^[-+ ]*Buffer: \\([^\r\n]*\\) File:" (line-end-position) t) (setq moccur-edit-buf (buffer-substring-no-properties (match-beginning 1) (match-end 1))))))) (save-excursion (beginning-of-line) (if (re-search-forward "^[ ]*\\([0-9]+\\) \\([^\n]+$\\)" (line-end-position) t) (progn (setq moccur-edit-line (string-to-number (buffer-substring-no-properties (match-beginning 1) (match-end 1)))) (setq moccur-edit-text (buffer-substring-no-properties (match-beginning 2) (match-end 2)))))) ) (defun moccur-edit-change-file () "*The changes on the moccur buffer apply to the file" (if buffer-read-only (if (and moccur-query-when-buffer-read-only (buffer-file-name) ;;(file-writable-p (buffer-file-name)) (y-or-n-p (format "Make buffer %s writable?" (current-buffer))) (or (toggle-read-only -1) t) (not buffer-read-only)) (moccur-edit-change-file) (display-warning ; (Not defined in old Emacses! *) 'moccur-edit (format "Buffer read only: %s" (current-buffer))) nil) (goto-line moccur-edit-line) (delete-region (line-beginning-position) (line-end-position)) (insert moccur-edit-text) t)) (defun moccur-edit-put-color-file () "*Highlight the changed line of the file" (let ((fileov)) (setq fileov (make-overlay (line-beginning-position) (line-end-position))) (overlay-put fileov 'face 'moccur-edit-file-face) (overlay-put fileov 'priority 0) (setq moccur-edit-file-overlays (cons fileov moccur-edit-file-overlays)) )) (defun moccur-edit-put-face (face) (let ((ov)) (beginning-of-line) (re-search-forward "^[ ]*[0-9]+ " nil t) (setq ov (make-overlay (point) (line-end-position))) (overlay-put ov 'moccur-edit t) (overlay-put ov 'face face) (overlay-put ov 'priority 0) (setq moccur-edit-result-overlays (cons ov moccur-edit-result-overlays)) )) (defun moccur-edit-remove-overlays () "Remove all overlays in all buffers." (interactive) (save-current-buffer (let (ov buf (buflist (buffer-list))) (while buflist (setq buf (car buflist)) (setq buflist (cdr buflist)) (when (and buf (buffer-live-p buf)) (set-buffer buf) (while moccur-edit-file-overlays (delete-overlay (car moccur-edit-file-overlays)) (setq moccur-edit-file-overlays (cdr moccur-edit-file-overlays)))))))) (defadvice moccur-quit (before moccur-edit-kill-buffer activate) (when moccur-edit-remove-overlays (moccur-edit-remove-overlays))) (defadvice basic-save-buffer (before moccur-remove-overlays-before-save-buffer activate) (when moccur-edit-remove-overlays-after-save-buffer (moccur-edit-remove-overlays))) (defun moccur-edit-finish-edit () "*The changes on the grep buffer apply to the file" (interactive) (let ((ov) fileov beg filename text line local-buf cbuf line) (setq cbuf (current-buffer)) (while moccur-edit-overlays (setq ov (car moccur-edit-overlays)) (setq moccur-edit-overlays (cdr moccur-edit-overlays)) (setq beg (overlay-start ov)) (when beg (goto-char beg) (moccur-edit-get-info) (if (and (listp moccur-edit-buf) (string= (car moccur-edit-buf) "grep")) (set-buffer (find-file-noselect (cdr moccur-edit-buf))) (set-buffer moccur-edit-buf)) ;; (if (moccur-edit-change-file) ;; File is changed. t: success (progn (when moccur-edit-highlight-edited-text (moccur-edit-put-color-file)) ;; Highlight changed text (set-buffer cbuf) (moccur-edit-put-face 'moccur-edit-done-face)) (set-buffer cbuf) (moccur-edit-put-face 'moccur-edit-reject-face)) ;; Return previous buffer (set-buffer cbuf) (delete-overlay ov) ))) (moccur-edit-reset-key)) (defun moccur-edit-remove-change (beg end) (interactive "r") (let ((ov (overlays-in beg end))) (while ov (if (overlay-get (car ov) 'moccur-edit) (delete-overlay (car ov))) (setq ov (cdr ov)))) (setq mark-active nil)) (defun moccur-edit-mode-in () (interactive) (moccur-edit-mode) (force-mode-line-update) (setq moccur-edit-old-content (buffer-substring (point-min) (point-max))) (setq moccur-edit-change-face-flg nil) (setq buffer-read-only nil) (moccur-edit-set-readonly-area) (setq moccur-edit-change-face-flg t) (setq line-move-ignore-invisible nil) ;; Cause use of ellipses for invisible text. (setq buffer-invisibility-spec nil) (add-hook 'after-change-functions 'moccur-mode-change-face nil t) ) (defun current-line () "Return the vertical position of point..." (1+ (count-lines 1 (point)))) (defun max-line () "Return the vertical position of point..." (save-excursion (goto-char (point-max)) (current-line))) (defun moccur-edit-kill-all-change () (interactive) (let (pos) (setq pos (current-line)) (moccur-edit-remove-change (point-min) (point-max)) (moccur-edit-reset-key) (let ((inhibit-read-only t)) (erase-buffer) (if (and (string= mode-name "Moccur-ee") (get-buffer "*Moccur*")) (progn (if (get-buffer "*ee-outline*/*Moccur*") (kill-buffer (get-buffer "*ee-outline*/*Moccur*"))) (switch-to-buffer (get-buffer "*Moccur*")) ;;(set-buffer (get-buffer "*Moccur*")) (ee-outline) (moccur-mode t) ;;(use-local-map moccur-mode-map) (setq moccur-mocur-buffer (current-buffer)) ;; highlight Moccur buffer (moccur-buffer-color) ;;(insert-before-markers-and-inherit moccur-edit-old-content) ) (insert moccur-edit-old-content) (moccur-buffer-color) )) (if (> pos (max-line)) (goto-line (max-line)) (goto-line pos)) )) (defun moccur-edit-set-readonly-area () (let ((inhibit-read-only t) beg end) (save-excursion (goto-char (point-min)) (setq beg (point)) (end-of-line) (setq end (point)) (put-text-property beg end 'read-only t) (put-text-property beg end 'front-sticky '(read-only)) (while (re-search-forward "\\(^[-+ ]*Buffer: [^\n]*File[^\n]+$\\)" nil t) (put-text-property (match-beginning 0) (match-end 0) 'read-only t) (put-text-property (match-beginning 0) (match-end 0) 'front-sticky '(read-only))) (goto-char (point-min)) (while (re-search-forward "\\(^[ ]*[0-9]+\\)" nil t) (put-text-property (match-beginning 1) (match-end 1) 'read-only t) (put-text-property (match-beginning 0) (match-end 0) 'front-sticky '(read-only))) (goto-char (point-min)) (while (re-search-forward "^\\([\r\n]+\\)" nil t) (put-text-property (match-beginning 1) (match-end 1) 'read-only t) (put-text-property (match-beginning 0) (match-end 0) 'front-sticky '(read-only))) ))) (defun moccur-edit-reset-key () (interactive) (setq buffer-read-only t) (cond ((string= mode-name "Moccurg-edit") (moccur-grep-mode)) ((string= mode-name "Moccure-edit") (moccur-mode t)) (t (moccur-mode))) (make-local-variable 'line-move-ignore-invisible) (setq line-move-ignore-invisible t) (if (not (and (boundp 'running-xemacs) running-xemacs)) (add-to-invisibility-spec '(moccur . t))) (force-mode-line-update) ) ;; moccur-mode (defvar moccur-edit-mode-map ()) (defun moccur-edit-set-key () (define-key moccur-edit-mode-map '[down] 'moccur-next) (define-key moccur-edit-mode-map '[up] 'moccur-prev) (define-key moccur-edit-mode-map "\C-c\C-r" 'moccur-edit-remove-change) (define-key moccur-edit-mode-map "\C-c\C-f" 'moccur-edit-finish-edit) (define-key moccur-edit-mode-map "\C-x\C-s" 'moccur-edit-finish-edit) (define-key moccur-edit-mode-map "\C-c\C-c" 'moccur-edit-finish-edit) (define-key moccur-edit-mode-map "\C-c\C-k" 'moccur-edit-kill-all-change) (define-key moccur-edit-mode-map "\C-xk" 'moccur-edit-kill-all-change) (define-key moccur-edit-mode-map "\C-ck" 'moccur-edit-kill-all-change) (define-key moccur-edit-mode-map "\C-c\C-u" 'moccur-edit-kill-all-change) ) (if moccur-edit-mode-map () (setq moccur-edit-mode-map (make-sparse-keymap)) (moccur-edit-set-key) ) (defun moccur-edit-mode () "Major mode" (let ((ee (cond ((string= major-mode 'moccur-grep-mode) 'grep) ((string= "Moccur-ee" mode-name) 'ee) (t nil)))) (kill-all-local-variables) (use-local-map moccur-edit-mode-map) (setq major-mode 'moccur-edit-mode) (cond ((string= ee 'grep) (setq mode-name "Moccurg-edit")) ((string= ee 'ee) (setq mode-name "Moccure-edit")) (t (setq mode-name "Moccur-edit"))) (moccur-edit-set-key))) ;; advice for query-replace (defun moccur-edit-add-skip-in-replace (command) "Advice COMMAND to skip matches while they have read-only properties. This is useful to avoid \"read-only\" errors in search and replace commands. This advice only has effect in moccur-edit mode." (eval `(defadvice ,command (around moccur-edit-discard-read-only activate) ,(format "Make %s to work better with moccur-edit,\n%s." command "skipping read-only matches when invoked without argument") ad-do-it (if (eq major-mode 'moccur-edit-mode) (while (and ad-return-value (text-property-any (max 1 (1- (match-beginning 0))) (match-end 0) 'read-only t)) ad-do-it)) ad-return-value))) (defun moccur-edit-replace-advice (command) "Advice COMMAND to skip matches while they have read-only properties. This is useful to avoid \"read-only\" errors in search and replace commands. This advice only has effect in moccur-edit mode." (eval `(defadvice ,command (around moccur-edit-grok-read-only activate) ,(format "Make %s to work better with moccur-edit,\n%s." command "skipping read-only matches when invoked without argument") (if (eq major-mode 'moccur-edit-mode) (progn (moccur-edit-add-skip-in-replace 'search-forward) (moccur-edit-add-skip-in-replace 're-search-forward) (unwind-protect ad-do-it (progn (ad-remove-advice 'search-forward 'around 'moccur-edit-discard-read-only) (ad-remove-advice 're-search-forward 'around 'moccur-edit-discard-read-only) (ad-update 'search-forward) (ad-update 're-search-forward)))) ad-do-it) ad-return-value))) (defadvice moccur-mode (after moccur-edit-set-key activate) (moccur-mode-edit-set-key)) (defadvice moccur-grep-mode (after moccur-edit-set-key activate) (moccur-mode-edit-set-key)) (mapcar 'moccur-edit-replace-advice '(query-replace query-replace-regexp replace-string)) (provide 'moccur-edit) ;;; moccur-edit.el ends here