Merge branch 'master' into core-updates
[jackhill/guix/guix.git] / emacs / guix-prettify.el
CommitLineData
9a130e19
AK
1;;; guix-prettify.el --- Prettify Guix store file names
2
3;; Copyright © 2014 Alex Kost <alezost@gmail.com>
4
5;; This file is part of GNU Guix.
6
7;; GNU Guix is free software; you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation, either version 3 of the License, or
10;; (at your option) any later version.
11
12;; GNU Guix is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20;;; Commentary:
21
22;; This package provides minor-mode for prettifying Guix store file
23;; names — i.e., after enabling `guix-prettify-mode',
24;; '/gnu/store/72f54nfp6g1hz873w8z3gfcah0h4nl9p-foo-0.1' names will be
25;; replaced with '/gnu/store/…-foo-0.1' in the current buffer. There is
26;; also `global-guix-prettify-mode' for global prettifying.
27
28;; To install, add the following to your emacs init file:
29;;
30;; (add-to-list 'load-path "/path/to/dir-with-guix-prettify")
31;; (autoload 'guix-prettify-mode "guix-prettify" nil t)
32;; (autoload 'global-guix-prettify-mode "guix-prettify" nil t)
33
34;; If you want to enable/disable composition after "M-x font-lock-mode",
35;; use the following setting:
36;;
37;; (setq font-lock-extra-managed-props
38;; (cons 'composition font-lock-extra-managed-props))
39
40;; Credits:
41;;
42;; Thanks to Ludovic Courtès for the idea of this package.
43;;
44;; Thanks to the authors of `prettify-symbols-mode' (part of Emacs 24.4)
45;; and "pretty-symbols.el" <http://github.com/drothlis/pretty-symbols>
46;; for the code. It helped to write this package.
47
48;;; Code:
49
50(defgroup guix-prettify nil
51 "Prettify Guix store file names."
52 :prefix "guix-prettify-"
53 :group 'font-lock
54 :group 'convenience)
55
56(defcustom guix-prettify-char ?…
57 "Character used for prettifying."
58 :type 'character
59 :group 'guix-prettify)
60
61(defcustom guix-prettify-decompose-force nil
62 "If non-nil, remove any composition.
63
64By default, after disabling `guix-prettify-mode',
65compositions (prettifying names with `guix-prettify-char') are
66removed only from strings matching `guix-prettify-regexp', so
67that compositions created by other modes are left untouched.
68
69Set this variable to non-nil, if you want to remove any
70composition unconditionally (like `prettify-symbols-mode' does).
71Most likely it will do no harm and will make the process of
72disabling `guix-prettify-mode' a little faster."
73 :type 'boolean
74 :group 'guix-prettify)
75
76(defcustom guix-prettify-regexp
77 (rx "/"
78 (or "nix" "gnu")
79 "/store/"
80 ;; Hash-parts do not include "e", "o", "u" and "t". See base32Chars
81 ;; at <https://github.com/NixOS/nix/blob/master/src/libutil/hash.cc>
82 (group (= 32 (any "0-9" "a-d" "f-n" "p-s" "v-z"))))
83 "Regexp matching file names for prettifying.
84
85Disable `guix-prettify-mode' before modifying this variable and
86make sure to modify `guix-prettify-regexp-group' if needed.
87
88Example of a \"deeper\" prettifying:
89
90 (setq guix-prettify-regexp \"store/[[:alnum:]]\\\\\\={32\\\\}\"
91 guix-prettify-regexp-group 0)
92
93This will transform
94'/gnu/store/72f54nfp6g1hz873w8z3gfcah0h4nl9p-foo-0.1' into
95'/gnu/…-foo-0.1'"
96 :type 'regexp
97 :group 'guix-prettify)
98
99(defcustom guix-prettify-regexp-group 1
100 "Regexp group in `guix-prettify-regexp' for prettifying."
101 :type 'integer
102 :group 'guix-prettify)
103
104(defvar guix-prettify-special-modes
105 '(guix-info-mode ibuffer-mode)
106 "List of special modes that support font-locking.
107
108By default, \\[global-guix-prettify-mode] enables prettifying in
109all buffers except the ones where `font-lock-defaults' is
110nil (see Info node `(elisp) Font Lock Basics'), because it may
111break the existing highlighting.
112
113Modes from this list and all derived modes are exceptions
114\(`global-guix-prettify-mode' enables prettifying there).")
115
116(defvar guix-prettify-flush-function
117 (cond ((fboundp 'font-lock-flush) #'font-lock-flush)
118 ((fboundp 'jit-lock-refontify) #'jit-lock-refontify))
119 "Function used to refontify buffer.
120This function is called without arguments after
121enabling/disabling `guix-prettify-mode'. If nil, do nothing.")
122
123(defun guix-prettify-compose ()
124 "Compose matching region in the current buffer."
125 (let ((beg (match-beginning guix-prettify-regexp-group))
126 (end (match-end guix-prettify-regexp-group)))
127 (compose-region beg end guix-prettify-char 'decompose-region))
128 ;; Return nil because we're not adding any face property.
129 nil)
130
131(defun guix-prettify-decompose-buffer ()
132 "Remove file names compositions from the current buffer."
133 (with-silent-modifications
134 (let ((inhibit-read-only t))
135 (if guix-prettify-decompose-force
136 (remove-text-properties (point-min)
137 (point-max)
138 '(composition nil))
139 (save-excursion
140 (goto-char (point-min))
141 (while (re-search-forward guix-prettify-regexp nil t)
142 (remove-text-properties
143 (match-beginning guix-prettify-regexp-group)
144 (match-end guix-prettify-regexp-group)
145 '(composition nil))))))))
146
147;;;###autoload
148(define-minor-mode guix-prettify-mode
149 "Toggle Guix Prettify mode.
150
151With a prefix argument ARG, enable Guix Prettify mode if ARG is
152positive, and disable it otherwise. If called from Lisp, enable
153the mode if ARG is omitted or nil.
154
155When Guix Prettify mode is enabled, hash-parts of the Guix store
156file names (see `guix-prettify-regexp') are prettified,
157i.e. displayed as `guix-prettify-char' character. This mode can
158be enabled programmatically using hooks:
159
160 (add-hook 'shell-mode-hook 'guix-prettify-mode)
161
162It is possible to enable the mode in any buffer, however not any
163buffer's highlighting may survive after adding new elements to
164`font-lock-keywords' (see `guix-prettify-special-modes' for
165details).
166
167Also you can use `global-guix-prettify-mode' to enable Guix
168Prettify mode for all modes that support font-locking."
169 :init-value nil
170 :lighter " …"
171 (let ((keywords `((,guix-prettify-regexp
172 (,guix-prettify-regexp-group
173 (guix-prettify-compose))))))
174 (if guix-prettify-mode
175 ;; Turn on.
176 (font-lock-add-keywords nil keywords)
177 ;; Turn off.
178 (font-lock-remove-keywords nil keywords)
179 (guix-prettify-decompose-buffer))
180 (and guix-prettify-flush-function
181 (funcall guix-prettify-flush-function))))
182
183(defun guix-prettify-supported-p ()
184 "Return non-nil, if the mode can be harmlessly enabled in current buffer."
185 (or font-lock-defaults
186 (apply #'derived-mode-p guix-prettify-special-modes)))
187
188(defun guix-prettify-turn-on ()
189 "Enable `guix-prettify-mode' in the current buffer if needed.
190See `guix-prettify-special-modes' for details."
191 (and (not guix-prettify-mode)
192 (guix-prettify-supported-p)
193 (guix-prettify-mode)))
194
195;;;###autoload
196(define-globalized-minor-mode global-guix-prettify-mode
197 guix-prettify-mode guix-prettify-turn-on)
198
199;;;###autoload
200(defalias 'guix-prettify-global-mode 'global-guix-prettify-mode)
201
202(provide 'guix-prettify)
203
204;;; guix-prettify.el ends here