From mboxrd@z Thu Jan 1 00:00:00 1970 From: Dan Davison Subject: Re: Using Org for browsing and managing buffers Date: Thu, 15 Apr 2010 17:21:41 -0400 Message-ID: <877ho8tnru.fsf@stats.ox.ac.uk> References: <87aatda0gv.fsf@stats.ox.ac.uk> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Return-path: Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1O2WVm-0005gp-Bz for emacs-orgmode@gnu.org; Thu, 15 Apr 2010 17:22:10 -0400 Received: from [140.186.70.92] (port=56506 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1O2WVj-0005gh-O3 for emacs-orgmode@gnu.org; Thu, 15 Apr 2010 17:22:09 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1O2WVW-0006do-Uz for emacs-orgmode@gnu.org; Thu, 15 Apr 2010 17:22:07 -0400 Received: from markov.stats.ox.ac.uk ([163.1.210.1]:33635) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1O2WVW-0006dY-Gh for emacs-orgmode@gnu.org; Thu, 15 Apr 2010 17:21:54 -0400 In-Reply-To: (Livin Stephen Sharma's message of "Thu, 15 Apr 2010 15:41:43 +0530") 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: Livin Stephen Sharma Cc: emacs-org-mode-help gnu Livin Stephen Sharma writes: > Am I the only one encountering: > > org-buffers-list: Invalid function: org-buffers-state-get I haven't seen that error, but I may have been doing something incorrect (with macros). I've got rid of them now; let me know if you still get the error. Code file: http://github.com/dandavison/org-buffers/raw/master/org-buffers.el Git repo: http://github.com/dandavison/org-buffers Thanks, Dan > > > > 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/dandavison/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 | > |-----+--------------------------------------------------------| > | 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 | > +--------------------------------------------------------------+ > > 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 > ;; 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= (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/emacs-orgmode > > > _______________________________________________ > 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