;; 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.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Code:
+(require 'font-lock)
+(require 'riece-options)
+(require 'riece-compat)
+(require 'riece-misc)
+(require 'riece-addon-modules)
+
(defgroup riece-addon-list nil
- "Add-on management"
- :tag "Addon"
- :prefix "riece-"
+ "Add-on listing."
+ :tag "Addon list"
+ :prefix "riece-addon-list-"
:group 'riece)
(defgroup riece-addon-list-faces nil
- "Faces for riece-addon-list-mode"
+ "Faces for riece-addon-list-mode."
:tag "Faces"
- :prefix "riece-addon-list"
- :group 'riece-addon)
+ :prefix "riece-addon-list-"
+ :group 'riece-addon-list)
(defface riece-addon-list-enabled-face
'((((class color) (background dark))
'((((class color) (background dark))
(:foreground "PaleTurquoise" :italic t))
(t
- (:italic t)))
+ ()))
"Face used for displaying the disabled addon."
:group 'riece-addon-list-faces)
(defvar riece-addon-list-disabled-face 'riece-addon-list-disabled-face)
-(defface riece-addon-list-unsupported-face
- '((((class color) (background dark))
- (:foreground "PaleTurquoise"))
- (t
- ()))
- "Face used for displaying the unsupported addon."
+(defface riece-addon-list-uninstalled-face
+ '((t
+ (:italic t)))
+ "Face used for displaying the uninstalled addon."
:group 'riece-addon-list-faces)
-(defvar riece-addon-list-unsupported-face 'riece-addon-list-unsupported-face)
+(defvar riece-addon-list-uninstalled-face 'riece-addon-list-uninstalled-face)
-(defface riece-addon-list-unknown-face
+(defface riece-addon-list-unloaded-face
'((t
- (:foreground "red")))
- "Face used for displaying the unknown addon."
+ (:italic t :inverse-video t)))
+ "Face used for displaying the unloaded addon."
:group 'riece-addon-list-faces)
-(defvar riece-addon-list-unknown-face 'riece-addon-list-unknown-face)
+(defvar riece-addon-list-unloaded-face 'riece-addon-list-unloaded-face)
(defface riece-addon-list-description-face
'((((class color)
(defcustom riece-addon-list-mark-face-alist
'((?+ . riece-addon-list-enabled-face)
(?- . riece-addon-list-disabled-face)
- (?= . riece-addon-list-unsupported-face)
- (?? . riece-addon-list-unknown-face))
+ (?? . riece-addon-list-uninstalled-face)
+ (? . riece-addon-list-unloaded-face))
"An alist mapping marks on riece-addon-list-buffer to faces."
:type 'list
:group 'riece-addon-list)
(defcustom riece-addon-list-font-lock-keywords
- '(("^\\([-+=?] [^:]+\\): \\(.*\\)"
+ '(("^\\([-+? ] \\S-+\\)\\s-+\\(.*\\)"
(1 (cdr (assq (aref (match-string 1) 0)
riece-addon-list-mark-face-alist)))
(2 riece-addon-list-description-face)))
:type '(repeat (list string))
:group 'riece-addon-list)
+(eval-when-compile
+ (autoload 'riece-command-save-variables "riece-commands"))
+
(defvar riece-addon-list-mode-map
(let ((keymap (make-sparse-keymap)))
(define-key keymap "+" 'riece-command-enable-addon)
(define-key keymap "-" 'riece-command-disable-addon)
+ (define-key keymap "i" 'riece-command-insinuate-addon)
+ (define-key keymap "u" 'riece-command-uninstall-addon)
+ (define-key keymap "U" 'riece-command-unload-addon)
(define-key keymap "n" 'next-line)
(define-key keymap "p" 'previous-line)
(define-key keymap " " 'scroll-up)
(define-key keymap [delete] 'scroll-down)
(define-key keymap "q" 'bury-buffer)
+ (define-key keymap "s" 'riece-command-save-variables)
keymap))
(defun riece-load-and-build-addon-dependencies (addons)
dependencies)
(while addons
(require (car addons)) ;error will be reported here
- (let* ((requires
- (funcall (or (intern-soft
- (concat (symbol-name (car addons)) "-requires"))
- #'ignore)))
+ (let* ((requires-function
+ (intern-soft
+ (concat (symbol-name (car addons)) "-requires")))
+ (requires
+ (if (and requires-function
+ (fboundp requires-function))
+ (funcall requires-function)))
(pointer requires)
entry)
;; Increment succs' pred count.
;; Merge pred's succs.
(while pointer
(if (setq entry (assq (car pointer) dependencies))
- (setcdr (cdr entry)
- (cons (car addons) (nthcdr 2 entry)))
+ (setcdr (cdr entry) (cons (car addons) (nthcdr 2 entry)))
(setq dependencies (cons (list (car pointer) 0 (car addons))
dependencies)))
(setq pointer (cdr pointer))))
(setq addons (cdr addons)))
dependencies))
-(defun riece-resolve-addons (addons)
- (let ((pointer addons)
- dependencies queue)
- ;; Uniquify, first.
- (while pointer
- (if (memq (car pointer) (cdr pointer))
- (setcar pointer nil))
- (setq pointer (cdr pointer)))
- (setq dependencies (riece-load-and-build-addon-dependencies
- (delq nil addons))
- pointer dependencies)
- ;; Sort them.
+(defun riece-sort-addon-dependencies (dependencies)
+ (let ((pointer dependencies)
+ addons queue)
(while pointer
(if (zerop (nth 1 (car pointer)))
(setq dependencies (delq (car pointer) dependencies)
queue (cons (car pointer) queue)))
(setq pointer (cdr pointer)))
- (setq addons nil)
(while queue
- (setq addons (cons (car (car queue)) addons)
+ (setq addons (cons (cons (car (car queue)) (nthcdr 2 (car queue)))
+ addons)
pointer (nthcdr 2 (car queue)))
(while pointer
(let* ((entry (assq (car pointer) dependencies))
(count (1- (nth 1 entry))))
(if (zerop count)
- (progn
- (setq dependencies (delq entry dependencies)
- queue (nconc queue (list entry))))
+ (setq dependencies (delq entry dependencies)
+ queue (nconc queue (list entry)))
(setcar (cdr entry) count)))
(setq pointer (cdr pointer)))
(setq queue (cdr queue)))
(if dependencies
- (error "Circular add-on dependency found"))
+ (error "Circular add-on dependency found: %S" dependencies))
(nreverse addons)))
+(defun riece-resolve-addons (addons)
+ ;; Add files in riece-addon-directory to addons.
+ (if (file-directory-p riece-addon-directory)
+ (setq addons (nconc
+ addons
+ (mapcar
+ (lambda (name)
+ (unless (file-directory-p
+ (expand-file-name name riece-addon-directory))
+ (intern (file-name-sans-extension name))))
+ (directory-files riece-addon-directory nil "\\`[^.]")))))
+ ;; Sort & uniquify.
+ (setq addons (sort addons (lambda (symbol1 symbol2)
+ (string-lessp (symbol-name symbol1)
+ (symbol-name symbol2)))))
+ (let ((pointer addons))
+ (while pointer
+ (if (memq (car pointer) (cdr pointer))
+ (setcar pointer nil))
+ (setq pointer (cdr pointer)))
+ (delq nil addons))
+ ;; Build & resolve dependencies.
+ (riece-sort-addon-dependencies
+ (riece-load-and-build-addon-dependencies addons)))
+
+(defun riece-insinuate-addon-1 (addon verbose)
+ (if (get addon 'riece-addon-insinuated)
+ (if verbose
+ (message "Add-on %S is already insinuated" addon))
+ (require addon)
+ (funcall (intern (concat (symbol-name addon) "-insinuate")))
+ (put addon 'riece-addon-insinuated t)
+ (if verbose
+ (message "Add-on %S is insinuated" addon))
+ (unless (get addon 'riece-addon-default-disabled)
+ (riece-enable-addon addon t))))
+
(defun riece-insinuate-addon (addon &optional verbose)
- (require addon) ;implicit dependency
- (funcall (intern (concat (symbol-name addon) "-insinuate")))
- (if verbose
- (message "Add-on %S is insinuated" addon)))
+ (unless (assq addon riece-addon-dependencies)
+ (setq riece-addons (cons addon riece-addons)
+ riece-save-variables-are-dirty t
+ riece-addon-dependencies
+ (riece-resolve-addons
+ (cons addon (mapcar #'car riece-addon-dependencies)))))
+ (let ((pointer riece-addon-dependencies)
+ addons)
+ (while pointer
+ (unless (get (car (car pointer)) 'riece-addon-insinuated)
+ (setq addons (cons (car (car pointer)) addons)))
+ (if (eq (car (car pointer)) addon)
+ (setq pointer nil)
+ (setq pointer (cdr pointer))))
+ (setq addons (nreverse addons))
+ (if (and (> (length addons) 1)
+ (eq verbose 'ask)
+ (not (y-or-n-p (format "%s will be insinuated. Continue? "
+ (mapconcat #'symbol-name addons ", ")))))
+ (error "Insinuate operation was cancelled"))
+ (while addons
+ (riece-insinuate-addon-1 (car addons) verbose)
+ (setq addons (cdr addons)))))
+
+(defun riece-uninstall-addon (addon &optional verbose)
+ (if (not (get addon 'riece-addon-insinuated))
+ (if verbose
+ (message "Add-on %S is not insinuated" addon))
+ (let ((entry (assq addon riece-addon-dependencies))
+ (uninstall (intern-soft (concat (symbol-name addon) "-uninstall"))))
+ (if entry
+ (if (cdr entry)
+ (if (= (length (cdr entry)) 1)
+ (error "%S depends on %S" (car (cdr entry)) addon)
+ (error "%s depend on %S"
+ (mapconcat #'symbol-name (cdr entry) ", ")
+ addon))
+ (riece-disable-addon addon verbose)
+ (if (and uninstall
+ (fboundp uninstall))
+ (funcall uninstall))
+ (setq riece-addon-dependencies
+ (delq entry riece-addon-dependencies))
+ (put addon 'riece-addon-insinuated nil)
+ (setq riece-addons (delq addon riece-addons)
+ riece-save-variables-are-dirty t
+ riece-addon-dependencies
+ (riece-resolve-addons
+ (delq addon (mapcar #'car riece-addon-dependencies))))))
+ (if verbose
+ (message "Add-on %S is uninstalled" addon)))))
(defun riece-enable-addon (addon &optional verbose)
- (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled"))))
- (if (null enabled)
- (if verbose
- (message "Add-on %S doesn't support enable/disable" addon))
- (if (symbol-value enabled)
- (if verbose
- (message "Can't enable add-on %S" addon))
- (funcall (intern (concat (symbol-name addon) "-enable")))
- (if verbose
- (message "Add-on %S enabled" addon))))))
+ (unless (get addon 'riece-addon-insinuated)
+ (error "Add-on %S is not insinuated" addon))
+ (if (get addon 'riece-addon-enabled)
+ (if verbose
+ (message "Add-on %S is already enabled" addon))
+ (let ((enable (intern-soft (concat (symbol-name addon) "-enable"))))
+ (if (and enable
+ (fboundp enable))
+ (funcall enable))
+ (put addon 'riece-addon-enabled t)
+ (if verbose
+ (message "Add-on %S enabled" addon)))))
(defun riece-disable-addon (addon &optional verbose)
- (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled"))))
- (if (null enabled)
- (if verbose
- (message "Add-on %S doesn't support enable/disable" addon))
- (if (symbol-value enabled)
- (progn
- (funcall (intern (concat (symbol-name addon) "-disable")))
- (if verbose
- (message "Add-on %S disabled" addon)))
- (if verbose
- (message "Can't disable add-on %S" addon))))))
+ (unless (get addon 'riece-addon-insinuated)
+ (error "Add-on %S is not insinuated" addon))
+ (if (not (get addon 'riece-addon-enabled))
+ (if verbose
+ (message "Add-on %S is already disabled" addon))
+ (let ((disable (intern-soft (concat (symbol-name addon) "-disable"))))
+ (if (and disable
+ (fboundp disable))
+ (funcall disable))
+ (put addon 'riece-addon-enabled nil)
+ (if verbose
+ (message "Add-on %S disabled" addon)))))
(put 'riece-addon-list-mode 'font-lock-defaults
'(riece-addon-list-font-lock-keywords t))
(kill-all-local-variables)
(buffer-disable-undo)
(setq major-mode 'riece-addon-list-mode
- mode-name "AddOns"
+ mode-name "AddOns"
mode-line-buffer-identification
- (riece-mode-line-buffer-identification '("Riece: "))
+ (riece-mode-line-buffer-identification '("Riece: %12b"))
truncate-lines t
buffer-read-only t)
(use-local-map riece-addon-list-mode-map)
(make-local-variable 'font-lock-defaults)
(setq font-lock-defaults '(riece-addon-list-font-lock-keywords t))
+ ;; In XEmacs, auto-initialization of font-lock is not effective
+ ;; if buffer-file-name is not set.
+ (font-lock-set-defaults)
(run-hooks 'riece-addon-list-mode-hook))
(defun riece-command-list-addons ()
(interactive)
- (set-buffer (riece-get-buffer-create "*AddOns*" 'riece-addon-list-mode))
+ (set-buffer (riece-get-buffer-create "*AddOn*" 'riece-addon-list-mode))
(riece-addon-list-mode)
(let ((inhibit-read-only t)
buffer-read-only
- (pointer riece-addons)
- enabled description point)
+ (pointer riece-addon-dependencies)
+ module-description-alist
+ description point longest)
+ (while pointer
+ (setq description (intern-soft (concat (symbol-name (car (car pointer)))
+ "-description"))
+ module-description-alist
+ (cons (cons (car (car pointer))
+ (if (and description
+ (boundp description))
+ (symbol-value description)
+ "(no description)"))
+ module-description-alist)
+ pointer (cdr pointer)))
+ (setq pointer riece-addon-modules)
+ (while pointer
+ (unless (assq (car (car pointer))
+ module-description-alist)
+ (setq module-description-alist
+ (cons (car pointer) module-description-alist)))
+ (setq pointer (cdr pointer)))
(erase-buffer)
(riece-kill-all-overlays)
+ (setq pointer module-description-alist
+ longest "")
+ (while pointer
+ (if (> (length (symbol-name (car (car pointer))))
+ (length longest))
+ (setq longest (symbol-name (car (car pointer)))))
+ (setq pointer (cdr pointer)))
+ (setq pointer (sort module-description-alist
+ (lambda (entry1 entry2)
+ (string-lessp (symbol-name (car entry1))
+ (symbol-name (car entry2))))))
(while pointer
- (setq enabled (intern-soft (concat (symbol-name (car pointer))
- "-enabled"))
- description (intern-soft (concat (symbol-name (car pointer))
- "-description")))
(setq point (point))
- (insert (format "%c %S: %s\n"
- (if (not (featurep (car pointer)))
- ??
- (if (null enabled)
- ?=
- (if (symbol-value enabled)
+ (insert (format (format "%%c %%-%dS %%s\n" (length longest))
+ (if (not (featurep (car (car pointer))))
+ ?
+ (if (not (get (car (car pointer))
+ 'riece-addon-insinuated))
+ ??
+ (if (get (car (car pointer)) 'riece-addon-enabled)
?+
?-)))
- (car pointer)
- (if description
- (symbol-value description)
- "(no description)")))
- (put-text-property point (point) 'riece-addon (car pointer))
+ (car (car pointer))
+ (cdr (car pointer))))
+ (put-text-property point (point) 'riece-addon (car (car pointer)))
(setq pointer (cdr pointer)))
(insert "
Symbols in the leftmost column:
- + The add-on is enabled.
- - The add-on is disabled.
- = The add-on doesn't support enable/disable operation.
- ? The add-on status is unknown.
+ + The add-on is enabled.
+ - The add-on is disabled.
+ ? The add-on is not insinuated.
+ The add-on is not loaded.
")
(insert (substitute-command-keys "
Useful keys:
- `\\[riece-command-enable-addon]' to enable the current add-on.
- `\\[riece-command-disable-addon]' to disable the current add-on.
+ `\\[riece-command-enable-addon]' to enable the current add-on.
+ `\\[riece-command-disable-addon]' to disable the current add-on.
+ `\\[riece-command-insinuate-addon]' to insinuate the current add-on.
+ `\\[riece-command-uninstall-addon]' to uninstall the current add-on.
+ `\\[riece-command-unload-addon]' to unload the current add-on.
+ `\\[riece-command-save-variables]' to save the current setting.
"))
(goto-char (point-min))
(pop-to-buffer (current-buffer))
(delete-other-windows)))
+(defun riece-addon-list-set-point (addon)
+ (let ((point (point-min)))
+ (while (and (not (eq (get-text-property point 'riece-addon) addon))
+ (setq point (next-single-property-change point
+ 'riece-addon))))
+ (if point
+ (goto-char point))))
+
(defun riece-command-enable-addon (addon)
(interactive
(list
(or (if (eq major-mode 'riece-addon-list-mode)
(get-text-property (point) 'riece-addon))
- (completing-read "Add-on: "
- (mapcar (lambda (addon)
- (list (symbol-name addon)))
- riece-addons)
- (lambda (pointer)
- (let ((enabled
- (intern-soft (concat (symbol-name
- (car pointer))
- "-enabled"))))
- (and enabled
- (null (symbol-value enabled)))))
- t))))
+ (intern-soft
+ (completing-read "Add-on: "
+ (mapcar (lambda (dependency)
+ (list (symbol-name (car dependency))))
+ riece-addon-dependencies)
+ (lambda (pointer)
+ (let ((enabled
+ (intern-soft (concat (car pointer)
+ "-enabled"))))
+ (and enabled
+ (null (symbol-value enabled)))))
+ t)))))
+ (riece-command-insinuate-addon addon)
(riece-enable-addon addon t)
- (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled"))))
- (if (and (eq major-mode 'riece-addon-list-mode)
- (get-text-property (point) 'riece-addon)
- enabled (symbol-value enabled))
- (save-excursion
- (beginning-of-line)
- (let ((point (point))
- (inhibit-read-only t)
- buffer-read-only)
- (delete-char 1)
- (insert "+")
- (put-text-property point (point) 'riece-addon addon))))))
+ (when (eq major-mode 'riece-addon-list-mode)
+ (riece-command-list-addons)
+ (riece-addon-list-set-point addon)))
(defun riece-command-disable-addon (addon)
(interactive
(list
(or (if (eq major-mode 'riece-addon-list-mode)
(get-text-property (point) 'riece-addon))
- (completing-read "Add-on: "
- (mapcar (lambda (addon)
- (list (symbol-name addon)))
- riece-addons)
- (lambda (pointer)
- (let ((enabled
- (intern-soft (concat (symbol-name
- (car pointer))
- "-enabled"))))
- (and enabled
- (symbol-value enabled))))
- t))))
+ (intern-soft
+ (completing-read "Add-on: "
+ (mapcar (lambda (dependency)
+ (list (symbol-name (car dependency))))
+ riece-addon-dependencies)
+ (lambda (pointer)
+ (let ((enabled
+ (intern-soft (concat (car pointer)
+ "-enabled"))))
+ (and enabled
+ (symbol-value enabled))))
+ t)))))
(riece-disable-addon addon t)
- (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled"))))
- (if (and (eq major-mode 'riece-addon-list-mode)
- (get-text-property (point) 'riece-addon)
- enabled (null (symbol-value enabled)))
- (save-excursion
- (beginning-of-line)
- (let ((point (point))
- (inhibit-read-only t)
- buffer-read-only)
- (delete-char 1)
- (insert "-")
- (put-text-property point (point) 'riece-addon addon))))))
-
+ (when (eq major-mode 'riece-addon-list-mode)
+ (riece-command-list-addons)
+ (riece-addon-list-set-point addon)))
+
+(defun riece-command-insinuate-addon (addon)
+ (interactive
+ (list
+ (or (if (eq major-mode 'riece-addon-list-mode)
+ (get-text-property (point) 'riece-addon))
+ (intern-soft
+ (completing-read "Add-on: "
+ (mapcar (lambda (dependency)
+ (list (symbol-name (car dependency))))
+ riece-addon-modules)
+ (lambda (pointer)
+ (not (get (car pointer) 'riece-addon-insinuated)))
+ t)))))
+ (riece-insinuate-addon addon 'ask)
+ (when (eq major-mode 'riece-addon-list-mode)
+ (riece-command-list-addons)
+ (riece-addon-list-set-point addon)))
+
+(defun riece-command-uninstall-addon (addon)
+ (interactive
+ (list
+ (or (if (eq major-mode 'riece-addon-list-mode)
+ (get-text-property (point) 'riece-addon))
+ (intern-soft
+ (completing-read "Add-on: "
+ (mapcar (lambda (dependency)
+ (list (symbol-name (car dependency))))
+ riece-addon-dependencies)
+ (lambda (pointer)
+ (get (car pointer) 'riece-addon-insinuated))
+ t)))))
+ (riece-uninstall-addon addon t)
+ (when (eq major-mode 'riece-addon-list-mode)
+ (riece-command-list-addons)
+ (riece-addon-list-set-point addon)))
+
+(defun riece-command-unload-addon (addon)
+ (interactive
+ (list
+ (or (if (eq major-mode 'riece-addon-list-mode)
+ (get-text-property (point) 'riece-addon))
+ (intern-soft
+ (completing-read "Add-on: "
+ (mapcar (lambda (dependency)
+ (list (symbol-name (car dependency))))
+ riece-addon-dependencies)
+ (lambda (pointer)
+ (get (car pointer) 'riece-addon-insinuated))
+ t)))))
+ (riece-uninstall-addon addon t)
+ (if (get addon 'riece-addon-not-unloadable)
+ (message "Add-on %S is not allowed to unload" addon)
+ (unload-feature addon)
+ (message "Add-on %S is unloaded" addon))
+ (when (eq major-mode 'riece-addon-list-mode)
+ (riece-command-list-addons)
+ (riece-addon-list-set-point addon)))
+
(provide 'riece-addon)
;;; riece-addon.el ends here