From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Juri Linkov Newsgroups: gmane.emacs.devel Subject: Re: Search minibuffer history Date: Mon, 16 Jul 2007 23:54:43 +0300 Organization: JURTA Message-ID: <87y7hgw0qu.fsf@jurta.org> References: <87myy5thsa.fsf@jurta.org> <87odikbdtl.fsf@jurta.org> <87fy3v7uwp.fsf@jurta.org> <874pka4khu.fsf@jurta.org> <87tzs55t7o.fsf@jurta.org> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: sea.gmane.org 1184622168 10625 80.91.229.12 (16 Jul 2007 21:42:48 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Mon, 16 Jul 2007 21:42:48 +0000 (UTC) Cc: rms@gnu.org, emacs-devel@gnu.org To: Stefan Monnier Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Mon Jul 16 23:42:46 2007 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1IAYL6-0001az-7C for ged-emacs-devel@m.gmane.org; Mon, 16 Jul 2007 23:42:44 +0200 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1IAYL5-0007ox-Mn for ged-emacs-devel@m.gmane.org; Mon, 16 Jul 2007 17:42:43 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1IAYJO-0005HM-8m for emacs-devel@gnu.org; Mon, 16 Jul 2007 17:40:58 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1IAYJN-0005GQ-DX for emacs-devel@gnu.org; Mon, 16 Jul 2007 17:40:57 -0400 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1IAYJN-0005GD-9k for emacs-devel@gnu.org; Mon, 16 Jul 2007 17:40:57 -0400 Original-Received: from relay01.kiev.sovam.com ([62.64.120.200]) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1IAYJK-0006rR-Uv; Mon, 16 Jul 2007 17:40:55 -0400 Original-Received: from [83.170.232.243] (helo=smtp.svitonline.com) by relay01.kiev.sovam.com with esmtp (Exim 4.67) (envelope-from ) id 1IAYJD-000JPg-5C; Tue, 17 Jul 2007 00:40:47 +0300 In-Reply-To: (Stefan Monnier's message of "Mon\, 16 Jul 2007 11\:05\:43 -0400") User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.1.50 (gnu/linux) X-Scanner-Signature: 4c561568753117dab8afed3506ee37ea X-DrWeb-checked: yes X-SpamTest-Envelope-From: juri@jurta.org X-SpamTest-Group-ID: 00000000 X-SpamTest-Header: Not Detected X-SpamTest-Info: Profiles 1229 [July 16 2007] X-SpamTest-Info: helo_type=3 X-SpamTest-Method: none X-SpamTest-Rate: 0 X-SpamTest-Status: Not detected X-SpamTest-Status-Extended: not_detected X-SpamTest-Version: SMTP-Filter Version 3.0.0 [0255], KAS30/Release X-detected-kernel: FreeBSD 4.8-5.1 (or MacOS X 10.2-10.3) X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:74929 Archived-At: >> ! (defun next-history-element (n &optional narg) >> "Puts next element of the minibuffer history in the minibuffer. >> ! With argument N, it uses the Nth following element. >> ! The optional argument NARG overrides the argument N and specifies the >> ! absolute history position instead of relative position specified by N." > > Am I the only to find the above interface to be .... ugly? > I'd rather have a function `next-history-element-internal' which takes the > NARG and then a command `next-history-element' which takes the N arg and > calls next-history-element-internal. I think the `-internal' suffix doesn't describe exactly what a new function should do. These two interfaces differ only in a way how they navigate the minibuffer history. The current function navigates by an offset relative to the minibuffer position, but a new function should navigate the history by the absolute position. So this function should have a name without the `next-' prefix. I propose the name `minibuffer-history-element' and the argument `nabs'. Below is a patch created with `-w -b' switches to better show diffs without changes in whitespace. Index: lisp/simple.el =================================================================== RCS file: /sources/emacs/emacs/lisp/simple.el,v retrieving revision 1.865 diff -c -w -b -r1.865 simple.el *** lisp/simple.el 7 Jul 2007 11:17:51 -0000 1.865 --- lisp/simple.el 16 Jul 2007 20:52:18 -0000 *************** *** 1300,1322 **** (defvar minibuffer-temporary-goal-position nil) ! (defun next-history-element (n) ! "Puts next element of the minibuffer history in the minibuffer. ! With argument N, it uses the Nth following element." ! (interactive "p") ! (or (zerop n) ! (let ((narg (- minibuffer-history-position n)) ! (minimum (if minibuffer-default -1 0)) elt minibuffer-returned-to-present) (if (and (zerop minibuffer-history-position) (null minibuffer-text-before-history)) (setq minibuffer-text-before-history (minibuffer-contents-no-properties))) ! (if (< narg minimum) (if minibuffer-default (error "End of history; no next item") (error "End of history; no default available"))) ! (if (> narg (length (symbol-value minibuffer-history-variable))) (error "Beginning of history; no preceding item")) (unless (memq last-command '(next-history-element previous-history-element)) --- 1300,1319 ---- (defvar minibuffer-temporary-goal-position nil) ! (defun minibuffer-history-element (nabs) ! "Puts element of the minibuffer history in the minibuffer. ! The argument NABS specifies the absolute history position." ! (let ((minimum (if minibuffer-default -1 0)) elt minibuffer-returned-to-present) (if (and (zerop minibuffer-history-position) (null minibuffer-text-before-history)) (setq minibuffer-text-before-history (minibuffer-contents-no-properties))) ! (if (< nabs minimum) (if minibuffer-default (error "End of history; no next item") (error "End of history; no default available"))) ! (if (> nabs (length (symbol-value minibuffer-history-variable))) (error "Beginning of history; no preceding item")) (unless (memq last-command '(next-history-element previous-history-element)) *************** *** 1327,1336 **** (t (point)))))) (goto-char (point-max)) (delete-minibuffer-contents) ! (setq minibuffer-history-position narg) ! (cond ((= narg -1) (setq elt minibuffer-default)) ! ((= narg 0) (setq elt (or minibuffer-text-before-history "")) (setq minibuffer-returned-to-present t) (setq minibuffer-text-before-history nil)) --- 1324,1333 ---- (t (point)))))) (goto-char (point-max)) (delete-minibuffer-contents) ! (setq minibuffer-history-position nabs) ! (cond ((= nabs -1) (setq elt minibuffer-default)) ! ((= nabs 0) (setq elt (or minibuffer-text-before-history "")) (setq minibuffer-returned-to-present t) (setq minibuffer-text-before-history nil)) *************** *** 1342,1354 **** (let ((print-level nil)) (prin1-to-string elt)) elt)) ! (goto-char (or minibuffer-temporary-goal-position (point-max)))))) (defun previous-history-element (n) "Puts previous element of the minibuffer history in the minibuffer. With argument N, it uses the Nth previous element." (interactive "p") ! (next-history-element (- n))) (defun next-complete-history-element (n) "Get next history element which completes the minibuffer before the point. --- 1339,1359 ---- (let ((print-level nil)) (prin1-to-string elt)) elt)) ! (goto-char (or minibuffer-temporary-goal-position (point-max))))) ! ! (defun next-history-element (n) ! "Puts next element of the minibuffer history in the minibuffer. ! With argument N, it uses the Nth following element." ! (interactive "p") ! (or (zerop n) ! (minibuffer-history-element (- minibuffer-history-position n)))) (defun previous-history-element (n) "Puts previous element of the minibuffer history in the minibuffer. With argument N, it uses the Nth previous element." (interactive "p") ! (or (zerop n) ! (minibuffer-history-element (+ minibuffer-history-position n)))) (defun next-complete-history-element (n) "Get next history element which completes the minibuffer before the point. *************** *** 1381,1386 **** --- 1386,1521 ---- ;; the buffer; this should be 0 for normal buffers. (1- (minibuffer-prompt-end))) + ;; isearch minibuffer history + (add-hook 'minibuffer-setup-hook 'minibuffer-history-isearch-setup) + + (defvar minibuffer-history-isearch-message-overlay) + (make-variable-buffer-local 'minibuffer-history-isearch-message-overlay) + + (defun minibuffer-history-isearch-setup () + "Set up a minibuffer for using isearch to search the minibuffer history. + Intended to be added to `minibuffer-setup-hook'." + (set (make-local-variable 'isearch-search-fun-function) + 'minibuffer-history-isearch-search) + (set (make-local-variable 'isearch-message-function) + 'minibuffer-history-isearch-message) + (set (make-local-variable 'isearch-wrap-function) + 'minibuffer-history-isearch-wrap) + (set (make-local-variable 'isearch-push-state-function) + 'minibuffer-history-isearch-push-state) + (add-hook 'isearch-mode-end-hook 'minibuffer-history-isearch-end nil t)) + + (defun minibuffer-history-isearch-end () + "Clean up the minibuffer after terminating an incremental search." + (if minibuffer-history-isearch-message-overlay + (delete-overlay minibuffer-history-isearch-message-overlay))) + + (defun minibuffer-history-isearch-search () + "Return the function for isearch that searches in the minibuffer history." + (cond + (isearch-word + (if isearch-forward 'word-search-forward 'word-search-backward)) + (t + (lambda (string bound noerror) + (let ((search-fun + ;; Use standard functions to search within minibuffer text + (cond + (isearch-regexp + (if isearch-forward 're-search-forward 're-search-backward)) + (t + (if isearch-forward 'search-forward 'search-backward)))) + found) + ;; Avoid lazy-highlighting matches in the minibuffer prompt when + ;; searching forward. Lazy-highlight calls this lambda with the + ;; bound arg, so skip the minibuffer prompt. + (if (and bound isearch-forward (< (point) (minibuffer-prompt-end))) + (goto-char (minibuffer-prompt-end))) + (or + ;; 1. First try searching in the initial minibuffer text + (funcall search-fun string + (if isearch-forward bound (minibuffer-prompt-end)) + noerror) + ;; 2. If the above search fails, start putting next/prev history + ;; elements in the minibuffer successively, and search the string + ;; in them. Do this only when bound is nil (i.e. not while + ;; lazy-highlighting search strings in the current minibuffer text). + (unless bound + (condition-case nil + (progn + (while (not found) + (cond (isearch-forward + (next-history-element 1) + (goto-char (minibuffer-prompt-end))) + (t + (previous-history-element 1) + (goto-char (point-max)))) + (setq isearch-barrier (point) isearch-opoint (point)) + ;; After putting the next/prev history element, search + ;; the string in them again, until next-history-element + ;; or previous-history-element raises an error at the + ;; beginning/end of history. + (setq found (funcall search-fun string + (unless isearch-forward + ;; For backward search, don't search + ;; in the minibuffer prompt + (minibuffer-prompt-end)) + noerror))) + ;; Return point of the new search result + (point)) + ;; Return nil when next(prev)-history-element fails + (error nil))))))))) + + (defun minibuffer-history-isearch-message (&optional c-q-hack ellipsis) + "Display the minibuffer history search prompt. + It is either the standard isearch prompt displayed by `isearch-message' + in case of a search error, or an overlay with the isearch prompt displayed + over the original minibuffer prompt in case of successful search." + (if (not (and (minibufferp) isearch-success (not isearch-error))) + ;; Use standard function `isearch-message' when not in the minibuffer, + ;; or search fails, or has an error (like incomplete regexp). + ;; This function overwrites minibuffer text with isearch message, + ;; so it's possible to see what is wrong in the search string. + (isearch-message c-q-hack ellipsis) + ;; Otherwise, put the overlay with the standard isearch prompt over + ;; the initial minibuffer prompt. + (if (overlayp minibuffer-history-isearch-message-overlay) + (move-overlay minibuffer-history-isearch-message-overlay + (point-min) (minibuffer-prompt-end)) + (setq minibuffer-history-isearch-message-overlay + (make-overlay (point-min) (minibuffer-prompt-end))) + (overlay-put minibuffer-history-isearch-message-overlay 'evaporate t)) + (overlay-put minibuffer-history-isearch-message-overlay + 'display (isearch-message-prefix c-q-hack ellipsis)) + ;; And clear any previous isearch message. + (message ""))) + + (defun minibuffer-history-isearch-wrap () + "Wrap the minibuffer history search when search is failed. + Move point to the first history element for a forward search, + or to the last history element for a backward search." + (unless isearch-word + ;; When `minibuffer-history-isearch-search' fails on reaching the + ;; beginning/end of the history, wrap the search to the first/last + ;; minibuffer history element. + (if isearch-forward + (minibuffer-history-element (length (symbol-value minibuffer-history-variable))) + (minibuffer-history-element 0)) + (setq isearch-success t)) + (goto-char (if isearch-forward (minibuffer-prompt-end) (point-max)))) + + (defun minibuffer-history-isearch-push-state () + "Save a function restoring the state of minibuffer history search. + Save `minibuffer-history-position' to the additional state parameter + in the search status stack." + `(lambda (cmd) + (minibuffer-history-isearch-pop-state cmd ,minibuffer-history-position))) + + (defun minibuffer-history-isearch-pop-state (cmd hist-pos) + "Restore the minibuffer history search state. + Go to the history element by the absolute history position `hist-pos'." + (minibuffer-history-element hist-pos)) + + ;Put this on C-x u, so we can force that rather than C-_ into startup msg (defalias 'advertised-undo 'undo) -- Juri Linkov http://www.jurta.org/emacs/