Commit c0e48b0b authored by Richard M. Stallman's avatar Richard M. Stallman

*** empty log message ***

parent 7aeff5ab
......@@ -90,6 +90,14 @@ The underscore cursor is set by putting `(cursor-type . hbar)' in
default-frame-alist. It supports variable heights, like the `bar'
cursor does.
** Filesets are collections of files. You can define a fileset in
various ways, such as based on a directory tree or based on
program files that include other program files.
Once you have defined a fileset, you can perform various operations on
all the files in it, such as visiting them or searching and replacing
in them.
** PO translation files are decoded according to their MIME headers
when Emacs visits them.
2002-04-28 Richard M. Stallman <>
* filesets.el: New file.
* replace.el (occur-accumulate-lines): Avoid incf and decf.
(occur-engine-add-prefix): New function.
(occur-engine): Avoid using macrolet, incf and decf.
;;; filesets.el --- handle group of files
;; Copyright (C) 2002 Free Software Foundation, Inc.
;; Author: Thomas Link aka (at gmx at)
;; Time-stamp: <2002-03-22>
;; Keywords: filesets convenience
;; 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, or (at your option)
;; 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
;; GNU General Public License for more details.
;; A copy of the GNU General Public License can be obtained from this
;; program's author or from the Free Software Foundation, Inc., 675 Mass
;; Ave, Cambridge, MA 02139, USA.
(defvar filesets-version "1.8.1")
(defvar filesets-homepage
;;; Commentary:
;;Define filesets, which can be opened or saved with the power one or
;;two mouse clicks only. A fileset is either a list of files, a file
;;pattern, a base directory and a search pattern (for files), or an
;;inclusion group (i.e. a base file including other files).
;;Usage: 1. Put (require 'filesets) into your start-up file. 2. Type
;;M-x filesets-edit or choose "Edit Filesets" from the menu. 3. Save
;;your customizations.
;;Caveat: Fileset names have to be unique.
;;Filesets.el adds a nifty filesets menu to your menubar. If you change
;;your filesets on the fly, don't forget to select "Save Filesets" from
;;the menu.
;;Pressing on the first item in the submenu will open all files at once.
;;Define your own function, e.g. browse-url, for opening a fileset's
;;files. Or define external viewers for opening files with other
;;programs. See `filesets-external-viewers'.
;;BTW, if you close a fileset, files, which have been changed, will
;;be silently saved. Change this behaviour by setting
;;; Supported modes for inclusion groups (`filesets-ingroup-patterns'):
;; - Elisp
;; - Emacs-Wiki (simple names only)
;; - LaTeX
;;; Known bugs:
;;; To do:
;;- better handling of different customization scenarios
;;; Credits:
;; Helpful suggestions (but no significant code) were contributed by
;;- Christoph Conrad (at gmx de)
;;- Christian Ohler (at Informatik Uni-Oldenburg DE)
;;- Richard Stallman aka RMS (at gnu org)
;;- Per Abrahamsen aka abraham (at dina kvl dk)
;;; Code:
(require 'cl))
;;; Some variables
(unless (boundp 'filesets-running-xemacs)
(defvar filesets-running-xemacs (string-match "XEmacs\\|Lucid" emacs-version)
"Non-nil means we are runninn XEmacs."))
(defvar filesets-menu-cache nil
"The whole filesets menu.")
(defvar filesets-cache-version nil
"Filesets' cached version number.")
(defvar filesets-cache-hostname nil
"Filesets' cached system name.")
(defvar filesets-ingroup-cache nil
"A plist containing files and their ingroup data.")
(defvar filesets-ingroup-paths nil
"A temporary list of path already processed when searching for
included files.")
(defvar filesets-has-changed-flag t
"Non-nil means some fileset definition has changed.")
(defvar filesets-submenus nil
"An association list with filesets menu data.")
(defvar filesets-updated-buffers nil
"A list of buffers with updated menu bars.")
(defvar filesets-menu-use-cached-flag nil
"Use cached data. See `filesets-menu-ensure-use-cached' for details.")
(defvar filesets-update-cache-file-flag nil
"Non-nil means the cache needs updating.")
(defvar filesets-ignore-next-set-default nil
"A list of custom variables for which the next `set-default' will be
(defvar filesets-output-buffer-flag nil
"Non-nil means the current buffer is an output buffer created by filesets.
Is buffer local variable.")
(defvar filesets-verbosity 1
"An integer defining the level of verbosity. 0 means no messages
at all.")
(defvar filesets-menu-ensure-use-cached
(and filesets-running-xemacs
(not (emacs-version>= 21 5)))
"Make sure (X)Emacs uses filesets' cache.
Well, if you use XEmacs (prior to 21.5?) custom.el is loaded after
init.el. This means that settings saved in the cache file (see
`filesets-menu-cache-file') will be overwritten by custom.el. In order
to ensure the use of the cache file, set this variable to t -- which is
the default for XEmacs prior to 21.5. If you want to change this value
put \"(setq filesets-menu-ensure-use-cached VALUE)\" into your startup
file -- before loading filesets.el.
So, when should you think about setting this value to t? If filesets.el
is loaded before user customizations. Thus, if (require 'filesets)
precedes the custom-set-variables command or, for XEmacs, if init.el is
loaded before custom.el, set this variable to t.")
;;; utils
(defun filesets-filter-list (lst cond-fn)
"Remove all elements not conforming to COND-FN from list LST.
COND-FN takes one argument: the current element."
; (remove* 'dummy lst :test (lambda (dummy elt)
; (not (funcall cond-fn elt)))))
(let ((rv nil))
(dolist (elt lst rv)
(when (funcall cond-fn elt)
(setq rv (append rv (list elt)))))))
(defun filesets-sublist (lst beg &optional end)
"Get the sublist of LST from BEG to END - 1."
(let ((rv nil)
(i beg)
(top (or end
(length lst))))
(while (< i top)
(setq rv (append rv (list (nth i lst))))
(setq i (+ i 1)))
(defun filesets-select-command (cmd-list)
"Select one command from CMD-LIST -- a string with space separated names."
(let ((this (shell-command-to-string
(format "which --skip-alias %s 2> /dev/null | head -n 1"
(if (equal this "")
(file-name-nondirectory (substring this 0 (- (length this) 1))))))
(defun filesets-which-command (cmd)
"Calls \"which CMD\"."
(shell-command-to-string (format "which %s" cmd)))
(defun filesets-which-command-p (cmd)
"Calls \"which CMD\" and returns non-nil if the command was found."
(when (string-match (format "\\(/[^/]+\\)?/%s" cmd)
(filesets-which-command cmd))
(defun filesets-message (level &rest args)
"Show a message only if LEVEL is greater or equal then `filesets-verbosity'."
(when (<= level (abs filesets-verbosity))
(apply 'message args)))
;;; config file
(defun filesets-save-config ()
"Save filesets' customizations."
(defun filesets-reset-fileset (&optional fileset no-cache)
"Reset the cached values for one or all filesets."
(if fileset
(setq filesets-submenus (lax-plist-put filesets-submenus fileset nil))
(setq filesets-submenus nil))
(setq filesets-has-changed-flag t)
(setq filesets-update-cache-file-flag (or filesets-update-cache-file-flag
(not no-cache))))
(defun filesets-set-config (fileset var val)
"Set-default wrapper function."
(filesets-reset-fileset fileset)
(set-default var val))
; (customize-set-variable var val))
; (filesets-build-menu))
(defun filesets-set-default (sym val &optional init-flag)
"Set-default wrapper function used in conjunction with `defcustom'."
(let ((ignore-flag (member sym filesets-ignore-next-set-default)))
(if ignore-flag
(setq filesets-ignore-next-set-default
(delete sym filesets-ignore-next-set-default))
(if init-flag
(custom-initialize-set sym val)
(set-default sym val)))
(not ignore-flag)))
(defun filesets-set-default! (sym val)
"Call `filestes-set-default' and reset cached data (i.e. rebuild menu)."
(when (filesets-set-default sym val)
(defun filesets-set-default+ (sym val)
"Call `filestes-set-default' and reset filesets' standard menu."
(when (filesets-set-default sym val)
(setq filesets-has-changed-flag t)))
; (filesets-reset-fileset nil t)))
(defun filesets-data-set-default (sym val)
"Set the default for `filesets-data'."
(if filesets-menu-use-cached-flag
(setq filesets-menu-use-cached-flag nil)
(when (default-boundp 'filesets-data)
(let ((modified-filesets
(filesets-filter-list val
(lambda (x)
(let ((name (car x))
(data (cdr x)))
(let ((elt (assoc name filesets-data)))
(or (not elt)
(not (equal data (cdr elt))))))))))
(dolist (x modified-filesets)
(filesets-reset-fileset (car x))))))
(filesets-set-default sym val))
;;; configuration
(defgroup filesets nil
"The fileset swapper."
:prefix "filesets-"
:group 'convenience)
(defcustom filesets-menu-name "Filesets"
"*Filesets' menu name."
:set (function filesets-set-default)
:type 'sexp
:group 'filesets)
(defcustom filesets-menu-path nil
"*The menu under which the filesets menu should be inserted.
XEmacs specific; see `add-submenu' for documentation."
:set (function filesets-set-default)
:type 'sexp
:group 'filesets)
(defcustom filesets-menu-before "File"
"*The name of a menu before which this menu should be added.
XEmacs specific; see `add-submenu' for documentation."
:set (function filesets-set-default)
:type 'sexp
:group 'filesets)
(defcustom filesets-menu-in-menu nil
"*Use that instead of `current-menubar' as the menu to change.
XEmacs specific; see `add-submenu' for documentation."
:set (function filesets-set-default)
:type 'sexp
:group 'filesets)
(defcustom filesets-menu-shortcuts-flag t
"*Non-nil means to prepend menus with hopefully unique shortcuts."
:set (function filesets-set-default!)
:type 'boolean
:group 'filesets)
(defcustom filesets-menu-shortcuts-marker "%_"
"*String for marking menu shortcuts."
:set (function filesets-set-default!)
:type 'string
:group 'filesets)
;(defcustom filesets-menu-cnvfp-flag nil
; "*Non-nil means show \"Convert :pattern to :files\" entry for :pattern menus."
; :set (function filesets-set-default!)
; :type 'boolean
; :group 'filesets)
(defcustom filesets-menu-cache-file
(if filesets-running-xemacs
"*File to be used for saving the filesets menu between (X)Emacs
sessions. Set this to \"\", to disable caching of menus.
Don't forget to check out `filesets-menu-ensure-use-cached'."
:set (function filesets-set-default)
:type 'file
:group 'filesets)
(defcustom filesets-menu-cache-contents
"*Stuff we want to save in `filesets-menu-cache-file'.
Possible uses: don't save configuration data in the main startup files
but in filesets's own cache. In this case add `filesets-data' to this
There is a second reason for putting `filesets-data' on this list. If
you frequently add and remove buffers on the fly to :files filesets, you
don't need to save your customizations if `filesets-data' is being
mirrored in the cache file. In this case the version in the cache file
is the current one, and the version in your startup file will be
silently updated later on.
If you want caching to work properly, at least `filesets-submenus',
`filesets-menu-cache', and `filesets-ingroup-cache' should be in this
Don't forget to check out `filesets-menu-ensure-use-cached'."
:set (function filesets-set-default)
:type '(repeat
(choice :tag "Variable"
(const :tag "filesets-submenus"
:value filesets-submenus)
(const :tag "filesets-menu-cache"
:value filesets-menu-cache)
(const :tag "filesets-ingroup-cache"
:value filesets-ingroup-cache)
(const :tag "filesets-data"
:value filesets-data)
(const :tag "filesets-external-viewers"
:value filesets-external-viewers)
(const :tag "filesets-ingroup-patterns"
:value filesets-ingroup-patterns)
(const :tag "filesets-be-docile-flag"
:value filesets-be-docile-flag)
(sexp :tag "Other" :value nil)))
:group 'filesets)
(defcustom filesets-cache-fill-content-hooks nil
"*Hooks to run when writing the contents of filesets' cache file.
The hook is called with the cache file as current buffer and the cursor
at the last position. I.e. each hook has to make sure that the cursor is
at the last position.
Possible uses: If you don't want to save `filesets-data' in your normal
configuration file, you can add a something like this
\(lambda ()
\(insert (format \"(setq-default filesets-data '%S)\"
\(newline 2))
to this hook.
Don't forget to check out `filesets-menu-ensure-use-cached'."
:set (function filesets-set-default)
:type 'hook
:group 'filesets)
(defcustom filesets-cache-hostname-flag nil
"*Non-nil means cache the hostname. If the current name differs from
the cached one, rebuild the menu and create a new cache file."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-cache-save-often-flag nil
"*Non-nil means save buffer on every change of the filesets menu.
If this variable is set to nil and if Emacs crashes, the cache and
filesets-data could get out of sync. Set this to t if this happens from
time to time or if the fileset cache causes troubles."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-max-submenu-length 25
"*Maximum length of submenus.
Set this value to 0 to turn menu splitting off. BTW, parts of submenus
will not be rewrapped if their length exceeds this value."
:set (function filesets-set-default)
:type 'integer
:group 'filesets)
(defcustom filesets-max-entry-length 50
"*Truncate names of splitted submenus to this length."
:set (function filesets-set-default)
:type 'integer
:group 'filesets)
(defcustom filesets-browse-dir-fn 'dired
"*A function or command used for browsing directories.
When using an external command, \"%s\" will be replaced with the
directory's name.
Note: You have to manually rebuild the menu if you change this value."
:set (function filesets-set-default)
:type '(choice :tag "Function:"
(const :tag "dired"
:value dired)
(list :tag "Command"
:value ("" "%s")
(string :tag "Name")
(string :tag "Arguments"))
(function :tag "Function"
:value nil))
:group 'filesets)
(defcustom filesets-open-file-fn 'filesets-find-or-display-file
"*The function used for opening files.
`filesets-find-or-display-file' ... Filesets' default function for
visiting files. This function checks if an external viewer is defined
for a specific file type. Either this viewer, if defined, or
`find-file' will be used to visit a file.
`filesets-find-file' ... An alternative function that always uses
`find-file'. If `filesets-be-docile-flag' is true, a file, which isn't
readable, will not be opened.
Caveat: Changes will take effect only after rebuilding the menu."
:set (function filesets-set-default)
:type '(choice :tag "Function:"
(const :tag "filesets-find-or-display-file"
:value filesets-find-or-display-file)
(const :tag "filesets-find-file"
:value filesets-find-file)
(function :tag "Function"
:value nil))
:group 'filesets)
(defcustom filesets-save-buffer-fn 'save-buffer
"*The function used to save a buffer.
Caveat: Changes will take effect after rebuilding the menu."
:set (function filesets-set-default)
:type '(choice :tag "Function:"
(const :tag "save-buffer"
:value save-buffer)
(function :tag "Function"
:value nil))
:group 'filesets)
(defcustom filesets-find-file-delay
(if (and filesets-running-xemacs gutter-buffers-tab-visible-p)
"*Delay before calling find-file.
This is for calls via `filesets-find-or-display-file'
or `filesets-find-file'.
Set this to 0, if you don't use XEmacs' buffer tabs."
:set (function filesets-set-default)
:type 'number
:group 'filesets)
(defcustom filesets-be-docile-flag nil
"*Non-nil means don't complain if a file or a directory doesn't exist.
This is useful if you want to use the same startup files in different
computer environments."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-sort-menu-flag t
"*Non-nil means sort the filesets menu alphabetically."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-sort-case-sensitive-flag t
"*Non-nil means sorting of the filesete menu is case sensitive."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-tree-max-level 3
"*Maximum scan depth for directory trees.
A :tree fileset is defined by a base directory the contents of which
will be recursively added to the menu. filesets-tree-max-level tells up
to which level the directory structure should be scanned/listed,
i.e. how deep the menu should be. Try something like
\(\"HOME -- only one level\"
\(:tree \"~\" \"^[^.].*[^~]$\")
\(:tree-max-level 1)
\(:filter-dirs-flag t))
\(\"HOME -- up to 3 levels\"
\(:tree \"~\" \"^[^.].*[^~]$\")
\(:tree-max-level 3)
\(:filter-dirs-flag t))
and it should become clear what this option is about. In any case,
including directory trees to the menu can take a lot of memory."
:set (function filesets-set-default)
:type 'integer
:group 'filesets)
(defcustom filesets-commands
`(("Query Replace"
("Query Replace (regexp)"
("Grep <<selection>>"
("-n " filesets-get-quoted-selection " " "<<file-name>>"))
("Run Shell Command"
"*Commands to run on filesets.
An association list of names, functions, and an argument list (or a
function that returns one) to be run on a filesets' files.
The argument <file-name> or <<file-name>> (quoted) will be replaced with
the filename."
:set (function filesets-set-default+)
:type '(repeat :tag "Commands"
(list :tag "Definition" :value ("")
(string "Name")
(choice :tag "Command"
(string :tag "String")
(function :tag "Function"))
(repeat :tag "Argument List"
(choice :tag "Arguments"
(sexp :tag "Sexp"
:value nil)
(string :tag "File Name"
:value "<file-name>")
(string :tag "Quoted File Name"
:value "<<file-name>>")
(function :tag "Function"
:value nil)))))
:group 'filesets)
(defcustom filesets-external-viewers
; ((ps-cmd (or (and (boundp 'my-ps-viewer) my-ps-viewer)
; (filesets-select-command "ggv gv")))
; (pdf-cmd (or (and (boundp 'my-ps-viewer) my-pdf-viewer)
; (filesets-select-command "xpdf acroread")))
; (dvi-cmd (or (and (boundp 'my-ps-viewer) my-dvi-viewer)
; (filesets-select-command "xdvi tkdvi")))
; (doc-cmd (or (and (boundp 'my-ps-viewer) my-doc-viewer)
; (filesets-select-command "antiword")))
; (pic-cmd (or (and (boundp 'my-ps-viewer) my-pic-viewer)
; (filesets-select-command "gqview ee display"))))
((ps-cmd "ggv")
(pdf-cmd "xpdf")
(dvi-cmd "xdvi")
(doc-cmd "antiword")
(pic-cmd "gqview"))
`(("^.+\\..?html?$" browse-url
((:ignore-on-open-all t)))
("^.+\\.pdf$" ,pdf-cmd
((:ignore-on-open-all t)
(:ignore-on-read-text t)
(:constraint-flag ,pdf-cmd)))
("^.+\\.e?ps\\(.gz\\)?$" ,ps-cmd
((:ignore-on-open-all t)
(:ignore-on-read-text t)
(:constraint-flag ,ps-cmd)))
("^.+\\.dvi$" ,dvi-cmd
((:ignore-on-open-all t)
(:ignore-on-read-text t)
(:constraint-flag ,dvi-cmd)))
("^.+\\.doc$" ,doc-cmd
((:capture-output t)
(:ignore-on-read-text t)
(:constraint-flag ,doc-cmd)))
("^.+\\.\\(tiff\\|xpm\\|gif\\|pgn\\)$" ,pic-cmd
((:ignore-on-open-all t)
(:ignore-on-read-text t)
(:constraint-flag ,pic-cmd)))))
"*Association list of file patterns and external viewers for use with
Has the form ((FILE-PATTERN VIEWER PROPERTIES) ...), VIEWER being either a
function or a command name as string.
Properties is an association list determining filesets' behaviour in
several conditions. Choose one from this list:
:ignore-on-open-all ... Don't open files of this type automatically --
i.e. on open-all-files-events or when running commands
:capture-output ... capture an external viewer output
:constraintp FUNCTION ... use this viewer only if FUNCTION returns non-nil
:constraint-flag SYMBOL ... use this viewer only if SYMBOL is non-nil
:open-hook HOOK ... run hooks after spawning the viewer -- mainly useful
in conjunction with :capture-output
:args (FORMAT-STRING or SYMBOL or FUNCTION) ... a list of arguments
\(defaults to (list \"%S\")) when using shell commands
Avoid modifying this variable and achieve minor speed-ups by setting the