;; Electric-replace  (version 1.02, 28 Mar 1991)
;; Electrically toggle replace options while in minibuffer.
;; Copyright (C) 1991 Greg Holley
;; Written by Greg Holley (sun!sono!holley, holley@sono.uucp)

;; This file is not part of the GNU Emacs distribution.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; this file, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

;; Bugs to Greg Holley (holley@sono.uucp, sun.com!sono!holley)

;; How it works:
;;
;; The non-interactive routine electric-replace handles all the
;; mode-toggling, which is performed by short functions
;; (electric-replace-toggle-query, etc) bound to keys in the minibuffer
;; keymap (electric-replace-minibuffer-keymap).  The interactive
;; functions electric-replace-string, electric-replace-regexp,
;; electric-query-replace-string, and electric-query-replace-regexp
;; call electric-replace and place it in different initial states.
;; These functions are, however, unnecessary, since any initial state
;; can be changed to any other possible state.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Date: 4 Apr 91 03:00:57 GMT
;;; From: cis.ohio-state.edu!amdcad!sono!fog!holley  (Greg Holley)
;;; Organization: Acuson; Mountain View, California
;;; Subject: Electric-replace mode
;;; To: gnu-emacs-sources@prep.ai.mit.edu
;;;
;;; The following source code replaces the emacs commands replace-string,
;;; replace-regexp, query-replace, and query-replace-regexp with a single
;;; command electric-replace-string.  While entering from string, the user 
;;; can use keystrokes to toggle between replacing regexps and strings, or
;;; between query and noquery modes.  In addition, the user can choose to
;;; apply the replace over the entire (potentially narrowed) buffer, or
;;; over the entire region.  Lastly, the user can yank the last string or
;;; regexp searched (depending on mode) to the from-string buffer.
;;;
;;; Quick summary:
;;;
;;;   C-h		Print this help message
;;;   C-r		Toggle between string/regexp replace
;;;   M-q		Toggle in and out of query replace mode
;;;   C-l		Toggle region mode (replace only in region)
;;;   M-a		Toggle full buffer mode
;;;   C-s		Yank the last string/regexp searched
;;;
;;; The key bindings are far from perfect.  Perfect bindings should
;;; 	a)  not interfere with useful standard editing functions (hence
;;; 	    M-a and M-q instead of C-a, C-q).
;;; 	b)  be reasonably mnemonic.
;;;
;;; If anyone has any suggestions, (keybindings, suggested functions,
;;; effusive praise, or "you idiot, Thomas Jefferson did the same thing
;;; better back in 1782"), send me e-mail at:
;;; 	holley@sono.uucp 	or 	sun.com!sono!holley
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar electric-replace-minibuffer-keymap ()
  "Minibuffer keymap used in electric replace.")

(defvar electric-replace-help-key
  (char-to-string help-char)
  "Key binding (within a local minibuffer keymap) used to print help in
electric replace.")

(if electric-replace-minibuffer-keymap ()
  (setq electric-replace-minibuffer-keymap (copy-keymap minibuffer-local-map))
  (define-key electric-replace-minibuffer-keymap electric-replace-help-key
    'electric-replace-print-help)
  (define-key electric-replace-minibuffer-keymap "\M-q"
    'electric-replace-toggle-query)
  (define-key electric-replace-minibuffer-keymap "\C-s"
    'electric-replace-use-last-search)
  (define-key electric-replace-minibuffer-keymap "\C-r"
    'electric-replace-toggle-regexp)
  (define-key electric-replace-minibuffer-keymap "\M-a"
    'electric-replace-toggle-all)
  (define-key electric-replace-minibuffer-keymap "\C-L"
    'electric-replace-toggle-region))


(defun electric-replace (query regexp &optional delimited)
  (let ((from-string nil)
	(localization nil)
	(continue t)
	prompt)
    (setq prompt (electric-replace-build-prompt query regexp localization)) 
    (while continue
      (setq continue nil)
      (setq from-string (read-from-minibuffer
			 prompt from-string
			 electric-replace-minibuffer-keymap nil)))
    (setq prompt (concat (electric-replace-build-prompt
			  query regexp localization t)
			 from-string
			 " with: "))
    (setq to-string (read-from-minibuffer prompt nil nil nil))
    (cond
     ((eq localization 'region)
      (save-excursion
	(save-restriction
	  (narrow-to-region (point) (mark))
	  (goto-char (point-min))
	  (perform-replace from-string to-string query regexp nil))))
     ((eq localization 'all)
      (save-excursion
	(goto-char (point-min))
	(perform-replace from-string to-string query regexp nil)))
     (t
      (perform-replace from-string to-string query regexp nil)))))

(defun electric-replace-string (&optional delimited) 
  "Electrically read from-string and replace with to-string.
Optional prefix arg DELIMITED non-nil means replace only matches surrounded
by word boundaries.  See replace-string."
  (interactive "*P")
  (electric-replace nil nil delimited))

(defun electric-replace-regexp (&optional delimited) 
  "Electrically read regexp and replace with string.
Optional prefix arg DELIMITED non-nil means replace only matches surrounded
by word boundaries.  See replace-regexp."
  (interactive "*P") 
  (electric-replace nil t delimited))

(defun electric-query-replace-string (&optional delimited) 
  "Electrically read from-string and sometimes replace with to-string.
Optional prefix arg DELIMITED non-nil means replace only matches surrounded
by word boundaries.  See query-replace."
  (interactive "*P")
  (electric-replace t nil delimited))

(defun electric-query-replace-regexp (&optional delimited) 
  "Electrically read regexp and sometimes replace with string.
Optional prefix arg DELIMITED non-nil means replace only matches surrounded
by word boundaries.  See query-replace-regexp."
  (interactive "*P")
  (electric-replace t t delimited))


(defun electric-replace-build-prompt (query regexp
				      localization &optional nocolon)
  (let ((m (concat (if query "Query " "")
		   "replace "
		   (if regexp "regexp" "string")
		   (cond ((eq localization 'region) " (region)")
			 ((eq localization 'all) " (all)")
			 (""))
		   (if nocolon " " ": ")
		   )))
    (aset m 0 (upcase (aref m 0)))
    m))

(defun electric-replace-toggle-query ()
  "Toggles query replace feature in electric-replace."
  (interactive)
  (setq query (not query))
  (setq prompt (electric-replace-build-prompt query regexp localization))
  (setq continue t)
  (exit-minibuffer))
  
(defun electric-replace-toggle-regexp ()
  "Toggles regexp/string in electric-replace."
  (interactive)
  (setq regexp (not regexp))
  (setq prompt (electric-replace-build-prompt query regexp localization))
  (setq continue t)
  (exit-minibuffer))

;; Region/all behavior:
;; Replacements take place over one of three localizations:
;;	1) from point to end-of-buffer* (default)
;;	2) over entire buffer* (all)
;;	3) over user-defined region (region)
;; * excluding restricted (narrowed) text
;;
;; The following two commands switch between the three modes as follows:
;;
;;			     |    COMMAND:
;;			     |	t-r	t-a
;;		      -------+-----------------
;;			---  |	 R	 A
;;	   CURR MODE:	 R   |	---	 A	  <== NEXT MODE
;;			 A   |	 R	---
;;
;;
;;   Where	--- == default mode
;;		 R  == region mode
;;		 A  == all mode
;;		t-r == electric-replace-toggle-region
;;		t-a == electric-replace-toggle-all
;;
(defun electric-replace-toggle-region () 
  "Toggles electric-replace region mode (confines replacements to region)."
  (interactive)
  (setq localization (if (eq localization 'region) nil 'region))
  (setq prompt (electric-replace-build-prompt query regexp localization))
  (setq continue t)
  (exit-minibuffer))

(defun electric-replace-toggle-all () 
  "Toggles electric-replace all mode (replacement throughout buffer)."
  (interactive)
  (setq localization (if (eq localization 'all) nil 'all))
  (setq prompt (electric-replace-build-prompt query regexp localization))
  (setq continue t)
  (exit-minibuffer))

(defun electric-replace-use-last-search ()
  "Replaces the string being entered with the last search string/regexp."
  (interactive)
  (delete-region (point-min) (point-max))
  (insert (if regexp search-last-regexp search-last-string)))


(defconst electric-replace-help 
  "\\<electric-replace-minibuffer-keymap>
electric-replace minibuffer commands:
  \\[electric-replace-print-help]\t\tPrint this help message
  \\[electric-replace-toggle-regexp]\t\tToggle between string/regexp replace
  \\[electric-replace-toggle-query]\t\tToggle in and out of query replace mode
  \\[electric-replace-toggle-region]\t\tToggle region mode (replace only in region)
  \\[electric-replace-toggle-all]\t\tToggle full buffer mode
  \\[electric-replace-use-last-search]\t\tYank the last string/regexp searched"
  "Help message while reading strings in electric replace.")

(defun electric-replace-print-help ()
  "Prints a brief help message describing electric-replace commands."
  (interactive)
  (with-output-to-temp-buffer "*Help*"
    (princ (substitute-command-keys electric-replace-help))))
