Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

331 lines
12 KiB

4 years ago
  1. ;;; pkg-info.el --- Information about packages -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2013-2015 Sebastian Wiesner <swiesner@lunaryorn.com>
  3. ;; Author: Sebastian Wiesner <swiesner@lunaryorn.com>
  4. ;; URL: https://github.com/lunaryorn/pkg-info.el
  5. ;; Package-Version: 20150517.1143
  6. ;; Keywords: convenience
  7. ;; Version: 0.7-cvs
  8. ;; Package-Requires: ((epl "0.8"))
  9. ;; This file is not part of GNU Emacs.
  10. ;; This program is free software; you can redistribute it and/or modify
  11. ;; it under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation, either version 3 of the License, or
  13. ;; (at your option) any later version.
  14. ;; This program is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;; GNU General Public License for more details.
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. ;;; Commentary:
  21. ;; This library extracts information from installed packages.
  22. ;;;; Functions:
  23. ;; `pkg-info-library-version' extracts the version from the header of a library.
  24. ;;
  25. ;; `pkg-info-defining-library-version' extracts the version from the header of a
  26. ;; library defining a function.
  27. ;;
  28. ;; `pkg-info-package-version' gets the version of an installed package.
  29. ;;
  30. ;; `pkg-info-format-version' formats a version list as human readable string.
  31. ;;
  32. ;; `pkg-info-version-info' returns complete version information for a specific
  33. ;; package.
  34. ;;
  35. ;; `pkg-info-get-melpa-recipe' gets the MELPA recipe for a package.
  36. ;;
  37. ;; `pkg-info-get-melpa-fetcher' gets the fetcher used to build a package on
  38. ;; MELPA.
  39. ;;
  40. ;; `pkg-info-wiki-package-p' determines whether a package was build from
  41. ;; EmacsWiki on MELPA.
  42. ;;; Code:
  43. (require 'epl)
  44. (require 'lisp-mnt)
  45. (require 'find-func)
  46. (require 'json) ; `json-read'
  47. (require 'url-http) ; `url-http-parse-response'
  48. (defvar url-http-end-of-headers)
  49. ;;; Version information
  50. (defun pkg-info-format-version (version)
  51. "Format VERSION as human-readable string.
  52. Return a human-readable string representing VERSION."
  53. ;; XXX: Find a better, more flexible way of formatting?
  54. (package-version-join version))
  55. (defsubst pkg-info--show-version-and-return (version show)
  56. "Show and return VERSION.
  57. When SHOW is non-nil, show VERSION in minibuffer.
  58. Return VERSION."
  59. (when show
  60. (message (if (listp version) (pkg-info-format-version version) version)))
  61. version)
  62. (defun pkg-info--read-library ()
  63. "Read a library from minibuffer."
  64. (completing-read "Load library: "
  65. (apply-partially 'locate-file-completion-table
  66. load-path
  67. (get-load-suffixes))))
  68. (defun pkg-info--read-function ()
  69. "Read a function name from minibuffer."
  70. (let ((input (completing-read "Function: " obarray #'boundp :require-match)))
  71. (if (string= input "") nil (intern input))))
  72. (defun pkg-info--read-package ()
  73. "Read a package name from minibuffer."
  74. (let* ((installed (epl-installed-packages))
  75. (names (sort (mapcar (lambda (pkg)
  76. (symbol-name (epl-package-name pkg)))
  77. installed)
  78. #'string<))
  79. (default (car names)))
  80. (completing-read "Installed package: " names nil 'require-match
  81. nil nil default)))
  82. (defun pkg-info-library-source (library)
  83. "Get the source file of LIBRARY.
  84. LIBRARY is either a symbol denoting a named feature, or a library
  85. name as string.
  86. Return the source file of LIBRARY as string."
  87. (find-library-name (if (symbolp library) (symbol-name library) library)))
  88. (defun pkg-info-defining-library (function)
  89. "Get the source file of the library defining FUNCTION.
  90. FUNCTION is a function symbol.
  91. Return the file name of the library as string. Signal an error
  92. if the library does not exist, or if the definition of FUNCTION
  93. was not found."
  94. (unless (functionp function)
  95. (signal 'wrong-type-argument (list 'functionp function)))
  96. (let ((library (symbol-file function 'defun)))
  97. (unless library
  98. (error "Can't find definition of %s" function))
  99. library))
  100. (defun pkg-info-x-original-version (file)
  101. "Read the X-Original-Version header from FILE.
  102. Return the value as version list, or return nil if FILE lacks
  103. this header. Signal an error, if the value of the header is not
  104. a valid version."
  105. (let ((version-str (with-temp-buffer
  106. (insert-file-contents file)
  107. (lm-header "X-Original-Version"))))
  108. (when version-str
  109. (version-to-list version-str))))
  110. ;;;###autoload
  111. (defun pkg-info-library-original-version (library &optional show)
  112. "Get the original version in the header of LIBRARY.
  113. The original version is stored in the X-Original-Version header.
  114. This header is added by the MELPA package archive to preserve
  115. upstream version numbers.
  116. LIBRARY is either a symbol denoting a named feature, or a library
  117. name as string.
  118. If SHOW is non-nil, show the version in the minibuffer.
  119. Return the version from the header of LIBRARY as list. Signal an
  120. error if the LIBRARY was not found or had no X-Original-Version
  121. header.
  122. See Info node `(elisp)Library Headers' for more information
  123. about library headers."
  124. (interactive (list (pkg-info--read-library) t))
  125. (let ((version (pkg-info-x-original-version
  126. (pkg-info-library-source library))))
  127. (if version
  128. (pkg-info--show-version-and-return version show)
  129. (error "Library %s has no original version" library))))
  130. ;;;###autoload
  131. (defun pkg-info-library-version (library &optional show)
  132. "Get the version in the header of LIBRARY.
  133. LIBRARY is either a symbol denoting a named feature, or a library
  134. name as string.
  135. If SHOW is non-nil, show the version in the minibuffer.
  136. Return the version from the header of LIBRARY as list. Signal an
  137. error if the LIBRARY was not found or had no proper header.
  138. See Info node `(elisp)Library Headers' for more information
  139. about library headers."
  140. (interactive (list (pkg-info--read-library) t))
  141. (let* ((source (pkg-info-library-source library))
  142. (version (epl-package-version (epl-package-from-file source))))
  143. (pkg-info--show-version-and-return version show)))
  144. ;;;###autoload
  145. (defun pkg-info-defining-library-original-version (function &optional show)
  146. "Get the original version of the library defining FUNCTION.
  147. The original version is stored in the X-Original-Version header.
  148. This header is added by the MELPA package archive to preserve
  149. upstream version numbers.
  150. If SHOW is non-nil, show the version in mini-buffer.
  151. This function is mainly intended to find the version of a major
  152. or minor mode, i.e.
  153. (pkg-info-defining-library-version 'flycheck-mode)
  154. Return the version of the library defining FUNCTION. Signal an
  155. error if FUNCTION is not a valid function, if its defining
  156. library was not found, or if the library had no proper version
  157. header."
  158. (interactive (list (pkg-info--read-function) t))
  159. (pkg-info-library-original-version (pkg-info-defining-library function) show))
  160. ;;;###autoload
  161. (defun pkg-info-defining-library-version (function &optional show)
  162. "Get the version of the library defining FUNCTION.
  163. If SHOW is non-nil, show the version in mini-buffer.
  164. This function is mainly intended to find the version of a major
  165. or minor mode, i.e.
  166. (pkg-info-defining-library-version 'flycheck-mode)
  167. Return the version of the library defining FUNCTION. Signal an
  168. error if FUNCTION is not a valid function, if its defining
  169. library was not found, or if the library had no proper version
  170. header."
  171. (interactive (list (pkg-info--read-function) t))
  172. (pkg-info-library-version (pkg-info-defining-library function) show))
  173. ;;;###autoload
  174. (defun pkg-info-package-version (package &optional show)
  175. "Get the version of an installed PACKAGE.
  176. If SHOW is non-nil, show the version in the minibuffer.
  177. Return the version as list, or nil if PACKAGE is not installed."
  178. (interactive (list (pkg-info--read-package) t))
  179. (let* ((name (if (stringp package) (intern package) package))
  180. (package (car (epl-find-installed-packages name))))
  181. (unless package
  182. (error "Can't find installed package %s" name))
  183. (pkg-info--show-version-and-return (epl-package-version package) show)))
  184. ;;;###autoload
  185. (defun pkg-info-version-info (library &optional package show)
  186. "Obtain complete version info for LIBRARY and PACKAGE.
  187. LIBRARY is a symbol denoting a named feature, or a library name
  188. as string. PACKAGE is a symbol denoting an ELPA package. If
  189. omitted or nil, default to LIBRARY.
  190. If SHOW is non-nil, show the version in the minibuffer.
  191. When called interactively, prompt for LIBRARY. When called
  192. interactively with prefix argument, prompt for PACKAGE as well.
  193. Return a string with complete version information for LIBRARY.
  194. This version information contains the version from the headers of
  195. LIBRARY, and the version of the installed PACKAGE, the LIBRARY is
  196. part of. If PACKAGE is not installed, or if the PACKAGE version
  197. is the same as the LIBRARY version, do not include a package
  198. version."
  199. (interactive (list (pkg-info--read-library)
  200. (when current-prefix-arg
  201. (pkg-info--read-package))
  202. t))
  203. (let* ((package (or package (if (stringp library) (intern library) library)))
  204. (orig-version (condition-case nil
  205. (pkg-info-library-original-version library)
  206. (error nil)))
  207. ;; If we have X-Original-Version, we assume that MELPA replaced the
  208. ;; library version with its generated version, so we use the
  209. ;; X-Original-Version header instead, and ignore the library version
  210. ;; header
  211. (lib-version (or orig-version (pkg-info-library-version library)))
  212. (pkg-version (condition-case nil
  213. (pkg-info-package-version package)
  214. (error nil)))
  215. (version (if (and pkg-version
  216. (not (version-list-= lib-version pkg-version)))
  217. (format "%s (package: %s)"
  218. (pkg-info-format-version lib-version)
  219. (pkg-info-format-version pkg-version))
  220. (pkg-info-format-version lib-version))))
  221. (pkg-info--show-version-and-return version show)))
  222. (defconst pkg-info-melpa-recipe-url "http://melpa.org/recipes.json"
  223. "The URL from which to fetch MELPA recipes.")
  224. (defvar pkg-info-melpa-recipes nil
  225. "An alist of MELPA recipes.")
  226. (defun pkg-info-retrieve-melpa-recipes ()
  227. "Retrieve MELPA recipes from MELPA archive."
  228. (let ((buffer (url-retrieve-synchronously pkg-info-melpa-recipe-url)))
  229. (with-current-buffer buffer
  230. (unwind-protect
  231. (let ((response-code (url-http-parse-response)))
  232. (unless (equal response-code 200)
  233. (error "Failed to retrieve MELPA recipes from %s (code %s)"
  234. pkg-info-melpa-recipe-url response-code))
  235. (goto-char url-http-end-of-headers)
  236. (json-read))
  237. (when (and buffer (buffer-live-p buffer))
  238. (kill-buffer buffer))))))
  239. (defun pkg-info-get-melpa-recipes ()
  240. "Get MELPA recipes."
  241. (setq pkg-info-melpa-recipes
  242. (or pkg-info-melpa-recipes
  243. (pkg-info-retrieve-melpa-recipes))))
  244. (defun pkg-info-get-melpa-recipe (package)
  245. "Get the MELPA recipe for PACKAGE.
  246. Return nil if PACKAGE is not on MELPA."
  247. (cdr (assq package (pkg-info-get-melpa-recipes))))
  248. (defun pkg-info-get-melpa-fetcher (package)
  249. "Get the MELPA fetcher for PACKAGE."
  250. (cdr (assq 'fetcher (pkg-info-get-melpa-recipe package))))
  251. (defun pkg-info-wiki-package-p (package)
  252. "Determine whether PACKAGE is build from the EmacsWiki."
  253. (equal (pkg-info-get-melpa-fetcher package) "wiki"))
  254. (provide 'pkg-info)
  255. ;; Local Variables:
  256. ;; indent-tabs-mode: nil
  257. ;; coding: utf-8
  258. ;; End:
  259. ;;; pkg-info.el ends here