bui-info.el 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. ;;; bui-info.el --- 'Info' buffer interface for displaying data -*- lexical-binding: t -*-
  2. ;; Copyright © 2014–2017 Alex Kost <alezost@gmail.com>
  3. ;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
  4. ;; This program is free software; you can redistribute it and/or modify
  5. ;; it under the terms of the GNU General Public License as published by
  6. ;; the Free Software Foundation, either version 3 of the License, or
  7. ;; (at your option) any later version.
  8. ;;
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; This file provides 'info' (help-like) buffer interface for displaying
  18. ;; an arbitrary data.
  19. ;;; Code:
  20. (require 'dash)
  21. (require 'bui-core)
  22. (require 'bui-entry)
  23. (require 'bui-button)
  24. (require 'bui-utils)
  25. (bui-define-groups bui-info)
  26. (defface bui-info-heading
  27. '((((type tty pc) (class color)) :weight bold)
  28. (t :inherit variable-pitch :height 1.2 :weight bold))
  29. "Face for headings."
  30. :group 'bui-info-faces)
  31. (defface bui-info-param-title
  32. '((t :inherit font-lock-type-face))
  33. "Face used for titles of parameters."
  34. :group 'bui-info-faces)
  35. ;;; General 'info' variables
  36. (defvar bui-info-format nil
  37. "List of methods for inserting entries.
  38. Each METHOD should be either nil, a function or a list.
  39. If METHOD is nil, newline is inserted at point.
  40. If METHOD is a function, it is called with an entry as argument.
  41. If METHOD is a list, it should have the following form:
  42. (PARAM INSERT-TITLE INSERT-VALUE)
  43. PARAM is a name of the entry parameter.
  44. INSERT-TITLE may be either a symbol or a list. If it is a
  45. symbol, it should be a function or an alias from
  46. `bui-info-title-aliases', in which case it is called with title
  47. as argument. If it is a list, it should have a
  48. form (FUN-OR-ALIAS [ARGS ...]), in which case FUN-OR-ALIAS is
  49. called with title and ARGS as arguments.
  50. INSERT-VALUE may be either a symbol or a list. If it is a
  51. symbol, it should be a function or an alias from
  52. `bui-info-value-aliases', in which case it is called with value
  53. and entry as arguments. If it is a list, it should have a
  54. form (FUN-OR-ALIAS [ARGS ...]), in which case FUN-OR-ALIAS is
  55. called with value and ARGS as arguments.
  56. After inserting title/value with such a list METHOD, a new line
  57. is inserted.
  58. Parameters are inserted in the same order as defined by this list.")
  59. (put 'bui-info-format 'permanent-local t)
  60. (defcustom bui-info-ignore-empty-values nil
  61. "If non-nil, do not display non-boolean parameters with nil values."
  62. :type 'boolean
  63. :group 'bui-info)
  64. (put 'bui-info-ignore-empty-values 'permanent-local t)
  65. (defcustom bui-info-ignore-void-values t
  66. "If non-nil, do not display non-existing parameters."
  67. :type 'boolean
  68. :group 'bui-info)
  69. (put 'bui-info-ignore-void-values 'permanent-local t)
  70. (defcustom bui-info-fill t
  71. "If non-nil, fill string parameters to fit the window.
  72. If nil, insert text parameters in a raw form."
  73. :type 'boolean
  74. :group 'bui-info)
  75. (put 'bui-info-fill 'permanent-local t)
  76. (defcustom bui-info-param-title-format "%-18s: "
  77. "String used to format a title of a parameter.
  78. It should be a '%s'-sequence. After inserting a title formatted
  79. with this string, a value of the parameter is inserted.
  80. This string is used by `bui-info-insert-title-format'."
  81. :type 'string
  82. :group 'bui-info)
  83. (put 'bui-info-param-title-format 'permanent-local t)
  84. (defcustom bui-info-multiline-prefix
  85. (make-string (length (format bui-info-param-title-format " "))
  86. ?\s)
  87. "String used to format multi-line parameter values.
  88. If a value occupies more than one line, this string is inserted
  89. in the beginning of each line after the first one.
  90. This string is used by `bui-info-insert-value-format'."
  91. :type 'string
  92. :group 'bui-info)
  93. (put 'bui-info-multiline-prefix 'permanent-local t)
  94. (defcustom bui-info-delimiter "\n\f\n"
  95. "String used to separate entries."
  96. :type 'string
  97. :group 'bui-info)
  98. (put 'bui-info-delimiter 'permanent-local t)
  99. (defconst bui-info-symbol-specifications
  100. '((:delimiter delimiter t)
  101. (:fill fill t)
  102. (:format format always)
  103. (:ignore-empty-values ignore-empty-values t)
  104. (:ignore-void-values ignore-void-values t)
  105. (:multiline-prefix multiline-prefix t)
  106. (:title-format param-title-format t))
  107. "Specifications for generating 'info' variables.
  108. See `bui-symbol-specifications' for details.")
  109. ;;; Wrappers for 'info' variables
  110. (defun bui-info-symbol (entry-type symbol)
  111. "Return symbol for ENTRY-TYPE and 'info' buffer type."
  112. (bui-symbol entry-type 'info symbol))
  113. (defun bui-info-symbol-value (entry-type symbol)
  114. "Return SYMBOL's value for ENTRY-TYPE and 'info' buffer type."
  115. (bui-symbol-value entry-type 'info symbol))
  116. (defun bui-info-param-title (entry-type param)
  117. "Return a title of an ENTRY-TYPE parameter PARAM."
  118. (bui-param-title entry-type 'info param))
  119. (defun bui-info-format (entry-type)
  120. "Return 'info' format for ENTRY-TYPE."
  121. (bui-info-symbol-value entry-type 'format))
  122. (defun bui-info-displayed-params (entry-type)
  123. "Return a list of ENTRY-TYPE parameters that should be displayed."
  124. (-non-nil
  125. (--map (pcase it
  126. (`(,param . ,_) param))
  127. (bui-info-format entry-type))))
  128. ;;; Inserting entries
  129. (defvar bui-info-title-aliases
  130. '((format . bui-info-insert-title-format)
  131. (simple . bui-info-insert-title-simple))
  132. "Alist of aliases and functions to insert titles.")
  133. (defvar bui-info-value-aliases
  134. '((format . bui-info-insert-value-format)
  135. (indent . bui-info-insert-value-indent)
  136. (simple . bui-info-insert-value-simple)
  137. (time . bui-info-insert-time))
  138. "Alist of aliases and functions to insert values.")
  139. (defun bui-info-title-function (fun-or-alias)
  140. "Convert FUN-OR-ALIAS into a function to insert a title."
  141. (or (bui-assq-value bui-info-title-aliases fun-or-alias)
  142. fun-or-alias))
  143. (defun bui-info-value-function (fun-or-alias)
  144. "Convert FUN-OR-ALIAS into a function to insert a value."
  145. (or (bui-assq-value bui-info-value-aliases fun-or-alias)
  146. fun-or-alias))
  147. (defun bui-info-title-method->function (method)
  148. "Convert title METHOD into a function to insert a title."
  149. (pcase method
  150. ((pred null) #'ignore)
  151. ((pred symbolp) (bui-info-title-function method))
  152. (`(,fun-or-alias . ,rest-args)
  153. (lambda (title)
  154. (apply (bui-info-title-function fun-or-alias)
  155. title rest-args)))
  156. (_ (error "Unknown title method '%S'" method))))
  157. (defun bui-info-value-method->function (method)
  158. "Convert value METHOD into a function to insert a value."
  159. (pcase method
  160. ((pred null) #'ignore)
  161. ((pred functionp) method)
  162. (`(,fun-or-alias . ,rest-args)
  163. (lambda (value _)
  164. (apply (bui-info-value-function fun-or-alias)
  165. value rest-args)))
  166. (_ (error "Unknown value method '%S'" method))))
  167. (defun bui-info-insert-entries (entries entry-type)
  168. "Display ENTRY-TYPE ENTRIES in the current info buffer."
  169. (bui-mapinsert (lambda (entry)
  170. (bui-info-insert-entry entry entry-type))
  171. entries
  172. bui-info-delimiter)
  173. (bui-history-insert-buttons))
  174. (defun bui-info-insert-entry (entry entry-type &optional indent-level)
  175. "Insert ENTRY-TYPE ENTRY into the current info buffer.
  176. If INDENT-LEVEL is non-nil, indent displayed data by this number
  177. of `bui-indent' spaces."
  178. (bui-with-indent (* (or indent-level 0)
  179. bui-indent)
  180. (dolist (spec (bui-info-format entry-type))
  181. (bui-info-insert-entry-unit spec entry entry-type))))
  182. (defun bui-info-insert-entry-unit (format-spec entry entry-type)
  183. "Insert title and value of a PARAM at point.
  184. ENTRY is alist with parameters and their values.
  185. ENTRY-TYPE is a type of ENTRY."
  186. (pcase format-spec
  187. ((pred null)
  188. (bui-newline))
  189. ((pred functionp)
  190. (funcall format-spec entry))
  191. (`(,param ,title-method ,value-method)
  192. (let* ((value (bui-entry-value entry param))
  193. (void? (bui-void-value? value))
  194. (empty? (null value))
  195. (boolean? (bui-boolean-param? entry-type 'info param)))
  196. (unless (or (and bui-info-ignore-void-values void?)
  197. (and bui-info-ignore-empty-values
  198. empty? (not boolean?)))
  199. (let ((title (bui-info-param-title entry-type param))
  200. (insert-title (bui-info-title-method->function title-method))
  201. (insert-value (bui-info-value-method->function value-method)))
  202. (funcall insert-title title)
  203. (cond
  204. (void? (insert bui-empty-string))
  205. ((and empty? boolean?) (insert bui-false-string))
  206. (t (funcall insert-value value entry)))
  207. (bui-newline)))))
  208. (_ (error "Unknown format specification '%S'" format-spec))))
  209. (defun bui-info-insert-title-simple (title &optional face)
  210. "Insert \"TITLE: \" string at point.
  211. If FACE is nil, use `bui-info-param-title'."
  212. (bui-format-insert title
  213. (or face 'bui-info-param-title)
  214. "%s: "))
  215. (defun bui-info-insert-title-format (title &optional face)
  216. "Insert TITLE using `bui-info-param-title-format' at point.
  217. If FACE is nil, use `bui-info-param-title'."
  218. (bui-format-insert title
  219. (or face 'bui-info-param-title)
  220. bui-info-param-title-format))
  221. (defun bui-info-insert-value-simple (value &optional button-or-face indent)
  222. "Format and insert parameter VALUE at point.
  223. VALUE may be split into several short lines to fit the current
  224. window, depending on `bui-info-fill', and each line is indented
  225. with INDENT number of spaces.
  226. If BUTTON-OR-FACE is a button type symbol, transform VALUE into
  227. this (these) button(s) and insert each one on a new line. If it
  228. is a face symbol, propertize inserted line(s) with this face."
  229. (or indent (setq indent 0))
  230. (bui-with-indent indent
  231. (let* ((button? (bui-button-type? button-or-face))
  232. (face (unless button? button-or-face))
  233. (fill-col (unless (or button?
  234. (and (stringp value)
  235. (not bui-info-fill)))
  236. (- (bui-fill-column) indent)))
  237. (value (if (and value button?)
  238. (bui-buttonize value button-or-face "\n")
  239. value)))
  240. (bui-split-insert value face fill-col "\n"))))
  241. (defun bui-info-insert-value-indent (value &optional button-or-face)
  242. "Format and insert parameter VALUE at point.
  243. This function is intended to be called after inserting a title
  244. with `bui-info-insert-title-simple'.
  245. VALUE may be split into several short lines to fit the current
  246. window, depending on `bui-info-fill', and each line is indented
  247. with `bui-indent'.
  248. For the meaning of BUTTON-OR-FACE, see `bui-info-insert-value-simple'."
  249. (when value (bui-newline))
  250. (bui-info-insert-value-simple value button-or-face bui-indent))
  251. (defun bui-info-insert-value-format (value &optional button-or-face
  252. &rest button-properties)
  253. "Format and insert parameter VALUE at point.
  254. This function is intended to be called after inserting a title
  255. with `bui-info-insert-title-format'.
  256. VALUE may be split into several short lines to fit the current
  257. window, depending on `bui-info-fill' and
  258. `bui-info-multiline-prefix'. If VALUE is a list, its elements
  259. will be separated with `bui-list-separator'.
  260. If BUTTON-OR-FACE is a button type symbol, transform VALUE into
  261. this (these) button(s). If it is a face symbol, propertize
  262. inserted line(s) with this face.
  263. BUTTON-PROPERTIES are passed to `bui-buttonize' (only if
  264. BUTTON-OR-FACE is a button type)."
  265. (let* ((button? (bui-button-type? button-or-face))
  266. (face (unless button? button-or-face))
  267. (fill-col (when (or button?
  268. bui-info-fill
  269. (not (stringp value)))
  270. (- (bui-fill-column)
  271. (length bui-info-multiline-prefix))))
  272. (value (if (and value button?)
  273. (apply #'bui-buttonize
  274. value button-or-face bui-list-separator
  275. button-properties)
  276. value)))
  277. (bui-split-insert value face fill-col
  278. (concat "\n" bui-info-multiline-prefix))))
  279. (defun bui-info-insert-time (time &optional face)
  280. "Insert formatted time string using TIME at point.
  281. See `bui-get-time-string' for the meaning of TIME."
  282. (bui-format-insert (bui-get-time-string time)
  283. (or face 'bui-time)))
  284. ;;; Major mode
  285. (defvar bui-info-mode-map
  286. (let ((map (make-sparse-keymap)))
  287. (set-keymap-parent
  288. map (make-composed-keymap (list bui-map button-buffer-map)
  289. special-mode-map))
  290. map)
  291. "Keymap for `bui-info-mode' buffers.")
  292. (define-derived-mode bui-info-mode special-mode "BUI-Info"
  293. "Parent mode for displaying data in 'info' form."
  294. (bui-info-initialize))
  295. (defun bui-info-initialize ()
  296. "Set up the current 'info' buffer."
  297. ;; Without this, syntactic fontification is performed, and it may
  298. ;; break highlighting. For example, if there is a single "
  299. ;; (double-quote) character, the default syntactic fontification
  300. ;; highlights the rest text after it as a string.
  301. ;; See (info "(elisp) Font Lock Basics") for details.
  302. (setq font-lock-defaults '(nil t)))
  303. (provide 'bui-info)
  304. ;;; bui-info.el ends here