gnu-emacs-sources
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

igrep-find-replace.el --- igrep interface for query-replace


From: Kevin A. Burton
Subject: igrep-find-replace.el --- igrep interface for query-replace
Date: 04 Nov 2001 02:40:41 -0800
User-agent: Gnus/5.0808 (Gnus v5.8.8) Emacs/21.1

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


OK... just some small usability fixes.

;; - Wed Oct 31 2001 10:38 PM (address@hidden): overlay-arrow-string
;; must be non-nil.  AKA '>'
;;
;; - Wed Oct 31 2001 09:24 AM (address@hidden): need to show the user
;;   (somehow) that the replacement was actually done correctly.  This is
;;   necessary because we need to show the user what is actually happening
;;   without any confusion.
;;
;;     - now using sit-for to handle this.


;;; igrep-find-replace.el --- igrep interface for query-replace

;; $Id: igrep-find-query-replace.el,v 1.5 2001/11/02 09:43:06 burton Exp $

;; Copyright (C) 2000-2003 Free Software Foundation, Inc.
;; Copyright (C) 2000-2003 Kevin A. Burton (address@hidden)

;; Author: Kevin A. Burton (address@hidden)
;; Maintainer: Kevin A. Burton (address@hidden)
;; Location: http://relativity.yi.org
;; Keywords:
;; Version: 1.0.0

;; This file is [not yet] part of GNU Emacs.

;; 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 2 of the License, or 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
;; this program; if not, write to the Free Software Foundation, Inc., 59 Temple
;; Place - Suite 330, Boston, MA 02111-1307, USA.

;;; Commentary:
;;
;; This is a utility for igrep.  It allows you to run igrep-find and
;; 'query-replace' within all matched files.  This allows you to make major
;; changes to multiple files at once.
;;
;; The interface behaves just like 'igrep' and 'query-replace' and shouldn't
;; confuse the user with any new UI metaphor.
;;

;;; History

;; - Wed Oct 31 2001 10:38 PM (address@hidden): overlay-arrow-string
;; must be non-nil.  AKA '>'
;;
;; - Wed Oct 31 2001 09:24 AM (address@hidden): need to show the user
;;   (somehow) that the replacement was actually done correctly.  This is
;;   necessary because we need to show the user what is actually happening
;;   without any confusion.
;;
;;     - now using sit-for to handle this.

;; - Tue Oct 30 2001 07:26 PM (address@hidden): we now display the
;; process termination event.
;;
;; - Sat Oct 27 2001 08:32 PM (address@hidden): we are now using an
;; 'overlay-arrow' in the igrep buffer.
;;
;; - Sat Oct 27 2001 07:15 PM (address@hidden): message stating that
;; igrep-find-query-will start when igrep finishes.
;;
;; - Thu Oct 25 2001 01:15 AM (address@hidden): need a "y or n" question
;;   on the query prompt.
;;

;;; TODO:
;;
;;
;;; HIGH PRIORITY...

;; - when the file buffer (not the *igrep* buffer) shows the match near the end
;;   of beginning of the buffer, center the window so that it is more obvious 
what
;;   we are replacing.
;;
;;    - is there an easy function to do this?  maybe -recenter or something?
;;
;; - Make sure that the it is obvious what buffer we are in in the *igrep*
;;   buffer.
;;
;;     - use the secondary selection face to highlight the current line (in the
;;       *igrep* buffer) I am replacing.
;; 
;; - support the ability to replace multi-line text..
;;
;; - is it possible to stop using the *igrep* buffer and use an
;; *igrep-find-query-replace* buffer instead?

;;; LOW PRIORITY...

;; - implement igrep-query-replace as well as igrep-find-query-replace.  Any
;;   other functions I should implement??  agrep, fgrep, etc?
;;
;; - for some reason I am getting this error:
;;
;;     error in process sentinel: Args out of range: 0, 6 [2 times]
;;
;; - Use an italic overlay to highlight the current line/file.
;;
;; - In long buffers, keep the current line near the top of the buffer.

(require 'igrep)

;;; Code:
(defvar ifqr-from nil "String to search and replace from.")
(make-variable-buffer-local 'ifqr-from)

(defvar ifqr-with nil "String to search and replace with.")
(make-variable-buffer-local 'ifqr-with)

(defvar ifqr-replacement-count 0 "Number of replacements for the last 
`query-replace'.")

(defvar ifqr-text-buffer-name "*ifqr-text*" "Buffer used for reading and writing
text/multiline string")

(defvar ifqr-text-replace-from nil "Search for the given text.")

(defvar ifqr-text-replace-with nil "Replace the given text.")

(defvar ifqr-text-replace-var-name nil "Var name to set.")

(defvar ifqr-text-replace-function nil "Function to eval after completion.")

(defface ifqr-line-overlay-face nil "Overlay used to highlight the current 
match.")
(set-face-background 'ifqr-line-overlay-face (face-background 
'secondary-selection))

(defvar ifqr-line-overlay (make-overlay 0 0) "Overlay used to highlight this 
match.")

(defun igrep-find-query-replace(replace-from replace-with files)
  "Use igrep-find to query and replace strings within buffers."
  (interactive

   (let(from to files)
     (setq from (read-string "Query replace: "))

     (setq to (read-string (format "Query replace %s with: " from )))

     (setq files (igrep-read-files))

     (list from to files)))

  (delete-other-windows)
  
  (let ((igrep-find t)
        igrep-args
        filename
        process)
    
    (setq igrep-args (list "grep" replace-from files))

    ;;run igrep-find...
    (igrep (elt igrep-args 0)
           (elt igrep-args 1)
           (elt igrep-args 2))
    
    (message "igrep-find-query-replace will start when igrep finishes...")

    ;;get the process created by igrep
    (setq process (get-process "igrep"))

    ;;if the process is nil... it exited quickly... process the output.
    (if process
        ;; use a process sentinel to find it.
        (progn

          ;;update buffer local variables for from/width
          (save-excursion

            (set-buffer (process-buffer process))

            (setq ifqr-from replace-from)

            (setq ifqr-with replace-with))
          
          (set-process-sentinel process 'ifqr-sentinel))

      ;;this should never happen... I think... maybe we shouldn't signal an
      ;;error though.
      (error "Unable to find process"))))

(defun ifqr-do()
  "Do the query/replace after igrep has ran saving.  Show the number of
replacements when complete. "

  ;;reset the count
  (setq ifqr-replacement-count 0)
    
  (let(filename line-number replace-from replace-with quit)
      
    (set-buffer "*igrep*")
      
    (display-buffer "*igrep*")
      
    ;;get the local replacement vars..
    (setq replace-from ifqr-from)

    (setq replace-with ifqr-with)
      
    (beginning-of-buffer)

    (forward-line 2)

    (ifqr-highlight-line (point))
    
    ;;navigate forward though the igrep buffer.
    (while (and (not quit)
                (re-search-forward "\\(^/[^:]+\\):\\([0-9]+\\):" nil t))

      (ifqr-highlight-line (point))
        
      (setq filename (match-string 1))

      (setq line-number (string-to-number (match-string 2)))
      
      (setq quit (ifqr-do-replace filename line-number replace-from 
replace-with))

      ;;return to the igrep buffer just in case.
      (set-buffer "*igrep*")

      (forward-line 1)
      (beginning-of-line)))

  (message "Replaced %i occurrences...done" ifqr-replacement-count))

(defun ifqr-do-replace(filename line-number replace-from replace-with)
  "Do a replacement on the given line number, keep doing until we are complete.
If the user select 'q' (for quit) return true, else nil. "

  ;;valid options are y/n/q
  (let(source-buffer result)

    (setq source-buffer (find-file-noselect filename))

    (set-window-buffer (other-window 0) source-buffer)
    
    (set-buffer source-buffer)
    
    (goto-line line-number)

    (beginning-of-buffer)

    (assert (search-forward replace-from nil t)
            nil (format "Could not find %s" replace-from))

    ;;highlight the match...
    (ifqr-highlight (match-beginning 0) (match-end 0))

    ;;if an error is signaled... catch it... this can happen from mouse
    ;;events/etc..
    
    (condition-case nil
        (progn

          ;;should we replace
          (if (yes-or-no-p (format "Query replacing '%s' with '%s' in %s: " 
replace-from replace-with filename))
              (progn

                (replace-match replace-with)

                (ifqr-highlight-entire-match)
                
                (sit-for .2)
                
                ;;increment the match count...
                (setq ifqr-replacement-count (1+ ifqr-replacement-count))
                
                ;;important to save the buffer when we are done.
                (save-buffer))))
      nil)
    result))

(defun ifqr-highlight(region-start region-end)
  "Setup the mark within the given region."

  (goto-char region-start)
  (push-mark nil t t)
  (goto-char region-end))

(defun ifqr-highlight-entire-match()
  "Highlight the entire match."

  (ifqr-highlight (match-beginning 0) (match-end 0)))

(defun ifqr-sentinel(process event)
  "Process sentinel which detects if the igrep process is done."
  
  (if (and (string-equal (process-name process) "igrep")
           (string-match "^finished\n$" event))
      (ifqr-do)
    (progn

      ;;else somethign happened.

      (save-excursion

        (set-buffer "*igrep*")

        (end-of-buffer)

        (insert event))
    
      (message ""))))

(defun igrep-find-query-replace-text()
  "Run query replace on long/text strings."
  (interactive)

  (ifqr-text-step-1))

(defun ifqr-text-step-1()
  "Get the from variable."
  
  (ifqr-read-text-string 'ifqr-text-replace-from
                         'ifqr-text-step-2
                         "(Step one) Enter the text you would like to search 
for."))

(defun ifqr-text-step-2()

  (ifqr-read-text-string 'ifqr-text-replace-with
                         'ifqr-text-do
                         "(Step two) Enter the text you would like to use as a 
replacement."))

(defun ifqr-read-text-string(var-name function message)
  "Read a text string from a buffer."

  (setq ifqr-text-replace-var-name var-name)

  (setq ifqr-text-replace-function function)
  
  (save-excursion

    (set-buffer (get-buffer-create ifqr-text-buffer-name))

    (erase-buffer)
    
    (insert (format "//REPLACE-TEXT: %s\n" message))

    (ifqr-text-entry-mode))

  (pop-to-buffer ifqr-text-buffer-name)
    
  (message message))

(defun ifqr-read-text-complete()
  "Complete text entry and continue."
  (interactive)

  (ifqr-text-cleanse-buffer)

  (delete-window (get-buffer-window ifqr-text-buffer-name))

  (funcall ifqr-text-replace-function))

(defun ifqr-text-cleanse-buffer()
  
  (save-excursion
    (set-buffer ifqr-text-buffer-name)

    (beginning-of-buffer)

    (while (re-search-forward "^//REPLACE-TEXT: .*$" nil t)
      (delete-region (match-beginning 0)
                     (1+ (match-end 0))))))

(defun ifqr-highlight-line(point)
  "Highlight the current line so that people know what the heck is going on..."
  (interactive
   (list
    (point)))
  
  (save-excursion

    (set-buffer "*igrep*")

    (let((begin nil)
         (end nil))

      (goto-char point)                 

      (setq begin (point-at-bol))

      (setq end (1+ (point-at-eol)))

      ;;update the Emacs 21 arrow...
      (let(m)
        (setq m (make-marker))

        (set-marker m begin)

        (make-local-variable 'overlay-arrow-string)
        (setq overlay-arrow-string "=>")
        
        (setq overlay-arrow-position m))
      
      (move-overlay ifqr-line-overlay begin end (current-buffer))
      
      (overlay-put ifqr-line-overlay 'face 'ifqr-line-overlay-face)
      
      (overlay-put ifqr-line-overlay 'window (selected-window))

      (overlay-put ifqr-line-overlay 'priority 1)

      (sit-for .2))))

(define-derived-mode ifqr-text-entry-mode fundamental-mode "IGrepTextEntry"
  "Major mode with key bindings to jump to files."
  (interactive)

  (font-lock-mode 1))

;;comments
(font-lock-add-keywords 'ifqr-text-entry-mode '(("^//.*$"
                                                 (0 'font-lock-comment-face 
t))))

(define-key ifqr-text-entry-mode-map "\C-c\C-c" 'ifqr-read-text-complete)

(provide 'igrep-find-query-replace)

;;; igrep-find-replace.el ends here

- -- 
Kevin A. Burton ( address@hidden, address@hidden, address@hidden )
             Location - San Francisco, CA, Cell - 415.595.9965
        Jabber - address@hidden,  Web - http://relativity.yi.org/

Whenever there is a conflict between human rights and property rights, human
rights must prevail.
  -- Abraham Lincoln

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: Get my public key at: http://relativity.yi.org/pgpkey.txt

iD8DBQE75RCIAwM6xb2dfE0RAjn+AJ0a9nx+1P9b0X6mYkSqJA/w1aUo9QCgliL3
tWg5RRs7RdILnBh5+SFdkLQ=
=bYpz
-----END PGP SIGNATURE-----



reply via email to

[Prev in Thread] Current Thread [Next in Thread]