X-Git-Url: https://cgit.sxemacs.org/?p=riece;a=blobdiff_plain;f=lisp%2Friece-addon.el;h=1b1288fb1d55ad66eb11758fab56b7c16c96efe0;hp=a455516e093b545f2b90002b325204473c535e18;hb=19580aaa990e2d6cda91579c735b7d02f02cd910;hpb=648f9f763eba13118456922e76ebabedcf505878 diff --git a/lisp/riece-addon.el b/lisp/riece-addon.el index a455516..1b1288f 100644 --- a/lisp/riece-addon.el +++ b/lisp/riece-addon.el @@ -24,10 +24,109 @@ ;;; Code: +(require 'font-lock) +(require 'riece-options) +(require 'riece-compat) +(require 'riece-misc) +(require 'riece-addon-modules) + +(defgroup riece-addon-list nil + "Add-on listing." + :tag "Addon list" + :prefix "riece-addon-list-" + :group 'riece) + +(defgroup riece-addon-list-faces nil + "Faces for riece-addon-list-mode." + :tag "Faces" + :prefix "riece-addon-list-" + :group 'riece-addon-list) + +(defface riece-addon-list-enabled-face + '((((class color) (background dark)) + (:foreground "PaleTurquoise" :bold t)) + (t + (:bold t))) + "Face used for displaying the enabled addon." + :group 'riece-addon-list-faces) +(defvar riece-addon-list-enabled-face 'riece-addon-list-enabled-face) + +(defface riece-addon-list-disabled-face + '((((class color) (background dark)) + (:foreground "PaleTurquoise" :italic t)) + (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." + :group 'riece-addon-list-faces) +(defvar riece-addon-list-unsupported-face 'riece-addon-list-unsupported-face) + +(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-uninstalled-face 'riece-addon-list-uninstalled-face) + +(defface riece-addon-list-unloaded-face + '((t + (:italic t :inverse-video t))) + "Face used for displaying the unloaded addon." + :group 'riece-addon-list-faces) +(defvar riece-addon-list-unloaded-face 'riece-addon-list-unloaded-face) + +(defface riece-addon-list-description-face + '((((class color) + (background dark)) + (:foreground "lightyellow")) + (((class color) + (background light)) + (:foreground "blue4")) + (t + ())) + "Face used for displaying the description addon." + :group 'riece-addon-list-faces) +(defvar riece-addon-list-description-face 'riece-addon-list-description-face) + +(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-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))) + "Default expressions to addon in riece-addon-list-mode." + :type '(repeat (list string)) + :group 'riece-addon-list) + (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) keymap)) (defun riece-load-and-build-addon-dependencies (addons) @@ -35,10 +134,13 @@ 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. @@ -49,70 +151,143 @@ ;; 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)) + (while pointer + (unless (get (car (car pointer)) 'riece-addon-insinuated) + (riece-insinuate-addon-1 (car (car pointer)) verbose)) + (if (eq (car (car pointer)) addon) + (setq pointer nil) + (setq pointer (cdr pointer)))))) + +(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)) + (enabled (intern-soft (concat (symbol-name addon) "-enabled"))) + (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)) + (if (and enabled + (boundp enabled) + (symbol-value enabled)) + (riece-disable-addon addon verbose)) + (if (and uninstall + (fboundp uninstall)) + (funcall uninstall)) + (setq riece-addon-dependencies + (delq entry riece-addon-dependencies)) + (remprop addon 'riece-addon-insinuated) + (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) + (unless (get addon 'riece-addon-insinuated) + (error "Add-on %S is not insinuated" addon)) (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled")))) - (if (null enabled) + (if (or (null enabled) + (not (boundp 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)) + (message "Add-on %S is already enabled" addon)) (funcall (intern (concat (symbol-name addon) "-enable"))) (if verbose (message "Add-on %S enabled" addon)))))) (defun riece-disable-addon (addon &optional verbose) + (unless (get addon 'riece-addon-insinuated) + (error "Add-on %S is not insinuated" addon)) (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled")))) - (if (null enabled) + (if (or (null enabled) + (not (boundp enabled))) (if verbose (message "Add-on %S doesn't support enable/disable" addon)) (if (symbol-value enabled) @@ -121,7 +296,10 @@ (if verbose (message "Add-on %S disabled" addon))) (if verbose - (message "Can't disable add-on %S" addon)))))) + (message "Add-on %S is already enabled" addon)))))) + +(put 'riece-addon-list-mode 'font-lock-defaults + '(riece-addon-list-font-lock-keywords t)) (defun riece-addon-list-mode () "Major mode for displaying addon list. @@ -129,93 +307,200 @@ All normal editing commands are turned off." (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) - (save-excursion - (set-buffer (riece-get-buffer-create "*AddOns*" 'riece-addon-list-mode)) - (riece-addon-list-mode) - (let ((inhibit-read-only t) - buffer-read-only - (pointer riece-addons) - enabled description point) - (erase-buffer) - (riece-kill-all-overlays) - (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))) + (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-addon-dependencies) + module-description-alist + description enabled point) + (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 (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 (car pointer))) + "-enabled"))) + (setq point (point)) + (insert (format "%c %-15S %s\n" + (if (not (featurep (car (car pointer)))) + ? + (if (not (get (car (car pointer)) + 'riece-addon-insinuated)) ?? (if (null enabled) - ?= + ?! (if (symbol-value enabled) ?+ - ?-))) - (car pointer) - (if description - (symbol-value description) - "(no description)"))) - (put-text-property point (point) 'riece-addon (car pointer)) - (setq pointer (cdr pointer))) - (insert " + ?-)))) + (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 not known. + ! The add-on doesn't support enable/disable operation. + ? The add-on is not insinuated. + The add-on is not loaded. ") - (insert (substitute-command-keys " + (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. -"))) - (pop-to-buffer (current-buffer)))) +")) + (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 #'list riece-addons) - (lambda (pointer) - (setq enabled (intern-soft (concat (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-enable-addon addon t) - (riece-command-list-addons)) + (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 #'list riece-addons) - (lambda (pointer) - (setq enabled (intern-soft (concat (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) - (riece-command-list-addons)) - + (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 t) + (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