[Top][All Lists]

[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: 16 Mar 2006 05:22:13 -0800
User-agent: G2/0.2

New release.


- Added option (globalff-filter-regexps) to filter out unwanted files
from the output
- Added pgup/pgdown bindings for list navigation.

;;; 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
;; 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
If nil then the system default database is used.")

(defvar globalff-filter-regexps nil
  "List of regular expressions to filter out unwanted files from the

(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 "<prior>") 'globalff-previous-page)
    (define-key map (kbd "<next>") 'globalff-next-page)
    (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)
  "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)))))
        ;; Insert the text, advancing the process marker.
        (goto-char (process-mark process))

        (let ((begin (line-beginning-position)))
          (insert string)

          ;; filter out unwanted lines
          (if globalff-filter-regexps
              ;; current line can be incomplete, so store and remove
              ;; it before filtering
              (let ((line (buffer-substring (line-beginning-position)
                (delete-region (line-beginning-position)

                (dolist (regexp globalff-filter-regexps)
                  (goto-char begin)
                  (flush-lines regexp))

                (goto-char (point-max))
                (insert line)))

          (set-marker (process-mark process) (point))))

      (if empty

      (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 selection to the previous line."
  (globalff-move-selection 'next-line -1))

(defun globalff-next-line ()
  "Move selection to the next line."
  (globalff-move-selection 'next-line 1))

(defun globalff-previous-page ()
  "Move selection back with a pageful."
  (globalff-move-selection 'scroll-down nil))

(defun globalff-next-page ()
  "Move selection forward with a pageful."
  (globalff-move-selection 'scroll-up nil))

(defun globalff-move-selection (movefunc movearg)
  "Move the selection marker to a new position determined by
  (unless (= (buffer-size (get-buffer globalff-buffer)) 0)
    (let ((old-window (selected-window)))
      (select-window (get-buffer-window globalff-buffer))

      (condition-case nil
          (funcall movefunc movearg)
        (beginning-of-buffer (goto-char (point-min)))
        (end-of-buffer (goto-char (point-max))))

      ;; if line end is point-max then it's either an incomplete line
      ;; the end of the output, so move up a line
      (if (= (line-end-position) (point-max))
          (next-line -1))


      (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)

(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)

    (with-current-buffer globalff-buffer
    (globalff-set-state "idle")

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

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

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

                             (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
                                    "/"  (if globalff-regexp-search
                                    ":" state))

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

(defun globalff-toggle-regexp-search ()
  "Toggle state of regular expression pattern matching."
  (setq globalff-regexp-search (not globalff-regexp-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."
  (unless globalff-regexp-search
    (let* ((pattern (minibuffer-contents))
           (len (length pattern)))
      (if (> len 2)
            (if  (and (= (aref pattern 0) ?*)
                      (= (aref pattern (1- len)) ?*))
                ;; remove asterisks from around pattern
                  (delete-char 1)
                  (delete-char -1))

                ;; put asterisks around pattern
              (insert "*")
              (insert "*")))))))

(defun globalff ()
  "Start global find file."
  (let ((winconfig (current-window-configuration)))
    (pop-to-buffer globalff-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

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

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

      (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)
     (with-current-buffer globalff-buffer
       (buffer-substring-no-properties (overlay-start globalff-overlay)
(provide 'globalff)
;;; globalff.el ends here

reply via email to

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