al-misc.el 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. ;;; al-misc.el --- Miscellaneous additional functionality
  2. ;; Copyright © 2013-2016 Alex Kost
  3. ;; This program is free software; you can redistribute it and/or modify
  4. ;; it under the terms of the GNU General Public License as published by
  5. ;; the Free Software Foundation, either version 3 of the License, or
  6. ;; (at your option) any later version.
  7. ;; This program is distributed in the hope that it will be useful,
  8. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. ;; GNU General Public License for more details.
  11. ;; You should have received a copy of the GNU General Public License
  12. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ;;; Code:
  14. (defun al/xor (a b)
  15. "Exclusive or."
  16. (if a (not b) b))
  17. (defun al/warning (format-string &rest args)
  18. "Display a warning message."
  19. (apply #'message
  20. (concat "WARNING: " format-string)
  21. args))
  22. (defun al/p (predicate val &optional message)
  23. "Return non-nil if PREDICATE returns non-nil on VAL.
  24. Otherwise display warning MESSAGE on VAL and return nil."
  25. (or (funcall predicate val)
  26. (progn (and message (al/warning message val))
  27. nil)))
  28. (defun al/every (predicate vals &optional message)
  29. "Return non-nil if PREDICATE returns non-nil on each element of VALS.
  30. If VALS is not a list, call PREDICATE on this value."
  31. (if (and (listp vals)
  32. (not (functionp vals))) ; to avoid treating "(lambda …)" as list
  33. (cl-every (lambda (val)
  34. (al/p predicate val message))
  35. vals)
  36. (al/p predicate vals message)))
  37. (defun al/function? (object)
  38. "Non-nil if OBJECT is a function or a list of functions."
  39. (al/every #'functionp object
  40. "Unknown function '%S'."))
  41. (defun al/bound? (object)
  42. "Non-nil if OBJECT is a bound symbol or a list of bound symbols."
  43. (al/every #'boundp object
  44. "Symbol '%S' is not bound."))
  45. (defun al/file? (object)
  46. "Non-nil if OBJECT is an existing file or a list of directories."
  47. (al/every #'file-exists-p object
  48. "File '%s' does not exist."))
  49. (defun al/directory? (object)
  50. "Non-nil if OBJECT is an existing directory or a list of directories."
  51. (al/every #'file-directory-p object
  52. "Directory '%s' does not exist."))
  53. (defmacro al/with-check (&rest body)
  54. "Call rest of BODY if all checks are passed successfully.
  55. BODY should start with checks (keyword arguments). The following
  56. keywords are available: `:fun'/`:var'/`:file'/`:dir'. Each
  57. keyword argument may be an object or a list of objects. These
  58. objects are checkced to be a proper function / a bound symbol /
  59. an existing file / an existing directory.
  60. Return nil if checks are not passed."
  61. (declare (indent 0) (debug (name body)))
  62. (let (fun var file dir)
  63. (while (keywordp (car body))
  64. (pcase (pop body)
  65. (`:fun (setq fun (pop body)))
  66. (`:var (setq var (pop body)))
  67. (`:file (setq file (pop body)))
  68. (`:dir (setq dir (pop body)))
  69. (_ (pop body))))
  70. `(when (and ,(or (null fun) `(al/function? ,fun))
  71. ,(or (null var) `(al/bound? ,var))
  72. ,(or (null file) `(al/file? ,file))
  73. ,(or (null dir) `(al/directory? ,dir)))
  74. ,@body)))
  75. (defun al/funcall-or-dolist (val function)
  76. "Call FUNCTION on VAL if VAL is not a list.
  77. If VAL is a list, call FUNCTION on each element of the list."
  78. (declare (indent 1))
  79. (if (listp val)
  80. (dolist (v val)
  81. (funcall function v))
  82. (funcall function val)))
  83. (defun al/list-maybe (obj)
  84. "Return OBJ if it is a list, or a list with OBJ otherwise."
  85. (if (listp obj) obj (list obj)))
  86. (defun al/add-to-load-path-maybe (&rest dirs)
  87. "Add existing directories from DIRS to `load-path'."
  88. (dolist (dir dirs)
  89. (al/with-check
  90. :dir dir
  91. (push dir load-path))))
  92. (defun al/load (file)
  93. "Load FILE.
  94. FILE may omit an extension. See `load' for details."
  95. (or (load file 'noerror)
  96. (al/warning "Failed to load '%s'." file)))
  97. (defun al/add-hook-maybe (hooks functions &optional append local)
  98. "Add all bound FUNCTIONS to all HOOKS.
  99. Both HOOKS and FUNCTIONS may be single variables or lists of those."
  100. (declare (indent 1))
  101. (al/funcall-or-dolist functions
  102. (lambda (fun)
  103. (al/with-check
  104. :fun fun
  105. (al/funcall-or-dolist hooks
  106. (lambda (hook)
  107. (add-hook hook fun append local)))))))
  108. (defun al/add-after-init-hook (functions)
  109. "Add functions to `after-init-hook'.
  110. See `al/add-hook-maybe'."
  111. (al/add-hook-maybe 'after-init-hook functions))
  112. (defmacro al/eval-after-init (&rest body)
  113. "Add to `after-init-hook' a `lambda' expression with BODY."
  114. (declare (indent 0))
  115. `(add-hook 'after-init-hook (lambda () ,@body)))
  116. (defmacro al/define-package-exists (name &optional symbol)
  117. "Define `al/NAME-exists?' variable.
  118. The value of the variable tells if SYMBOL is `fbound'. If SYMBOL
  119. is not specified, NAME is checked (both should be unquoted
  120. symbols)."
  121. (let* ((name-str (symbol-name name))
  122. (var (intern (concat "al/" name-str "-exists?"))))
  123. `(defvar ,var (fboundp ',(or symbol name))
  124. ,(format "Non-nil, if `%s' package is available."
  125. name-str))))
  126. (provide 'al-misc)
  127. ;;; al-misc.el ends here