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

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

Re: globalff.el --- Global find file


From: spamfilteraccount
Subject: Re: globalff.el --- Global find file
Date: 13 Mar 2006 06:09:11 -0800
User-agent: G2/0.2

Hi,

I added a few new features. Thanks to those who sent me patches and
ideas.


The changes:

- The current state of the search process is indicated on the mode
line.

- Added option for case sensitive search. It can also be toggled
  during searching. The current setting is indicated on the mode line.

- The same goes for regexp search.

- Added key to put/remove asterisks around pattern if glob matching is
  used. This make it easier to use globs, since by default glob
patterns
  have to match the file name exactly.


There is a rarely occuring bug which I can't put my finger on.
Sometimes the selection spans multiple lines instead of a single line.
It's not a big deal, because pressing the up or down key restores the
normal state of things, but it's bugging me. If you can reproduce the
fault reliably or know the cause, let me know.

/Tamas


;;; globalff.el --- Global find file

;; Copyright (C) 2006  Free Software Foundation, Inc.

;; This file 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, or (at your option)
;; any later version.

;; This file 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., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;; Start with M-x globallff and type in any substring of any path on
;; your system to display the matching files. The displayed list is
;; updated dynamically as you type more characters or delete some.
;;
;; Needs an up-to-date locate database for file name searching.
;;
;; Since the search is based on locate you can use any globbing
;; characters allowed by the locate command.
;;
;; You can move up/down the list with the cursor keys (I know these
;; bindings are not very Emacsian, but I happen to like them) and
;; select a file to open with Enter.
;;
;; You can quit with C-g.
;;
;; See the variable `globalff-map' for further bindings.
;;

;;; Code:

;;
;; User configurable variables
;;

(defvar globalff-case-sensitive-search nil
  "Whether to use case sensitive pattern matching.")

(defvar globalff-regexp-search nil
  "Whether to use regular expression pattern matching.")

(defvar globalff-databases nil
  "List of database files separated with colon to be used by the locate
command.
If nil then the system default database is used.")

(defvar globalff-minimum-input-length 3
  "The minimum number of characters needed to start file searching.")

(defvar globalff-search-delay 0.5
  "Idle time after last input event, before starting the search.")

(defvar globalff-map
  (let ((map (copy-keymap minibuffer-local-map)))
    (define-key map (kbd "<down>") 'globalff-next-line)
    (define-key map (kbd "<up>") 'globalff-previous-line)
    (define-key map (kbd "C-c") 'globalff-toggle-case-sensitive-search)
    ;; I wanted to choose C-t as a homage to iswitchb, but
    ;; transpose-chars can be useful during pattern editing
    (define-key map (kbd "C-r") 'globalff-toggle-regexp-search)
    (define-key map (kbd "C-s") 'globalff-toggle-around-globs)
    map)
  "Keymap for globalff.")

;;
;; End of user configurable variables
;;

(defvar globalff-idle-timer nil
  "Idle timer for monitoring typed characters.")

(defconst globalff-buffer "*globalff*"
  "Buffer used for finding files.")

(defvar globalff-previous-input ""
  "The previous input substring used for searching.")

(defvar globalff-overlay nil
  "Overlay used to highlight the current selection.")


(defun globalff-output-filter (process string)
  "Avoid moving of point if the buffer is empty."
  (with-current-buffer globalff-buffer
    (let* ((empty (= (buffer-size) 0))
           (moving (and (not empty)
                        (= (point) (process-mark process)))))
      (save-excursion
        ;; Insert the text, advancing the process marker.
        (goto-char (process-mark process))
        (insert string)
        (set-marker (process-mark process) (point)))

      (if empty
          (globalff-mark-current-line))

      (if moving (goto-char (process-mark process))))))


(defun globalff-mark-current-line ()
  "Mark current line with a distinctive color."
  (move-overlay globalff-overlay (point-at-bol) (point-at-eol)))


(defun globalff-previous-line ()
  "Move mark to the previous line."
  (interactive)
  (unless (with-current-buffer globalff-buffer
            (= (point-min) (line-beginning-position)))
    (globalff-move-mark -1)))


(defun globalff-next-line ()
  "Move mark to the next line."
  (interactive)
  (unless (with-current-buffer globalff-buffer
            (save-excursion
              (forward-line)
              (eobp)))
    (globalff-move-mark +1)))


(defun globalff-move-mark (direction)
  (let ((old-window (selected-window)))
    (select-window (get-buffer-window globalff-buffer))

    (next-line direction)
    (globalff-mark-current-line)

    (select-window old-window)))


(defun globalff-process-sentinel (process event)
  "Prevent printing of process status messages into the output buffer."
  (if (eq nil (process-status globalff-buffer))
      (globalff-set-state "finished")))


(defun globalff-check-input ()
  "Check input string and start/stop search if necessary."
  (unless (equal (minibuffer-contents) globalff-previous-input)
    (globalff-restart-search)))


(defun globalff-restart-search ()
  "Stop the current search if any and start a new one if needed."
  (let ((input (minibuffer-contents)))
    (setq globalff-previous-input input)

    (globalff-kill-process)
    (with-current-buffer globalff-buffer
      (erase-buffer))
    (globalff-set-state "idle")

    (unless (or (equal input "")
                (< (length input) globalff-minimum-input-length))
      (let ((process (apply 'start-process "globalff-process"
globalff-buffer
                            "locate"
                            (append
                             (unless globalff-case-sensitive-search
                               (list "-i"))

                             (if globalff-regexp-search
                               (list "-r"))

                             (when globalff-databases
                               (list (concat "--database="
                                             globalff-databases)))

                             (list input)))))
        (globalff-set-state "searching")
        (set-process-filter process 'globalff-output-filter)
        (set-process-sentinel process 'globalff-process-sentinel)))))


(defun globalff-kill-process ()
  "Kill find process."
  (if (eq 'run (process-status globalff-buffer))
      (delete-process globalff-buffer)))


(defun globalff-set-state (state)
  "Set STATE in mode line."
  (with-current-buffer globalff-buffer
    (setq mode-line-process (concat ":" (if
globalff-case-sensitive-search
                                            "case"
                                          "nocase")
                                    "/"  (if globalff-regexp-search
                                            "regexp"
                                           "glob")
                                    ":" state))
    (force-mode-line-update)))


(defun globalff-toggle-case-sensitive-search ()
  "Toggle state of case sensitive pattern matching."
  (interactive)
  (setq globalff-case-sensitive-search (not
globalff-case-sensitive-search))
  (globalff-restart-search))


(defun globalff-toggle-regexp-search ()
  "Toggle state of regular expression pattern matching."
  (interactive)
  (setq globalff-regexp-search (not globalff-regexp-search))
  (globalff-restart-search))


(defun globalff-toggle-around-globs ()
  "Put/remove asterisks around pattern if glob matching is used. This
make it easier to use globs, since by default glob patterns have to
match the file name exactly."
  (interactive)
  (unless globalff-regexp-search
    (let* ((pattern (minibuffer-contents))
           (len (length pattern)))
      (if (> len 2)
          (save-excursion
            (if  (and (= (aref pattern 0) ?*)
                      (= (aref pattern (1- len)) ?*))
                ;; remove asterisks from around pattern
                (progn
                  (beginning-of-line)
                  (delete-char 1)
                  (end-of-line)
                  (delete-char -1))

                ;; put asterisks around pattern
              (beginning-of-line)
              (insert "*")
              (end-of-line)
              (insert "*")))))))


(defun globalff ()
  "Start global find file."
  (interactive)
  (let ((winconfig (current-window-configuration)))
    (pop-to-buffer globalff-buffer)
    (erase-buffer)
    (setq mode-name "GlobalFF")

    (unless globalff-overlay
      (setq globalff-overlay (make-overlay 0 0))
      (overlay-put globalff-overlay 'face 'region))

    (globalff-set-state "idle")
    (setq globalff-previous-input "")
    (setq globalff-idle-timer
          (run-with-idle-timer globalff-search-delay t
'globalff-check-input))

    (with-current-buffer globalff-buffer
      (setq cursor-type nil))

    (unwind-protect
        (let ((minibuffer-local-map globalff-map))
          (read-string "substring: "))

      (globalff-kill-process)
      (cancel-timer globalff-idle-timer)

      (with-current-buffer globalff-buffer
        (setq cursor-type t))

      (set-window-configuration winconfig)))

  (unless (= (buffer-size (get-buffer globalff-buffer)) 0)
    (find-file
     (with-current-buffer globalff-buffer
       (buffer-substring-no-properties (overlay-start globalff-overlay)
                                       (overlay-end
globalff-overlay))))))
 
 
(provide 'globalff)
;;; globalff.el ends here



reply via email to

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