gnu-emacs-sources
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

usage-memo.el --- integration of Emacs help system and memo


From: rubikitch
Subject: usage-memo.el --- integration of Emacs help system and memo
Date: Mon, 02 Jul 2007 06:00:23 +0900 (JST)

Hi,

 This program enables you to write annotation in *Help* and
 third-party help systems. When we do programming, we often use
 Emacs help system (ie. describe-function). Do you want to take a
 note in the *Help* buffer and want Emacs to show your note later?
 In other words, integration of Emacs help and your memo!

 Annotation files are stored below ~/memo/umemo by default. Its
 subdirectories are categories. And their subdirectories are entry
 annotation files. Because annotation files are read in each case,
 you can generate annotation files automatically.


;;; usage-memo.el --- integration of Emacs help system and memo

;; $Id: usage-memo.el,v 1.3 2007/07/01 20:05:53 rubikitch Exp $

;; Copyright (C) 2007  rubikitch

;; Author: rubikitch <address@hidden>
;; Keywords: convenience, languages, lisp, help, tools, docs

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

;;; Commentary:

;; This program enables you to write annotation in *Help* and
;; third-party help systems. When we do programming, we often use
;; Emacs help system (ie. describe-function). Do you want to take a
;; note in the *Help* buffer and want Emacs to show your note later?
;; In other words, integration of Emacs help and your memo!

;; Annotation files are stored below ~/memo/umemo by default. Its
;; subdirectories are categories. And their subdirectories are entry
;; annotation files. Because annotation files are read in each case,
;; you can generate annotation files automatically.

;;; Supported help systems:

;; describe-function
;; describe-variable
;; slime-documentation (slime.el)
;; slime-describe-symbol (slime.el)
;; slime-describe-function (slime.el)
;; ri (ri-ruby.el)
;; lh-refe (langhelp / ReFe)
;;
;; usage-memo is general-purpose: it is easy to support other help systems.

;;; Installation:

;; Append to .emacs:
;;   (require 'usage-memo)
;;   (umemo-initialize)

;; If you support other help systems, add `define-usage-memo' sexps in
;; .emacs or redefine `umemo-initialize'.

;;; Usage:

;; * Switch to *Help*.
;; * Write your annotation below `===*===*===*===*===*===*===*===*===*===*===' 
line.
;; * Press C-x C-s to save annotation.
;; * Even if *Help* is killed or Emacs is restarted,
;;   your annotation is shown when you look up the same entry!!

;;; Further information:

;; [EVAL IT] (describe-function 'define-usage-memo)
;; [EVAL IT] (describe-function 'usage-memo-mode)
;; [EVAL IT] (describe-variable 'umemo-base-directory)
;; [EVAL IT] (describe-function 'umemo-pathname)
;; [EVAL IT] (describe-variable 'umemo-category)
;; [EVAL IT] (describe-variable 'umemo-entry-name)
;; [EVAL IT] (describe-variable 'umemo-current-entry-name)

;;; History:

;; $Log: usage-memo.el,v $
;; Revision 1.3  2007/07/01 20:05:53  rubikitch
;; Support slime-describe-function.
;; Update docs.
;; First public release!
;;
;; Revision 1.2  2007/07/01 18:40:47  rubikitch
;; write docs.
;;
;; Revision 1.1  2007/07/01 10:07:15  rubikitch
;; Initial revision
;;

;;; Code:

(defvar umemo-base-directory "~/memo/umemo"
  "Directory where memo files are placed.
You need not create directory when it does not exist.
usage-memo creates it automatically.")
(defvar umemo-separator "\n===*===*===*===*===*===*===*===*===*===*===\n"
  "Separator between documentation and memo.")
(defvar umemo-category nil
  "Category of usage-memo. It is defined by `define-usage-memo'.")
(defvar umemo-entry-name nil
  "Entry name that help buffer(per buffer) is showing. It is used as filename 
of annotation.")
(defvar umemo-current-entry-name nil
  "Entry name that help buffer(globally) is showing. See also 
`umemo-entry-name'.

It is needed because some help system (eg. SLIME) kills help
buffer and re-create it.  In this case, the buffer-local
`umemo-entry-name' is cleared: I have no choice but to use global
variable. But it is probaby rare case.")
(make-variable-buffer-local 'umemo-category)
(make-variable-buffer-local 'umemo-entry-name)

;;;; low-level utils
(defun umemo-pathname (category entry-name)
  "Filename of annotation. `umemo-base-directory'/CATEGORY/ENTRY-NAME."
  (format "%s/%s/%s" umemo-base-directory category entry-name))

(defun umemo-fall-back-to-global-key-binding-in-memo-area (key)
  (let* ((lb (local-key-binding key))
         (gb (global-key-binding key)))
    (call-interactively
     (cond ((umemo-point-is-in-memo-area-p (point))  gb)
           (lb                                       lb)
           (t                                        gb)))))

(defun umemo-point-is-in-memo-area-p (point)
  (save-excursion
    (goto-char point)
    (search-backward umemo-separator nil t)))

(defmacro umemo-with-append-to-buffer (buffer &rest body)
  "Execute BODY at the end of BUFFER."
  `(save-excursion
     (set-buffer buffer)
     (goto-char (point-max))
     ,@body))

(defmacro umemo-if-buffer-has-separator (&rest body)
  `(save-excursion
     (goto-char (point-min))
     (when (search-forward umemo-separator nil t)
       ,@body)))

(defun umemo-has-entry-p ()
  (save-excursion
    (goto-char (point-min))
    (and (search-forward umemo-separator nil t)
         (/= (point) (point-max)))))

;;;; mid-level utils
(defun umemo-insert-memo (category entry-name buffer)
  (let ((path (umemo-pathname category entry-name)))
    (umemo-with-append-to-buffer buffer
     (setq buffer-read-only nil)
     (unless (umemo-has-entry-p)
       (insert umemo-separator)
       (and (file-exists-p path)  (insert-file-contents path))))))

(defun umemo-setup-variables (category entry-name buffer)
  (setq umemo-current-entry-name entry-name)
  (with-current-buffer buffer
    (setq umemo-category category
          umemo-entry-name entry-name)))

;;;; initializer
(defun umemo-setup (category entry-name buffer)
  (when (get-buffer buffer)
    (with-current-buffer buffer
      (umemo-setup-variables category entry-name buffer)
      (umemo-insert-memo category entry-name buffer)
      (usage-memo-mode 1))))

;;;; commands
(defun umemo-save ()
  "Save current usage memo(annotation) into file."
  (interactive)
  (umemo-if-buffer-has-separator
   (let* ((path (umemo-pathname umemo-category umemo-entry-name))
          (dir  (file-name-directory path)))
     (unless (file-directory-p dir) (make-directory dir t))
     (write-region (point) (point-max) path)
     (set-buffer-modified-p nil)
     (message "Wrote %s" path))))

(defun umemo-electric-return ()
  (interactive)
  (umemo-fall-back-to-global-key-binding-in-memo-area "\r"))

;;;; define API
(defmacro define-usage-memo (command category nth-arg buffer-fmt)
  "Add usage-memo feature to COMMAND.

Define `usage-memo' around-advice for COMMAND.
CATEGORY is usage-memo category to use, typically language name.
NTH-ARG is COMMAND's argument position (0-origin) representing entry name,
NTH-ARG is passed to `ad-get-arg' macro.
BUFFER-FMT is a `format' string, which %s is replaced with entry name,
representing document display buffer.

For example: To support `describe-function' (already supported):

  (define-usage-memo describe-function \"elisp\" 0 \"*Help*\")

Because `define-usage-memo' is a macro, COMMAND is not quoted.
The CATEGORY is \"elisp\".  The NTH-ARG is 0 because
`describe-function' takes a symbol to lookup at the first
argument. Because NTH-ARG is 0-origin, 0 means the first
argument.  BUFFER-FMT is same as *Help* buffer: it is only
coincidental.


Another example: Support RI lookup (Ruby's document-lookup tool)

`ri' function is defined in ri-ruby.el. The usage is:

  (ri ENTRY_NAME)

Then `ri' creates

  ri `ENTRY_NAME'

buffer. Instead of reusing a buffer, it creates a buffer per query.
That is why BUFFER-FMT uses `format' string!

  (define-usage-memo ri \"ruby\" 0 \"ri `%s'\")


See also `umemo-initialize' definition.

"
  `(defadvice ,command (around usage-memo activate)
     ad-do-it
     (let* ((entry-name (ad-get-arg ,nth-arg))
            (buf (with-no-warnings (format ,buffer-fmt entry-name))))
       (umemo-setup ,category entry-name buf))))

;; (umemo-initialize)
;; (ri "Array#length")
;; (lh-refe "Array#length")
;; (describe-function 'princ)
;; (slime-documentation "princ")
;; (slime-describe-symbol "princ")
;; (slime-documentation "car")

;;;; sample definition
(defun umemo-initialize ()
  "A bunch of `define-usage-memo' definitions. Feel free to redefine!"
  (define-usage-memo ri "ruby" 0 "ri `%s'")
  (define-usage-memo lh-refe "ruby" 0 "refe \"%s\"")
  (define-usage-memo slime-documentation "cl" 0 "*SLIME Description*")
  (define-usage-memo slime-describe-symbol "cl" 0 "*SLIME Description*")
  (define-usage-memo slime-describe-function "cl" 0 "*SLIME Description*")
  (define-usage-memo describe-function "elisp" 0 "*Help*")
  (define-usage-memo describe-variable "elisp" 0 "*Help*")
  )

;;;; minor mode definition
(defvar usage-memo-mode-map (make-sparse-keymap))
(define-key usage-memo-mode-map "\C-x\C-s" 'umemo-save)
(define-key usage-memo-mode-map "\r" 'umemo-electric-return)
(define-minor-mode usage-memo-mode
  "Automatically enabled minor mode to add usage-memo feature by 
`define-usage-memo'.

Write your annotation below `===*===*===*===*===*===*===*===*===*===*===' line.
\\<usage-memo-mode-map>\\[umemo-save]: Save your annotation to file indicated 
by `umemo-pathname'.

Of course, your annotation is revived even if Emacs is restarted!
"
  nil "<UMemo>" usage-memo-mode-map
  :global nil
  (setq buffer-read-only nil)
  (when view-mode (view-mode -1))
  (buffer-enable-undo))

;;;; SLIME hack
(defadvice slime-show-description (after usage-memo-aux activate)
  "usage-memo hack."
  (when umemo-current-entry-name
    (umemo-setup "cl" umemo-current-entry-name "*SLIME Description*")))

;; (progn (ad-disable-advice 'slime-show-description 'around 'usage-memo) 
(ad-update 'slime-show-description))


(provide 'usage-memo)

;; How to save (DO NOT REMOVE!!)
;; (let ((oddmuse-wiki "EmacsWiki")(oddmuse-page-name "usage-memo.el")) 
(call-interactively 'oddmuse-post))
;;; usage-memo.el ends here

--
rubikitch
http://www.rubyist.net/~rubikitch/




reply via email to

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