From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: Oliver Scholz Newsgroups: gmane.emacs.devel Subject: `buffer-list' and the frame-parameter `buffer-predicate' Date: Fri, 16 Aug 2002 20:27:00 +0200 Organization: Olymp Sender: emacs-devel-admin@gnu.org Message-ID: NNTP-Posting-Host: localhost.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: main.gmane.org 1029517281 29678 127.0.0.1 (16 Aug 2002 17:01:21 GMT) X-Complaints-To: usenet@main.gmane.org NNTP-Posting-Date: Fri, 16 Aug 2002 17:01:21 +0000 (UTC) Return-path: Original-Received: from quimby.gnus.org ([80.91.224.244]) by main.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 17fkTL-0007iT-00 for ; Fri, 16 Aug 2002 19:01:15 +0200 Original-Received: from monty-python.gnu.org ([199.232.76.173]) by quimby.gnus.org with esmtp (Exim 3.12 #1 (Debian)) id 17fktM-0000pX-00 for ; Fri, 16 Aug 2002 19:28:09 +0200 Original-Received: from localhost ([127.0.0.1] helo=monty-python.gnu.org) by monty-python.gnu.org with esmtp (Exim 4.10) id 17fkUH-0003HA-00; Fri, 16 Aug 2002 13:02:13 -0400 Original-Received: from list by monty-python.gnu.org with tmda-scanned (Exim 4.10) id 17fkTM-0002qW-00 for emacs-devel@gnu.org; Fri, 16 Aug 2002 13:01:16 -0400 Original-Received: from mail by monty-python.gnu.org with spam-scanned (Exim 4.10) id 17fkTK-0002pM-00 for emacs-devel@gnu.org; Fri, 16 Aug 2002 13:01:15 -0400 Original-Received: from main.gmane.org ([80.91.224.249]) by monty-python.gnu.org with esmtp (Exim 4.10) id 17fkTJ-0002pH-00 for emacs-devel@gnu.org; Fri, 16 Aug 2002 13:01:13 -0400 Original-Received: from root by main.gmane.org with local (Exim 3.35 #1 (Debian)) id 17fkSG-0007f8-00 for ; Fri, 16 Aug 2002 19:00:08 +0200 Original-To: emacs-devel@gnu.org X-Injected-Via-Gmane: http://gmane.org/ Original-Received: from news by main.gmane.org with local (Exim 3.35 #1 (Debian)) id 17fkI7-00076G-00 for ; Fri, 16 Aug 2002 18:49:39 +0200 Original-Path: hermes!nobody Original-Newsgroups: gmane.emacs.devel Original-Lines: 444 Original-NNTP-Posting-Host: dialin-145-254-194-057.arcor-ip.net Original-X-Trace: main.gmane.org 1029516579 27286 145.254.194.57 (16 Aug 2002 16:49:39 GMT) Original-X-Complaints-To: usenet@main.gmane.org Original-NNTP-Posting-Date: Fri, 16 Aug 2002 16:49:39 +0000 (UTC) X-Operating-System: Linux from Scratch X-Attribution: os X-Face: "HgH2sgK|bfH$;PiOJI6|qUCf.ve<51_Od(%ynHr?=>znn#~#oS>",F%B8&\vus),2AsPYb -n>PgddtGEn}s7kH?7kH{P_~vu?]OvVN^qD(L)>G^gDCl(U9n{:d>'DkilN!_K"eNzjrtI4Ya6;Td% IZGMbJ{lawG+'J>QXPZD&TwWU@^~A}f^zAb[Ru;CT(UA]c& User-Agent: Gnus/5.090008 (Oort Gnus v0.08) Emacs/21.2 (i686-pc-linux-gnu) Cancel-Lock: sha1:S1AFeeBugv3WMkwjqS22zGlT9Uw= Errors-To: emacs-devel-admin@gnu.org X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.0.11 Precedence: bulk List-Help: List-Post: List-Subscribe: , List-Id: Emacs development discussions. List-Unsubscribe: , List-Archive: Xref: main.gmane.org gmane.emacs.devel:6583 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:6583 --=-=-= I tried to write a frame-local minor mode that was intended to provide a feature to deal with the metric tons of buffers and that could be useful -- in my humble opinion -- especially for beginners. In the middle of writing this package I discovered that the whole approach has a bug which makes it unusable. I can think of no way to get around this, except for a change in Emacs itself. (But I may, of course, be missing something.) So this is the feature that I want to implement: it should be possible to "dedicate" a frame to a certain pre-configured type of buffers. That means that this special frame hides all buffers that it doesn't cover. I think, it is best explained with an example of the possible usage. Say, I want to have two frames, one for Gnus and the other one for all the rest of my editing. The effect of my mode should be that all functions for buffer-listing or buffer-switching in the Gnus-frame show only resp. apply only to the *Group*, *Summary*, *Article* and the message-buffers, while exactly those buffer are not visible in the other frame. In other words: it would seem as if Gnus were running in a separate instance of Emacs, while, of course, it still _is_ the same instance with all the benefits implied by this. The same could be useful for Emacs/W3 (separate browser-frame), dired (separate file-manager frame), shell-mode (separate Emacs-terms) or whatever. I could even think of, say, frames dedicated to a certain programming-project. Now, my approach is to add a predicate-function to the frame-parameter `buffer-predicate' and to advise the function `buffer-list' to return only buffers for which this predicate function returns non-nil. This way the mode bypasses the myriads of available buffer-switch- and -list-functions. The big, big problem is that this bypasses functions like `save-buffers-kill-emacs', too. And exactly this makes it unusable. So here is my petition: change `buffer-list' to return _always_ only buffers for which the function in the frame-paramter 'buffer-predicate returns non-nil. And add a second, underlying function for the internal use in functions like `save-buffers-kill-emacs'. Provided, of course, that you find this feature useful enough, that you want to add it to Emacs. I have attached the unfinished code that I have written so far; so you can see what I am aiming at. Or at least, that I am serious about this. [BTW: one thing that puzzles me: this works for functions like `iswitchb-mode' or the according function in `ido' or for `bs-show' and -- unfortunately -- for `save-buffers-kill-emacs'. But not for `switch-to-buffer'.] -- Oliver --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=sframes.el Content-Transfer-Encoding: 8bit ;;; sframes.el --- special-purpose frames ;; Copyright (C) 2002 Oliver Scholz ;; Author: Oliver Scholz ;; Keywords: convenience ;; 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: ;; EXPERIMENTAL! DO NOT USE! This package is not finished. Moreover: ;; the parts that are written by now are not well-tested. In fact, ;; there are a few bugs in here. And among them is a major, a critical ;; bug: When the user calls `save-buffers-kill-emacs' while the ;; selected frame is an Sframe this function gets the list of buffers ;; form the _advised_ function `buffer-list'. As a result Emacs will ;; possibly quit unconditionally, even if there are modified buffers, ;; that are not yet saved. EXPERIMENTAL! DO NOT USE! ;; CAVEAT! This packages advises the functions `buffer-list' and ;; `delete-frame'. I know that using `defadvice' in a distributed ;; package is evil, but I am not aware of another way to deal with ;; this, because `delete-frame' lacks a hook and I had to make sure, ;; that `buffer-list' honours the frame-parameter 'buffer-predicate. ;; ;; Sframes allows you to "dedicate" frames to certain purposes. For a ;; frame to be dedicated means here: This frame hides all buffers, ;; except buffers of a certain, specified kind. For example, you can ;; dedicate a frame to Gnus, so that all commands to list or to switch ;; buffers show only Gnus-related buffers. Some of such frame-types ;; are predefined in the variable `sframes-type-alist' (see the ;; doc-string for detaills). You can customize this variable and some ;; other stuff with `M-x customize-group RET sframes RET' ;; ;; To make a frame an "Sframe", do `M-x sframe-mode RET' while this ;; frame is selected. This aktivates the buffer-local minor mode of ;; the same name and make the frame an Sframe of the type "Everything ;; else" (see below). To dedicate this frame to another purpose, do ;; `M-x sframes-dedicate-frame-command RET'. This prompts (with completion) ;; for a type and activates it. ;; Sframes of the type "Everything else" are special in the sense, ;; that they are dedicated to all buffers that are _not_ treated by ;; any other Sframe. ;; The command `sframes-make-frame' prompts for a type, creates a ;; brand-new frame and activates `sframes-mode'. Depending on the ;; type, it my also call a function before the creation of the frame ;; and another one after the creation of the frame. (See the doc ;; string of `sframes-type-alist'.) ;; Example: You have a frames A. Now activate `sframes-mode' for ;; it. This frame is now an Sframe of the type "Everything" else. So ;; far nothing in its behaviour has changed. ;; Now create a frame B dedicated to Gnus with `M-x sframes-make-frame ;; RET Gnus RET'. The new buffer B shows only Gnus-buffers (typically ;; *Group*, *Summary*, *Article* and some buffers in message-mode). Go ;; back to frame a and try to switch to a Gnus-buffer. You can't. ;; Delete frame B with `C-x 5 0'. Now try again to switch a ;; Gnus-buffer in A. Now this is possible again. ;; It would be nice to provide a prefix to temporarily disable the ;; effects of `sframes-mode'. But for this it is necessary to hack the ;; myriads of functions to switch buffers or to list buffers. You can ;; easily do it yourself for your switch-function du jour like this: ;; FIXME: ;; (defadvice (around disable-sframes-mode ;; activate) ;; (let ((sframes-mode nil)) ;; ad-do-it)) ;;; Code: (eval-when-compile (require 'cl)) ;;;; Variables (defvar sframes-type-alist ;; Name/Keyword | Predicates | function-before | function-after | Change-Name | Parameters FIXME '(("Shell" (eshell-mode shell-mode) nil nil nil nil) ("Dired" (dired-mode) nil nil nil nil) ("W3" (w3-mode) nil nil nil nil) ("Gnus" (gnus-group-mode gnus-summary-mode gnus-article-mode message-mode) nil nil nil nil)) "List of types of Sframes. FIXME") ;; The user should not be able to remove this type: so we add it to ;; `sframes-type-alist' on the fly. (defconst sframes-default-type-spec '("Everything else" sframes-orphan-buffer-p nil nil nil nil)) (defvar sframes-predicate-functions nil "List of predicate-functions currently used by existing Sframes.") (defgroup sframes nil "FIXME" :group 'local) (defcustom sframes-mode nil ;;; FIXME: Perhaps this shouldn't be a defcustom? "Non-nil if Sframes mode is enabled. See the command `sframes-mode' for a description of this minor-mode. Setting this variable directly does not take effect; use either \\[customize] or the function `sframes-mode'." :set (lambda (symbol value) (funcall symbol (or value 0))) :initialize 'custom-initialize-default :group 'sframes :type 'boolean) (make-variable-frame-local 'sframes-mode) (defcustom sframes-mode-hook nil "Hook run at the end of function `sframes-mode'." :group 'sframes :type 'hook) (add-minor-mode 'sframes-mode 'nil (when (boundp 'sframes-mode-map) (symbol-value 'sframes-mode-map))) ;;; Accessor-functions for `sframes-alist' (defmacro sframes-spec-predicates (spec) `(nth 1 ,spec)) (defmacro sframes-spec-before-function (spec) `(nth 2 ,spec)) (defmacro sframes-spec-after-function (spec) `(nth 3 ,spec)) (defmacro sframes-spec-name (spec) `(nth 4 ,spec)) (defmacro sframes-spec-parameters (spec) `(nth 5 ,spec)) ;;;; Other Macros (defmacro sframes-setq (&rest args) "Modify paramaters of the current frame. This is like `setq', but for frame-local variables." (let ((list nil)) (while args (push `(cons ',(pop args) ,(pop args)) list)) `(modify-frame-parameters nil (list ,@list)))) ;;;; defadvices (defadvice buffer-list (after sframes-buffer-list last activate) "Check the buffers returned by `buffer-list'. Modify the return value of `buffer-list' so, that the final list contains only buffers for which the function in the frame-parameter buffer-predicate returns non-nil (provided there is such a function)." (when sframes-mode (let ((func (frame-parameter nil 'buffer-predicate))) (when func (setq ad-return-value (delq nil (mapcar (lambda (buffer) (if (funcall func buffer) buffer nil)) ad-return-value))))))) ;; `delete-frame' lacks a hook: ;; FIXME ;; (defadvice delete-frame (around sframes-delete ;; (&optional frame force) activate) ;; (if sframes-mode ;; (unless (eq sframes-frame-type (car sframes-default-type-spec)) ;; (unwind-protect ;; (let* ((inhibit-quit t) ;; (this-frame (or frame (selected-frame))) ;; (func (frame-parameter this-frame 'buffer-predicate))) ;; (setq sframes-predicate-functions ;; (delq func sframes-predicate-functions))) ;; ad-do-it)) ;; ad-do-it)) ;;;; minor mode definition ;; The following is the modified expansion of ;; `define-minor-mode'. Unfortunately `define-minor-mode' is not ;; intended to work with frame-local variables. So I had to do it this ;; way. (defun sframes-mode (&optional arg) "Toggle Sframes mode in the current frame. With arg, turn Sframes mode off if and only if arg is a non-positive number; if arg is nil, toggle Sframes Lock mode; anything else turns Sframes mode on. Sframes mode is a frame local minor mode that allows to dedicate frames to a certain type of buffers. Sframes mode will hide every other buffer in the thus dedicated frame. See the variable `sframes-mode-alist' for a list of available types. If Sframes mode is on, you can change the type with the command `sframes-dedicate-frame'. The default type is \"Everything else\"." (interactive) (sframes-setq sframes-mode (if arg (> (prefix-numeric-value arg) 0) (not sframes-mode))) (if sframes-mode ;; Turn sframes-mode on. ;; --> Save certain frame-parameters and make current-frame an ;; Sframe of the default type. (progn (sframes-save-frame-parameters (selected-frame) '(buffer-predicate name)) ;FIXME (sframes-dedicate-frame (selected-frame) sframes-default-type-spec)) ;; Turn sframes-mode off --> restore everything. (sframes-restore-parameters (selected-frame))) ;FIXME (run-hooks 'sframes-mode-hook (if sframes-mode 'sframes-mode-on-hook 'sframes-mode-off-hook)) (when (interactive-p) (message "Sframes mode %sabled" (if sframes-mode "en" "dis"))) (force-mode-line-update) sframes-mode) ;; FIXME: not implemented, yet. (define-minor-mode global-sframes-mode "FIXME" :global t :lighter " SF") ;;;; Functions (defun sframes-dedicate-frame (frame spec) "Change the type of the SFrame FRAME according to SPEC." (when sframes-mode ;; Remove current buffer-predicate-function from ;; `sframes-predicate-functions'. (let ((pfunc (frame-parameter frame 'buffer-predicate))) (unless (eq pfunc (sframes-spec-predicates sframes-default-type-spec)) (setq sframes-predicate-functions (delq pfunc sframes-predicate-functions)))) ;; Set frame-parameter `buffer-predicate' to the new ;; predicate-function and update `sframes-predicate-functions' (let ((pfunc (sframes-make-predicate-function (sframes-spec-predicates spec)))) (modify-frame-parameters frame (list (cons 'buffer-predicate pfunc))) (unless (eq pfunc (sframes-spec-predicates sframes-default-type-spec)) (push pfunc sframes-predicate-functions))))) ;; FIXME (defun sframes-save-frame-parameters (frame parameters) "Save current frame-parameters listed in PARAMETERS. The function `sframes-restore-parameters' is used to restore the saved values." (or frame (setq frame (selected-frame))) (let ((current-params (frame-parameters frame)) (new-params (copy-sequence parameters)) (param nil)) (while new-params ;; Put everything on a symbol in the frame-paramter list. ;; UNLESS there is already something saved. (setq param (assq (pop new-params) current-params)) (unless (get (car param) 'sframes-saved) (put (car param) 'sframes-saved (cdr param)))))) ;; FIXME (defun sframes-restore-parameters (frame) "Restore the modified parameters of frame FRAME." (or frame (setq frame (selected-frame))) (let ((params (delq nil (mapcar (lambda (pair) (let ((saved (car pair))) (if (get saved 'sframes-saved) (cons saved (get saved 'sframes-saved)) nil))) (frame-parameters frame))))) (modify-frame-parameters frame params))) (defun sframes-dedicate-frame-command (type) "Change the type of the selected Sframe." (interactive (sframes-read-from-minibuffer)) (sframes-dedicate-frame-by-type nil type)) (defun sframes-dedicate-frame-by-type (frame type) "Change the type of the Sframe FRAME. If FRAME is nil, change the type of the selected frame." (sframes-dedicate-frame frame (or (assoc type (cons sframes-default-type-spec sframes-type-alist)) (error "Unknown Sframe-type: %s" type)))) (defun sframes-make-frame (type) "Make a new Sframe of type TYPE." (interactive (sframes-read-from-minibuffer)) (let ((frame (make-frame))) (sframes-dedicate-frame-by-type frame type))) (defun sframes-read-from-minibuffer () (let ((completion-ignore-case t)) (list (completing-read "Frame type (default: FIXME): " (mapcar (lambda (elt) (list (car elt))) (cons sframes-default-type-spec sframes-type-alist)) nil t nil nil "FIXME")))) ;; (defun sframes-orphan-buffer-p (buffer) ;; "Return nil if BUFFER is part of any existing Sframe." ;; (some (lambda (func) ;; (funcall func buffer)) ;; sframes-predicate-functions)) ;; Why isn't `some' from the cl-package implemented as a macro? (defun sframes-orphan-buffer-p (buffer) "Return nil if, and only if, BUFFER is part of any existing Sframe." (catch 'exit (dolist (func sframes-predicate-functions t) (when (funcall func buffer) (throw 'exit nil))))) (defun sframes-make-predicate-function (arg) "Return a predicate-function. If ARG is a function, return ARG unmodified, else create a function from ARG. The returned function takes a buffer as an argument. If ARG is a string, return a function that tests if ARG matches the name of the buffer. If it is a list, return a function that tests if the value of `major-mode' in the buffer is eq to one of the members of the list." (typecase arg (function arg) ;; We use pseudo-closures: (list (byte-compile `(lambda (buffer) (with-current-buffer buffer (if (memq major-mode ',arg) buffer nil))))) (string `(lambda (buffer) ;; Would byte-compilation on the fly gain something ;; here? (if (string-match ,arg (buffer-name buffer)) buffer nil))))) (provide 'sframes) ;;; sframes.el ends here --=-=-= Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit -- 29 Thermidor an 210 de la Révolution Liberté, Egalité, Fraternité! --=-=-=--