From mboxrd@z Thu Jan 1 00:00:00 1970 From: Livin Stephen Sharma Subject: Re: Using Org for browsing and managing buffers Date: Thu, 15 Apr 2010 15:41:43 +0530 Message-ID: References: <87aatda0gv.fsf@stats.ox.ac.uk> Mime-Version: 1.0 (Apple Message framework v1078) Content-Type: multipart/mixed; boundary="===============1622594151==" Return-path: Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1O2M3K-0001Mg-To for emacs-orgmode@gnu.org; Thu, 15 Apr 2010 06:12:07 -0400 Received: from [140.186.70.92] (port=56421 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1O2M3G-0001JR-Vw for emacs-orgmode@gnu.org; Thu, 15 Apr 2010 06:12:06 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1O2M3B-0001I2-RR for emacs-orgmode@gnu.org; Thu, 15 Apr 2010 06:12:02 -0400 Received: from mail-ew0-f214.google.com ([209.85.219.214]:36338) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1O2M3B-0001Hf-Ae for emacs-orgmode@gnu.org; Thu, 15 Apr 2010 06:11:57 -0400 Received: by ewy6 with SMTP id 6so434594ewy.32 for ; Thu, 15 Apr 2010 03:11:56 -0700 (PDT) In-Reply-To: <87aatda0gv.fsf@stats.ox.ac.uk> List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: emacs-org-mode-help gnu Cc: Dan Davison --===============1622594151== Content-Type: multipart/alternative; boundary=Apple-Mail-6-961327285 --Apple-Mail-6-961327285 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Am I the only one encountering: > org-buffers-list: Invalid function: org-buffers-state-get I do see many people are using this successfully :) Livin Stephen On Apr 09, 2010, at 06:47:20 , Dan Davison wrote: > I've been working on an Org tool to browse Emacs buffers. Emacs has = the > function list-buffers (C-x C-b), where you can view a list of buffers, > delete buffers, etc. This is intended to be a replacement for > list-buffers, implemented in Org-mode. >=20 > The code is attached, and there's a git repo at > http://github.com/dandavison/org-buffers >=20 > After putting the code in your load-path and doing=20 > (require 'org-buffers), use the function `org-buffers-list' to create > the listing buffer. This is a read-only Org-mode buffer populated with > links to open buffers. Information is stored for each buffer using > properties. By default, the buffers are grouped by major mode. Here's = a > screenshot. >=20 > http://www.princeton.edu/~ddavison/org-buffers/by-major-mode.png >=20 > The buffer has some special key-bindings: >=20 > ? Show all keybindings > g Update buffer (prefix arg does hard reset) > b Select a different property to group by > RET follow link to buffer on this line > d Mark buffer for deletion > u Remove mark > x Delete marked buffers > o Like RET (see variable org-buffers-follow-link-method) > . Like RET but switch to buffer in same window > h toggle between headings and plain entries for buffers > p toggle in-buffer properties on/off > c Switch to column-view >=20 > If there's an active region, d and u operate on all buffers in the > region. >=20 > Some variables that can be configured: > - org-buffers-buffer-properties > - org-buffers-excluded-modes > - org-buffers-excluded-buffers > - org-buffers-follow-link-method > - org-buffers-mode-hook > - org-buffers-buffer-name >=20 > Some possible extensions: > - Browse recent files using recentf > - Allow several buffers to be marked for side-by-side display > - Maintain folding configuration across buffer updates > - Make faster >=20 > As always, any feedback, suggestions and patches will be very welcome! >=20 > Dan >=20 > p.s. The column-view mode works for following links, but does need > further attention. >=20 > ;;; org-buffers.el --- An Org-mode tool for buffer management >=20 > ;; Copyright (C) 2010 Dan Davison >=20 > ;; Author: Dan Davison > ;; Keywords: outlines, hypermedia, calendar, wp > ;; Homepage: http://orgmode.org >=20 > ;;; License: >=20 > ;; 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 3, 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 > ;; 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., 51 Franklin Street, Fifth Floor, > ;; Boston, MA 02110-1301, USA. >=20 > ;;; Commentary: >=20 > ;;; Code: >=20 > (require 'org) > (require 'cl) >=20 > ;;; Variables > (defvar org-buffers-buffer-name > "*Buffers*" > "Name of buffer in which buffer list is displayed") >=20 > (defvar org-buffers-state > '((:by . "major-mode") (:atom . heading) (:properties . nil)) > "Association list specifiying the current state of org-buffers.") >=20 > (defvar org-buffers-follow-link-method 'org-open-at-point > "Method used to follow link with RET. Must be one of >=20 > 'org-open-at-point :: use `org-open-at-point' to follow link. > 'current-window :: use switch-to-buffer > 'other-window :: use switch-to-buffer-other-window >=20 > Setting this variable to 'current-window makes the behaviour more > consistent with that of `Buffer-menu-mode' and `dired-mode'") >=20 > (defvar org-buffers-buffer-properties > '(("buffer-name" . (buffer-name)) > ("major-mode" . (let ((mode (symbol-name major-mode))) > (if (string-match "-mode$" mode) > (replace-match "" nil t mode) mode))) > ("buffer-file-name" . (buffer-file-name)) > ("default-directory" . default-directory) > ("buffer-modified-p" . (format "%s" (buffer-modified-p)))) > "Association list specifying properties to be stored for each > buffer. The car of each element is the name of the property, and > the cdr is an expression which, when evaluated in the buffer, > yields the property value.") >=20 > (defcustom org-buffers-excluded-buffers > `("*Completions*" ,org-buffers-buffer-name) > "List of names of buffers that should not be listed by > org-buffers-list." > :group 'org-buffers) >=20 > (defcustom org-buffers-excluded-modes nil > "List of names of major-modes (strings) that should not be listed > by org-buffers-list." > :group 'org-buffers) >=20 > ;;; Mode > (defvar org-buffers-mode-map (make-sparse-keymap)) >=20 > (defvar org-buffers-mode-hook nil > "Hook for functions to be called after buffer listing is > created. Note that the buffer is read-only, so if the hook > function is to modify the buffer it should use a let binding to > temporarily bind buffer-read-only to nil.") >=20 > (define-minor-mode org-buffers-mode > "An Org-mode tool for buffer management. > \\{org-buffers-mode-map}" > nil " buffers" nil > (org-set-local 'org-tag-alist '(("delete" . ?d))) > (org-set-local'org-tags-column -50) > (org-set-local 'org-columns-default-format "%25buffer-name(Buffer) = %25major-mode(Mode) %25default-directory(Dir) = %5buffer-modified-p(Modified)") > (add-hook 'kill-buffer-hook 'org-buffers-reset-state nil 'local)) >=20 > (defun org-buffers-help () > (interactive) > (describe-function 'org-buffers-mode)) >=20 > ;;; Keys > (define-key org-buffers-mode-map [(return)] 'org-buffers-follow-link) > (define-key org-buffers-mode-map "b" 'org-buffers-list:by) > (define-key org-buffers-mode-map "c" 'org-buffers-columns-view) > (define-key org-buffers-mode-map "d" 'org-buffers-tag-for-deletion) > (define-key org-buffers-mode-map "g" 'org-buffers-list:refresh) > (define-key org-buffers-mode-map "." 'org-buffers-switch-to-buffer) > (define-key org-buffers-mode-map "h" 'org-buffers-toggle-headings) > (define-key org-buffers-mode-map "o" = 'org-buffers-switch-to-buffer-other-window) > (define-key org-buffers-mode-map "p" 'org-buffers-toggle-properties) > (define-key org-buffers-mode-map "u" 'org-buffers-remove-tags) > (define-key org-buffers-mode-map "x" = 'org-buffers-execute-pending-operations) > (define-key org-buffers-mode-map "?" 'org-buffers-help) > ;;; Listing and view cycling >=20 > (defun org-buffers-list (&optional refresh frame) > "Create an Org-mode listing of Emacs buffers. > By default, buffers are grouped by major mode. Optional > argument FRAME specifies the frame whose buffers should be > listed." > (interactive) > (pop-to-buffer > (or > (and (not refresh) (get-buffer org-buffers-buffer-name)) > (let ((org-buffers-p (equal (buffer-name) org-buffers-buffer-name)) > (by (or (org-buffers-state-get :by) "major-mode")) > (atom (org-buffers-state-get :atom)) target) > (when org-buffers-p > (if (and (org-before-first-heading-p) (not (org-on-heading-p))) > (outline-next-heading)) > (setq target > (condition-case nil (org-make-org-heading-search-string) = (error nil)))) > (with-current-buffer (get-buffer-create org-buffers-buffer-name) > (setq buffer-read-only nil) > (erase-buffer) > (org-mode) > (dolist > (buffer > (sort (remove-if 'org-buffers-exclude-p > (mapcar 'buffer-name (buffer-list frame))) = 'string<)) > (org-insert-heading t) > (insert > (org-make-link-string (concat "buffer:" buffer) buffer) "\n") > (dolist (pair (org-buffers-get-buffer-props buffer)) > (org-set-property (car pair) (cdr pair)))) > (org-buffers-set-state '((:atom . heading))) > (goto-char (point-min)) > (unless (equal by "NONE") (org-buffers-group-by by)) > (if target (condition-case nil (org-link-search target) (error = nil))) > (beginning-of-line) > (if (equal by "NONE") > (org-overview) > (case atom > ('heading (progn (org-overview) (org-content))) > ('line (progn (show-all) (org-buffers-toggle-headings))))) > (save-excursion > (mark-whole-buffer) > (indent-region (point-min) (point-max))) > (org-buffers-mode) > (setq buffer-read-only t) > (current-buffer)))))) >=20 > (defun org-buffers-list:refresh (&optional arg) > "Refresh org-buffers listing." > (interactive "P") > (if arg (org-buffers-reset-state)) > (org-buffers-list 'refresh)) >=20 > (defun org-buffers-list:by (&optional prop) > "Group buffers according to value of property PROP." > (interactive) > (let ((buffer-read-only nil) > (headings-p (org-buffers-state-eq :atom 'heading))) > (unless (org-buffers-state-get :properties) > (org-buffers-toggle-properties)) > (org-buffers-set-state > `((:by . > ,(or prop > (org-completing-read > "Property to group by: " > (cons "NONE" (mapcar 'car = org-buffers-buffer-properties))))))) > (org-buffers-list 'refresh) > (unless headings-p (org-buffers-toggle-headings)))) >=20 > (defun org-buffers-toggle-properties () > "Toggle entry properties in org-buffers listing buffer. > Removing properties may provide a less cluttered appearance for > browsing. However, in-buffer properties will be restored during > certain operations, such as `org-buffers-list:by'." > (interactive) > (if (org-buffers-state-get :properties) > (progn (org-buffers-delete-properties) > (show-all) > (org-buffers-set-state '((:properties . nil)))) > (org-buffers-set-state > '((:atom . heading) (:properties . t))) > (org-buffers-list 'refresh))) >=20 > (defun org-buffers-toggle-headings () > "Toggle viewing of buffers as org headings. > Headings will be automatically restored during certain > operations, such as setting deletion tags." > (interactive) > (let ((buffer-read-only nil) > (headings-p (org-buffers-state-eq :atom 'heading)) > (flat-p (org-buffers-state-eq :by "NONE"))) > (if (and headings-p (org-buffers-state-get :properties)) > (org-buffers-toggle-properties)) > (save-excursion > (goto-char (point-min)) > (if (and (or headings-p (not flat-p)) > (not (outline-on-heading-p))) > (outline-next-heading)) > (if flat-p > (progn=20 > (push-mark (point) 'nomsg 'activate) > (end-of-buffer) > (org-ctrl-c-star) > (pop-mark)) > (while (not (eobp)) > (push-mark > (save-excursion (forward-line 1) (point)) 'nomsg 'activate) > (org-forward-same-level 1)=09 > (org-ctrl-c-star) > (pop-mark))) > (mark-whole-buffer) > (indent-region (point-min) (point-max))) > (org-buffers-set-state > `((:atom . ,(if headings-p 'line 'heading)))))) >=20 > (defun org-buffers-delete-properties () > (let ((buffer-read-only nil)) > (save-excursion > (goto-char (point-min)) > (org-buffers-delete-regions > (nreverse > (org-buffers-map-entries 'org-buffers-get-property-block)))))) >=20 > (defun org-buffers-get-property-block () > "Return the (beg . end) range of the property drawer. > Unlike the org version the limits include the keywords delimiting > the drawer." > (let ((beg (point)) > (end (progn (outline-next-heading) (point)))) > (goto-char beg) > (if (re-search-forward org-property-drawer-re end t) > (cons (match-beginning 1) (match-end 0))))) >=20 > (defun org-buffers-group-by (property) > "Group top level headings according to the value of PROPERTY." > (let ((atom (org-buffers-state-get :atom))) > (save-excursion > (goto-char (point-min)) > (mapc (lambda (subtree) ;; Create subtree for each value of = `property' > (org-insert-heading t) > (if (> (org-buffers-outline-level) 1) > (org-promote)) > (insert (car subtree) "\n") > (org-insert-subheading t) > (mapc 'org-buffers-insert-parsed-entry (cdr subtree))) > (prog1 > (mapcar (lambda (val) ;; Form list of parsed entries for = each unique value of `property' > (cons val (org-buffers-parse-selected-entries = property val))) > (sort > (delete-dups (org-buffers-map-entries (lambda = () (org-entry-get nil property nil)))) > 'string<)) > (erase-buffer)))))) >=20 > (defun org-buffers-exclude-p (buffer) > "Return non-nil if BUFFER should not be listed." > (or (member (with-current-buffer buffer major-mode) > org-buffers-excluded-modes) > (member buffer org-buffers-excluded-buffers) > (string=3D (substring buffer 0 1) " "))) >=20 > (defun org-buffers-reset-state () > (org-buffers-set-state > '((:by . "major-mode") (:atom . heading) (:properties . nil)))) >=20 > (defun org-buffers-columns-view () > "View buffers in Org-mode columns view. > This is currently experimental. RET can be used to follow links > in the first column, but certain other org-buffers keys conflict > with column-view or otherwise do not work correctly." > (interactive) > (let ((by (org-buffers-state-get :by)) > (buffer-read-only nil)) > (unless (equal by "NONE") (org-buffers-list:by "NONE")) > (unless (org-buffers-state-get :properties) > (org-buffers-toggle-properties)) > (unless (equal by "NONE") > (goto-char (point-min)) > (org-sort-entries-or-items nil ?r nil nil by) > (org-overview)) > (mark-whole-buffer) > (org-columns))) >=20 > ;;; Parsing and inserting entries > (defun org-buffers-parse-selected-entries (prop val) > "Parse all entries with property PROP value VAL." > (delq nil > (org-buffers-map-entries > (lambda () (when (equal (org-entry-get nil prop) val) > (cons (org-get-heading) (org-get-entry))))))) >=20 > (defun org-buffers-insert-parsed-entry (entry) > "Insert a parsed entry" > (unless (org-at-heading-p) (org-insert-heading)) > (insert (car entry) "\n") > (if (org-buffers-state-get :properties) > (insert (cdr entry)))) >=20 > (defun org-buffers-get-buffer-props (buffer) > "Create alist of properties of BUFFER, as strings." > (with-current-buffer buffer > (mapcar=20 > (lambda (pair) (cons (car pair) (eval (cdr pair)))) > org-buffers-buffer-properties))) >=20 > ;;; Follow-link behaviour >=20 > (defun org-buffers-follow-link () > "Follow link to buffer on this line. > The buffer-switching behaviour of this function is determined by > the variable `org-buffers-follow-link-method'. See also > `org-buffers-switch-to-buffer' and > `org-buffers-switch-to-buffer-other-window', whose behaviour is > hard-wired." > (interactive) > (org-buffers-switch-to-buffer-generic = org-buffers-follow-link-method)) >=20 > (defun org-buffers-switch-to-buffer () > "Switch to this entry's buffer in current window." > (interactive) > (org-buffers-switch-to-buffer-generic 'current-window)) >=20 > (defun org-buffers-switch-to-buffer-other-window () > "Switch to this entry's buffer in other window." > (interactive) > (org-buffers-switch-to-buffer-generic 'other-window)) >=20 > (defun org-buffers-switch-to-buffer-generic (method) > (save-excursion > (let ((atom (org-buffers-state-get :atom)) buffer) > (cond > ((eq atom 'heading) (org-back-to-heading)) > (t (beginning-of-line))) > (setq buffer (org-buffers-get-buffer-name)) > (if (get-buffer buffer) > (case method > ('org-open-at-point (org-open-at-point)) > ('current-window (switch-to-buffer buffer)) > ('other-window (switch-to-buffer-other-window buffer))) > (error "No such buffer: %s" buffer))))) >=20 > (defun org-buffers-get-buffer-name () > "Get buffer-name for current entry." > (let ((headings-p (org-buffers-state-eq :atom 'heading))) > (or (and headings-p (org-entry-get nil "buffer-name")) > (and (save-excursion > (if headings-p (org-back-to-heading)) > (re-search-forward "\\[\\[buffer:\\([^\]]*\\)" = (point-at-eol) t)) > (org-link-unescape (match-string 1)))))) >=20 > ;;; Setting tags and executing operations >=20 > (defun org-buffers-tag-for-deletion () > "Mark buffer for deletion. > If a region is selected, all buffers in the region are marked for > deletion. Buffers marked for deletion can be deleted using > `org-buffers-execute-pending-operations'." > (interactive) > (org-buffers-set-tags '("delete"))) >=20 > (defun org-buffers-remove-tags () > "Remove deletion marks from buffers. > If a region is selected, marks are removed from all buffers in > the region." > (interactive) > (org-buffers-set-tags nil)) >=20 > (defun org-buffers-set-tags (data) > "Set tags to DATA at all non top-level headings in region. > DATA should be a list of strings. If DATA is nil, remove all tags > at such headings." > (let* ((buffer-read-only nil) > (region-p (org-region-active-p)) > (beg (if region-p (region-beginning) (point))) > (end (if region-p (region-end) (point))) > (headings-p (org-buffers-state-eq :atom 'heading))beg-line = end-line) > (save-excursion > (setq beg-line (progn (goto-char beg) (org-current-line)) > end-line (progn (goto-char end) (org-current-line))) > (if headings-p > (setq > end (if (and region-p (not (eq end-line beg-line)) (not = (eobp))) > (progn (goto-char end) (org-back-to-heading) (point)) > (progn (outline-end-of-heading) (point))) > beg (progn (goto-char beg) (point-at-bol))) > (org-buffers-toggle-headings) ;; doesn't alter line numbers > (setq beg (progn (org-goto-line beg-line) (point-at-bol)) > end (if (eq end-line beg-line) (point-at-eol) > (progn (org-goto-line end-line) (point-at-bol))))) > (narrow-to-region beg end) > (goto-char (point-min)) > (org-buffers-map-entries > (lambda () > (when (or (org-buffers-state-eq :by "NONE") > (> (org-outline-level) 1)) > (org-set-tags-to > (if data (delete-duplicates (append data (org-get-tags)) = :test 'string-equal)))))) > (widen) > (org-content)) > (unless region-p > (outline-next-heading) > (unless (or (> (org-outline-level) 1) (org-buffers-state-eq :by = "NONE")) > (outline-next-heading))) > (unless headings-p (org-buffers-toggle-headings)))) >=20 > (defun org-buffers-execute-pending-operations () > "Execute all pending operations. > Currently the only type of operation supported is > deletion. Buffers are tagged for deletion using > `org-buffers-tag-for-deletion'. Remove such tags from buffers > using `org-buffers-remove-tags'." > (interactive) > (let ((buffer-read-only nil) > (headings-p (org-buffers-state-eq :atom 'heading)) buffer) > (unless headings-p (org-buffers-toggle-headings)) > (org-buffers-delete-regions > (nreverse > (org-buffers-map-entries > (lambda () > (if (setq buffer (org-buffers-get-buffer-name)) > (if (not (kill-buffer buffer)) > (error "Failed to kill buffer %s" buffer) > (if (and (org-first-sibling-p) > (not (save-excursion (org-goto-sibling)))) > (org-up-heading-safe)) ;; Only child so delete parent = also > (cons (point) (1+ (org-end-of-subtree)))))) > "+delete"))) > (unless headings-p (org-buffers-toggle-headings)))) >=20 > ;;; Utilities >=20 > (defun org-buffers-map-entries (func &optional match) > (org-scan-tags > func (if match (cdr (org-make-tags-matcher match)) t))) >=20 > (defun org-buffers-set-state (state) > "Add STATE to global state list. > New settings have precedence over existing ones." > (mapc > (lambda (pair) (unless (assoc (car pair) state) > (add-to-list 'state pair))) > org-buffers-state) > (setq org-buffers-state state)) >=20 > (defmacro org-buffers-delete-regions (regions) > "Delete regions in list. > REGIONS is a list of (beg . end) cons cells specifying buffer > regions." > `(mapc (lambda (pair) (if pair (delete-region (car pair) (cdr = pair)))) > ,regions)) >=20 > (defmacro org-buffers-state-get (key) > `(cdr (assoc ,key org-buffers-state))) >=20 > (defmacro org-buffers-state-eq (key val) > `(equal (org-buffers-state-get ,key) ,val)) >=20 > (defmacro org-buffers-outline-level () > '(save-excursion (beginning-of-line) (org-outline-level))) >=20 > ;;; Links to buffers >=20 > (org-add-link-type "buffer" 'display-buffer) > (add-hook 'org-store-link-functions 'org-buffers-store-link) >=20 > (defun org-buffers-store-link (&optional force) > "Store a link to an Emacs buffer. > Returns nil by default, to avoid hijacking other link types." > (if force > (let* ((target (buffer-name)) > (desc target) link) > (org-store-link-props :type "buffer") > (setq link (org-make-link "buffer:" target)) > (org-add-link-props :link link :description desc) > link))) >=20 > (provide 'org-buffers) > ;;; org-buffers.el ends here > _______________________________________________ > Emacs-orgmode mailing list > Please use `Reply All' to send replies to the list. > Emacs-orgmode@gnu.org > http://lists.gnu.org/mailman/listinfo/emacs-orgmode --Apple-Mail-6-961327285 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=us-ascii Am I = the only one encountering:
org-buffers-list: Invalid = function: = org-buffers-state-get


I = do see many people are using this = successfully
:)

Livin = Stephen



On Apr 09, 2010, at 06:47:20 , Dan Davison wrote:

I've been = working on an Org tool to browse Emacs buffers. Emacs has = the
function list-buffers (C-x C-b), where you can view a list of = buffers,
delete buffers, etc. This is intended to be a replacement = for
list-buffers, implemented in Org-mode.

The code is = attached, and there's a git repo at
http://github.com/dandav= ison/org-buffers

After putting the code in your load-path and = doing
(require 'org-buffers), use the function `org-buffers-list' to = create
the listing buffer. This is a read-only Org-mode buffer = populated with
links to open buffers. Information is stored for each = buffer using
properties. By default, the buffers are grouped by major = mode. Here's = a
screenshot.

http://www.princeton.edu/~ddavison/org-buffers/by-= major-mode.png

The buffer has some special = key-bindings:

?Show all keybindings
gUpdate buffer (prefix arg does hard reset)
bSelect a different property to group by
RETfollow link to buffer on this line
dMark buffer for deletion
uRemove mark
xDelete marked buffers
oLike RET (see variable = org-buffers-follow-link-method)
.Like RET but switch to buffer in same window
htoggle between headings and plain entries for = buffers
ptoggle in-buffer properties on/off
cSwitch to column-view

If there's an active region, d and u operate on all buffers in = the
region.

Some variables that can be configured:
- = org-buffers-buffer-properties
- org-buffers-excluded-modes
- = org-buffers-excluded-buffers
- org-buffers-follow-link-method
- = org-buffers-mode-hook
- org-buffers-buffer-name

Some possible = extensions:
- Browse recent files using recentf
- Allow several = buffers to be marked for side-by-side display
- Maintain folding = configuration across buffer updates
- Make faster

As always, = any feedback, suggestions and patches will be very = welcome!

Dan

p.s. The column-view mode works for following = links, but does need
further attention.

;;; org-buffers.el --- = An Org-mode tool for buffer management

;; Copyright (C) 2010 =  Dan Davison

;; Author: Dan Davison <dandavison0 at gmail = dot com>
;; Keywords: outlines, hypermedia, calendar, wp
;; = Homepage: http://orgmode.org

;;; License:

;; 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 3, 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
;; 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., 51 = Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, = USA.

;;; Commentary:

;;; Code:

(require = 'org)
(require 'cl)

;;; Variables
(defvar = org-buffers-buffer-name
 "*Buffers*"
 "Name of buffer = in which buffer list is displayed")

(defvar org-buffers-state
=  '((:by . "major-mode") (:atom . heading) (:properties . nil))
=  "Association list specifiying the current state of = org-buffers.")

(defvar org-buffers-follow-link-method = 'org-open-at-point
 "Method used to follow link with RET. Must = be one of

'org-open-at-point :: use `org-open-at-point' to follow = link.
'current-window    :: use = switch-to-buffer
'other-window      :: use = switch-to-buffer-other-window

Setting this variable to = 'current-window makes the behaviour more
consistent with that of = `Buffer-menu-mode' and `dired-mode'")

(defvar = org-buffers-buffer-properties
 '(("buffer-name" . = (buffer-name))
   ("major-mode" . (let ((mode = (symbol-name major-mode)))
=      (if (string-match "-mode$" mode)
=  (replace-match "" nil t mode) mode)))
=    ("buffer-file-name" . (buffer-file-name))
=    ("default-directory" . default-directory)
=    ("buffer-modified-p" . (format "%s" = (buffer-modified-p))))
 "Association list specifying properties = to be stored for each
buffer. The car of each element is the name of = the property, and
the cdr is an expression which, when evaluated in = the buffer,
yields the property value.")

(defcustom = org-buffers-excluded-buffers
 `("*Completions*" = ,org-buffers-buffer-name)
 "List of names of buffers that = should not be listed by
 org-buffers-list."
 :group = 'org-buffers)

(defcustom org-buffers-excluded-modes nil
=  "List of names of major-modes (strings) that should not be = listed
 by org-buffers-list."
 :group = 'org-buffers)

;;; Mode
(defvar org-buffers-mode-map = (make-sparse-keymap))

(defvar org-buffers-mode-hook nil
=  "Hook for functions to be called after buffer listing is
=  created. Note that the buffer is read-only, so if the hook
=  function is to modify the buffer it should use a let binding = to
 temporarily bind buffer-read-only to = nil.")

(define-minor-mode org-buffers-mode
 "An Org-mode = tool for buffer management.
\\{org-buffers-mode-map}"
 nil " = buffers" nil
 (org-set-local 'org-tag-alist '(("delete" . = ?d)))
 (org-set-local'org-tags-column -50)
=  (org-set-local 'org-columns-default-format "%25buffer-name(Buffer) = %25major-mode(Mode) %25default-directory(Dir) = %5buffer-modified-p(Modified)")
 (add-hook 'kill-buffer-hook = 'org-buffers-reset-state nil 'local))

(defun org-buffers-help = ()
 (interactive)
 (describe-function = 'org-buffers-mode))

;;; Keys
(define-key org-buffers-mode-map = [(return)] 'org-buffers-follow-link)
(define-key org-buffers-mode-map = "b" 'org-buffers-list:by)
(define-key org-buffers-mode-map "c" = 'org-buffers-columns-view)
(define-key org-buffers-mode-map "d" = 'org-buffers-tag-for-deletion)
(define-key org-buffers-mode-map "g" = 'org-buffers-list:refresh)
(define-key org-buffers-mode-map "." = 'org-buffers-switch-to-buffer)
(define-key org-buffers-mode-map "h" = 'org-buffers-toggle-headings)
(define-key org-buffers-mode-map "o" = 'org-buffers-switch-to-buffer-other-window)
(define-key = org-buffers-mode-map "p" 'org-buffers-toggle-properties)
(define-key = org-buffers-mode-map "u" 'org-buffers-remove-tags)
(define-key = org-buffers-mode-map "x" = 'org-buffers-execute-pending-operations)
(define-key = org-buffers-mode-map "?" 'org-buffers-help)
;;; Listing and view = cycling

(defun org-buffers-list (&optional refresh frame)
=  "Create an Org-mode listing of Emacs buffers.
By default, = buffers are grouped by major mode. Optional
argument FRAME specifies = the frame whose buffers should be
listed."
=  (interactive)
 (pop-to-buffer
  (or
=    (and (not refresh) (get-buffer = org-buffers-buffer-name))
   (let ((org-buffers-p = (equal (buffer-name) org-buffers-buffer-name))
=  (by (or (org-buffers-state-get :by) "major-mode"))
=  (atom (org-buffers-state-get :atom)) target)
=      (when org-buffers-p
(if (and = (org-before-first-heading-p) (not (org-on-heading-p)))
=    (outline-next-heading))
(setq = target
=      (condition-case nil = (org-make-org-heading-search-string) (error nil))))
=      (with-current-buffer (get-buffer-create = org-buffers-buffer-name)
(setq buffer-read-only = nil)
= (erase-buffer)
(org-mode)
= (dolist
=    (buffer
    (sort = (remove-if 'org-buffers-exclude-p
=      (mapcar 'buffer-name (buffer-list frame))) = 'string<))
=  (org-insert-heading t)
=  (insert
=   (org-make-link-string (concat "buffer:" = buffer) buffer) "\n")
 (dolist (pair = (org-buffers-get-buffer-props buffer))
=    (org-set-property (car pair) (cdr pair))))
= (org-buffers-set-state '((:atom . heading)))
= (goto-char (point-min))
(unless (equal by "NONE") = (org-buffers-group-by by))
(if target (condition-case nil = (org-link-search target) (error nil)))
(beginning-of-line)
(if = (equal by "NONE")
=    (org-overview)
 (case atom
=    ('heading (progn (org-overview) = (org-content)))
   ('line (progn = (show-all) (org-buffers-toggle-headings)))))
= (save-excursion
=  (mark-whole-buffer)
 (indent-region (point-min) = (point-max)))
= (org-buffers-mode)
(setq buffer-read-only = t)
= (current-buffer))))))

(defun org-buffers-list:refresh = (&optional arg)
 "Refresh org-buffers listing."
=  (interactive "P")
 (if arg (org-buffers-reset-state))
=  (org-buffers-list 'refresh))

(defun org-buffers-list:by = (&optional prop)
 "Group buffers according to value of = property PROP."
 (interactive)
 (let = ((buffer-read-only nil)
(headings-p (org-buffers-state-eq = :atom 'heading)))
   (unless (org-buffers-state-get = :properties)
=      (org-buffers-toggle-properties))
=    (org-buffers-set-state
=     `((:by .
   ,(or = prop
= = (org-completing-read
 "Property to group by: = "
= =  (cons "NONE" (mapcar 'car = org-buffers-buffer-properties)))))))
=    (org-buffers-list 'refresh)
=    (unless headings-p = (org-buffers-toggle-headings))))

(defun = org-buffers-toggle-properties ()
 "Toggle entry properties in = org-buffers listing buffer.
Removing properties may provide a less = cluttered appearance for
browsing. However, in-buffer properties will = be restored during
certain operations, such as = `org-buffers-list:by'."
 (interactive)
 (if = (org-buffers-state-get :properties)
=      (progn = (org-buffers-delete-properties)
=     (show-all)
=     (org-buffers-set-state '((:properties . = nil))))
   (org-buffers-set-state
=     '((:atom . heading) (:properties . t)))
=    (org-buffers-list 'refresh)))

(defun = org-buffers-toggle-headings ()
 "Toggle viewing of buffers as = org headings.
Headings will be automatically restored during = certain
operations, such as setting deletion tags."
=  (interactive)
 (let ((buffer-read-only nil)
= (headings-p (org-buffers-state-eq :atom 'heading))
(flat-p = (org-buffers-state-eq :by "NONE")))
   (if (and = headings-p (org-buffers-state-get :properties))
= (org-buffers-toggle-properties))
=    (save-excursion
=      (goto-char (point-min))
=      (if (and (or headings-p (not = flat-p))
=       (not = (outline-on-heading-p)))
=  (outline-next-heading))
     (if = flat-p
=  (progn
   (push-mark = (point) 'nomsg 'activate)
=    (end-of-buffer)
=    (org-ctrl-c-star)
=    (pop-mark))
(while (not (eobp))
=  (push-mark
  (save-excursion = (forward-line 1) (point)) 'nomsg 'activate)
=  (org-forward-same-level 1)
 (org-ctrl-c-star)
=  (pop-mark)))
=      (mark-whole-buffer)
=      (indent-region (point-min) = (point-max)))
   (org-buffers-set-state
=     `((:atom . ,(if headings-p 'line = 'heading))))))

(defun org-buffers-delete-properties ()
=  (let ((buffer-read-only nil))
=    (save-excursion
=      (goto-char (point-min))
=      (org-buffers-delete-regions
=       (nreverse
= (org-buffers-map-entries = 'org-buffers-get-property-block))))))

(defun = org-buffers-get-property-block ()
 "Return the (beg . end) = range of the property drawer.
Unlike the org version the limits = include the keywords delimiting
the drawer."
 (let ((beg = (point))
= (end (progn (outline-next-heading) (point))))
=    (goto-char beg)
   (if = (re-search-forward org-property-drawer-re end t)
(cons = (match-beginning 1) (match-end 0)))))

(defun org-buffers-group-by = (property)
 "Group top level headings according to the value of = PROPERTY."
 (let ((atom (org-buffers-state-get :atom)))
=    (save-excursion
=      (goto-char (point-min))
=      (mapc (lambda (subtree) ;; Create subtree = for each value of `property'
=      (org-insert-heading t)
=      (if (> (org-buffers-outline-level) = 1)
= =  (org-promote))
=      (insert (car subtree) "\n")
=      (org-insert-subheading t)
=      (mapc 'org-buffers-insert-parsed-entry = (cdr subtree)))
=    (prog1
(mapcar (lambda (val) ;; Form = list of parsed entries for each unique value of `property'
=  (cons val (org-buffers-parse-selected-entries property = val)))
= = = (sort
= = = (delete-dups (org-buffers-map-entries (lambda () (org-entry-get = nil property nil))))
'string<))
=      (erase-buffer))))))

(defun = org-buffers-exclude-p (buffer)
 "Return non-nil if BUFFER = should not be listed."
 (or (member (with-current-buffer buffer = major-mode)
=      org-buffers-excluded-modes)
=      (member buffer = org-buffers-excluded-buffers)
     (string=3D= (substring buffer 0 1) " ")))

(defun org-buffers-reset-state = ()
 (org-buffers-set-state
  '((:by . = "major-mode") (:atom . heading) (:properties . nil))))

(defun = org-buffers-columns-view ()
 "View buffers in Org-mode columns = view.
This is currently experimental. RET can be used to follow = links
in the first column, but certain other org-buffers keys = conflict
with column-view or otherwise do not work correctly."
=  (interactive)
 (let ((by (org-buffers-state-get = :by))
= (buffer-read-only nil))
   (unless (equal by = "NONE") (org-buffers-list:by "NONE"))
   (unless = (org-buffers-state-get :properties)
=      (org-buffers-toggle-properties))
=    (unless (equal by "NONE")
=      (goto-char (point-min))
=      (org-sort-entries-or-items nil ?r nil nil = by)
     (org-overview))
=    (mark-whole-buffer)
=    (org-columns)))

;;; Parsing and inserting = entries
(defun org-buffers-parse-selected-entries (prop val)
=  "Parse all entries with property PROP value VAL."
 (delq = nil
= (org-buffers-map-entries
(lambda () (when (equal = (org-entry-get nil prop) val)
=      (cons (org-get-heading) = (org-get-entry)))))))

(defun org-buffers-insert-parsed-entry = (entry)
 "Insert a parsed entry"
 (unless = (org-at-heading-p) (org-insert-heading))
 (insert (car entry) = "\n")
 (if (org-buffers-state-get :properties)
=      (insert (cdr entry))))

(defun = org-buffers-get-buffer-props (buffer)
 "Create alist of = properties of BUFFER, as strings."
 (with-current-buffer = buffer
   (mapcar
=     (lambda (pair) (cons (car pair) (eval (cdr = pair))))
=     org-buffers-buffer-properties)))

;;; = Follow-link behaviour

(defun org-buffers-follow-link ()
=  "Follow link to buffer on this line.
The buffer-switching = behaviour of this function is determined by
the variable = `org-buffers-follow-link-method'. See = also
`org-buffers-switch-to-buffer' = and
`org-buffers-switch-to-buffer-other-window', whose behaviour = is
hard-wired."
 (interactive)
=  (org-buffers-switch-to-buffer-generic = org-buffers-follow-link-method))

(defun = org-buffers-switch-to-buffer ()
"Switch to this entry's buffer in = current window."
 (interactive)
=  (org-buffers-switch-to-buffer-generic = 'current-window))

(defun = org-buffers-switch-to-buffer-other-window ()
 "Switch to this = entry's buffer in other window."
 (interactive)
=  (org-buffers-switch-to-buffer-generic = 'other-window))

(defun org-buffers-switch-to-buffer-generic = (method)
 (save-excursion
   (let ((atom = (org-buffers-state-get :atom)) buffer)
=      (cond
=       ((eq atom 'heading) = (org-back-to-heading))
      (t = (beginning-of-line)))
     (setq buffer = (org-buffers-get-buffer-name))
     (if = (get-buffer buffer)
 (case method
=    ('org-open-at-point (org-open-at-point))
=    ('current-window (switch-to-buffer buffer))
=    ('other-window (switch-to-buffer-other-window = buffer)))
= (error "No such buffer: %s" buffer)))))

(defun = org-buffers-get-buffer-name ()
 "Get buffer-name for current = entry."
 (let ((headings-p (org-buffers-state-eq :atom = 'heading)))
   (or (and headings-p (org-entry-get nil = "buffer-name"))
(and (save-excursion
=       (if headings-p = (org-back-to-heading))
=       (re-search-forward = "\\[\\[buffer:\\([^\]]*\\)" (point-at-eol) t))
=     (org-link-unescape (match-string = 1))))))

;;; Setting tags and executing operations

(defun = org-buffers-tag-for-deletion ()
 "Mark buffer for = deletion.
If a region is selected, all buffers in the region are = marked for
deletion. Buffers marked for deletion can be deleted = using
`org-buffers-execute-pending-operations'."
=  (interactive)
 (org-buffers-set-tags = '("delete")))

(defun org-buffers-remove-tags ()
 "Remove = deletion marks from buffers.
If a region is selected, marks are = removed from all buffers in
the region."
 (interactive)
=  (org-buffers-set-tags nil))

(defun org-buffers-set-tags = (data)
 "Set tags to DATA at all non top-level headings in = region.
DATA should be a list of strings. If DATA is nil, remove all = tags
at such headings."
 (let* ((buffer-read-only = nil)
= (region-p (org-region-active-p))
(beg (if region-p = (region-beginning) (point)))
(end (if region-p (region-end) = (point)))
= (headings-p (org-buffers-state-eq :atom 'heading))beg-line = end-line)
   (save-excursion
=      (setq beg-line (progn (goto-char beg) = (org-current-line))
   end-line = (progn (goto-char end) (org-current-line)))
=      (if headings-p
=  (setq
=   end (if (and region-p (not (eq end-line beg-line)) = (not (eobp)))
= =   (progn (goto-char end) (org-back-to-heading) = (point))
= = (progn (outline-end-of-heading) (point)))
=   beg (progn (goto-char beg) (point-at-bol)))
= (org-buffers-toggle-headings) ;; doesn't alter line = numbers
= (setq beg (progn (org-goto-line beg-line) = (point-at-bol))
=      end (if (eq end-line beg-line) = (point-at-eol)
   (progn = (org-goto-line end-line) (point-at-bol)))))
=      (narrow-to-region beg end)
=      (goto-char (point-min))
=      (org-buffers-map-entries
=       (lambda ()
(when = (or (org-buffers-state-eq :by "NONE")
  (> = (org-outline-level) 1))
=   (org-set-tags-to
   (if data = (delete-duplicates (append data (org-get-tags)) :test = 'string-equal))))))
     (widen)
=      (org-content))
=    (unless region-p
=      (outline-next-heading)
=      (unless (or (> (org-outline-level) 1) = (org-buffers-state-eq :by "NONE"))
(outline-next-heading)))
=        (unless headings-p = (org-buffers-toggle-headings))))

(defun = org-buffers-execute-pending-operations ()
 "Execute all pending = operations.
Currently the only type of operation supported = is
deletion. Buffers are tagged for deletion = using
`org-buffers-tag-for-deletion'. Remove such tags from = buffers
using `org-buffers-remove-tags'."
 (interactive)
=  (let ((buffer-read-only nil)
(headings-p (org-buffers-state-eq = :atom 'heading)) buffer)
   (unless headings-p = (org-buffers-toggle-headings))
=    (org-buffers-delete-regions
=     (nreverse
=      (org-buffers-map-entries
=       (lambda ()
(if = (setq buffer (org-buffers-get-buffer-name))
=     (if (not (kill-buffer buffer))
(error = "Failed to kill buffer %s" buffer)
=       (if (and = (org-first-sibling-p)
(not (save-excursion = (org-goto-sibling))))
=   (org-up-heading-safe)) ;; Only child so delete parent = also
=       (cons (point) (1+ = (org-end-of-subtree))))))
=       "+delete")))
=    (unless headings-p = (org-buffers-toggle-headings))))

;;; Utilities

(defun = org-buffers-map-entries (func &optional match)
=  (org-scan-tags
  func (if match (cdr = (org-make-tags-matcher match)) t)))

(defun org-buffers-set-state = (state)
 "Add STATE to global state list.
New settings have = precedence over existing ones."
 (mapc
  (lambda = (pair) (unless (assoc (car pair) state)
   (add-to-list = 'state pair)))
  org-buffers-state)
 (setq = org-buffers-state state))

(defmacro org-buffers-delete-regions = (regions)
 "Delete regions in list.
REGIONS is a list of = (beg . end) cons cells specifying buffer
regions."
 `(mapc = (lambda (pair) (if pair (delete-region (car pair) (cdr pair))))
= ,regions))

(defmacro org-buffers-state-get (key)
 `(cdr = (assoc ,key org-buffers-state)))

(defmacro org-buffers-state-eq = (key val)
 `(equal (org-buffers-state-get ,key) = ,val))

(defmacro org-buffers-outline-level ()
=  '(save-excursion (beginning-of-line) = (org-outline-level)))

;;; Links to = buffers

(org-add-link-type "buffer" 'display-buffer)
(add-hook = 'org-store-link-functions 'org-buffers-store-link)

(defun = org-buffers-store-link (&optional force)
 "Store a link to = an Emacs buffer.
Returns nil by default, to avoid hijacking other = link types."
 (if force
     (let* = ((target (buffer-name))
    (desc = target) link)
= (org-store-link-props :type "buffer")
(setq = link (org-make-link "buffer:" target))
(org-add-link-props :link link = :description desc)
link)))

(provide = 'org-buffers)
;;; org-buffers.el ends = here
_______________________________________________
Emacs-orgmode = mailing list
Please use `Reply All' to send replies to the = list.
Emacs-orgmode@gnu.org
http://lists.gnu.org/mailman/listinfo/em= acs-orgmode

= --Apple-Mail-6-961327285-- --===============1622594151== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Emacs-orgmode mailing list Please use `Reply All' to send replies to the list. Emacs-orgmode@gnu.org http://lists.gnu.org/mailman/listinfo/emacs-orgmode --===============1622594151==--