gmm-utils.el (gmm-flet, gmm-labels): New macros.
[gnus] / lisp / gmm-utils.el
index 533d9a9..3d504d7 100644 (file)
@@ -1,6 +1,6 @@
 ;;; gmm-utils.el --- Utility functions for Gnus, Message and MML
 
-;; Copyright (C) 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+;; Copyright (C) 2006-2012 Free Software Foundation, Inc.
 
 ;; Author: Reiner Steib <reiner.steib@gmx.de>
 ;; Keywords: news
@@ -140,7 +140,7 @@ This is a copy of the `lazy' widget in Emacs 22.1 provided for compatibility."
 ;;   :mouse-2 command-on-mouse-2-press
 ;;   :mouse-3 command-on-mouse-3-press) ;; typically a menu of related commands
 ;;
-;; Combinations of mouse-[23] plus shift and/or controll might be overkill.
+;; Combinations of mouse-[23] plus shift and/or control might be overkill.
 ;;
 ;; Then use (plist-get rs-command :none), (plist-get rs-command :shift)
 
@@ -217,7 +217,7 @@ This is a copy of the `lazy' widget in Emacs 22.1 provided for compatibility."
                                 'static-color 'pseudo-color)))))
       'gnome
     'retro)
-  "Prefered tool bar style."
+  "Preferred tool bar style."
   :type '(choice (const :tag "GNOME style" gnome)
                 (const :tag "Retro look"  retro))
   :group 'gmm)
@@ -267,27 +267,16 @@ DEFAULT-MAP specifies the default key map for ICON-LIST."
                       ;; (tool-bar-add-item ICON DEF KEY &rest PROPS)
                       (apply 'tool-bar-add-item icon nil nil :enable nil props)))
                    ((equal fmap t) ;; Not a menu command
-                    (if (fboundp 'tool-bar-local-item)
-                        (apply 'tool-bar-local-item
-                               icon command
-                               (intern icon) ;; reuse icon or fmap here?
-                               tool-bar-map props)
-                      ;; Emacs 21 compatibility:
-                      (apply 'tool-bar-add-item
-                             icon command
-                             (intern icon)
-                             props)))
+                    (apply 'tool-bar-local-item
+                           icon command
+                           (intern icon) ;; reuse icon or fmap here?
+                           tool-bar-map props))
                    (t ;; A menu command
-                    (if (fboundp 'tool-bar-local-item-from-menu)
-                        (apply 'tool-bar-local-item-from-menu
-                               ;; (apply 'tool-bar-local-item icon def key
-                               ;; tool-bar-map props)
-                               command icon tool-bar-map (symbol-value fmap)
-                               props)
-                      ;; Emacs 21 compatibility:
-                      (apply 'tool-bar-add-item-from-menu
-                             command icon (symbol-value fmap)
-                             props))))
+                    (apply 'tool-bar-local-item-from-menu
+                           ;; (apply 'tool-bar-local-item icon def key
+                           ;; tool-bar-map props)
+                           command icon tool-bar-map (symbol-value fmap)
+                           props)))
              t))
          (if (symbolp icon-list)
              (eval icon-list)
@@ -363,7 +352,7 @@ compatibility with versions of Emacs that lack the variable
                 dir (expand-file-name "../" dir))))
       (setq image-directory-load-path dir))
 
-    ;; If `image-directory-load-path' isn't Emacs' image directory,
+    ;; If `image-directory-load-path' isn't Emacs's image directory,
     ;; it's probably a user preference, so use it.  Then use a
     ;; relative setting if possible; otherwise, use
     ;; `image-directory-load-path'.
@@ -394,7 +383,7 @@ compatibility with versions of Emacs that lack the variable
               ;; Set it to nil if image is not found.
               (cond ((file-exists-p (expand-file-name image d2ei)) d2ei)
                     ((file-exists-p (expand-file-name image d1ei)) d1ei)))))
-     ;; Use Emacs' image directory.
+     ;; Use Emacs's image directory.
      (image-directory-load-path
       (setq image-directory image-directory-load-path))
      (no-error
@@ -422,15 +411,72 @@ If mode is nil, use `major-mode' of the current buffer."
 
 In XEmacs, the seventh argument of `write-region' specifies the
 coding-system."
-  (if (and mustbenew
-          (or (featurep 'xemacs)
-              (= emacs-major-version 20)))
+  (if (and mustbenew (featurep 'xemacs))
       (if (file-exists-p filename)
-         (signal 'file-already-exists
-                 (list "File exists" filename))
+         (signal 'file-already-exists (list "File exists" filename))
        (write-region start end filename append visit lockname))
     (write-region start end filename append visit lockname mustbenew)))
 
+;; `flet' and `labels' got obsolete since Emacs 24.3.
+(defmacro gmm-flet (bindings &rest body)
+  "Make temporary overriding function definitions.
+
+\(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
+  `(let (fn origs)
+     (dolist (bind ',bindings)
+       (setq fn (car bind))
+       (push (cons fn (and (fboundp fn) (symbol-function fn))) origs)
+       (fset fn (cons 'lambda (cdr bind))))
+     (unwind-protect
+        (progn ,@body)
+       (dolist (orig origs)
+        (if (cdr orig)
+            (fset (car orig) (cdr orig))
+          (fmakunbound (car orig)))))))
+(put 'gmm-flet 'lisp-indent-function 1)
+
+;; An alist of original function names and those unique names.
+(defvar gmm-labels-environment)
+
+(defun gmm-labels-expand (form)
+  "Expand funcalls in FORM according to `gmm-labels-environment'.
+This function is a subroutine that `gmm-labels' uses to convert any
+`(FN ...)' and #'FN elements in FORM into `(funcall UN ...)' and `UN'
+respectively if `(FN . UN)' is listed in `gmm-labels-environment'."
+  (cond ((or (not (consp form)) (memq (car form) '(\` backquote quote)))
+        form)
+       ((assq (car form) gmm-labels-environment)
+        `(funcall ,(cdr (assq (car form) gmm-labels-environment))
+                  ,@(mapcar #'gmm-labels-expand (cdr form))))
+       ((eq (car form) 'function)
+        (if (and (assq (cadr form) gmm-labels-environment)
+                 (not (cddr form)))
+            (cdr (assq (cadr form) gmm-labels-environment))
+          (cons 'function (mapcar #'gmm-labels-expand (cdr form)))))
+       (t
+        (mapcar #'gmm-labels-expand form))))
+
+(defmacro gmm-labels (bindings &rest body)
+  "Make temporary function bindings.
+The lexical scoping is handled via `lexical-let' rather than relying
+on `lexical-binding'.
+
+\(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
+  (let (gmm-labels-environment def defs)
+    (dolist (binding bindings)
+      (push (cons (car binding)
+                 (make-symbol (format "--gmm-%s--" (car binding))))
+           gmm-labels-environment))
+    `(lexical-let ,(mapcar #'cdr gmm-labels-environment)
+       (setq ,@(dolist (env gmm-labels-environment (nreverse defs))
+                (setq def (cdr (assq (car env) bindings)))
+                (push (cdr env) defs)
+                (push `(lambda ,(car def)
+                         ,@(mapcar #'gmm-labels-expand (cdr def)))
+                      defs)))
+       ,@(mapcar #'gmm-labels-expand body))))
+(put 'gmm-labels 'lisp-indent-function 1)
+
 (provide 'gmm-utils)
 
 ;;; gmm-utils.el ends here