summary refs log tree commit diff stats
path: root/lisp/+modeline.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/+modeline.el')
-rw-r--r--lisp/+modeline.el488
1 files changed, 0 insertions, 488 deletions
diff --git a/lisp/+modeline.el b/lisp/+modeline.el deleted file mode 100644 index c6e8463..0000000 --- a/lisp/+modeline.el +++ /dev/null
@@ -1,488 +0,0 @@
1;;; +modeline.el --- my modeline customizations -*- lexical-binding: t; -*-
2
3;;; Commentary:
4
5;; `+modeline.el' is kind of a dumping ground for various
6;; modeline-related functions. I probably don't use everything in
7;; here. Credit given where possible.
8
9;;; Code:
10
11(require '+util)
12(require 'actually-selected-window)
13(require 'simple-modeline)
14(require 'minions)
15
16(defgroup +modeline nil
17 "Various customization options for my modeline things."
18 :prefix "+modeline-"
19 :group 'simple-modeline)
20
21(defcustom +modeline-default-spacer " "
22 "Default spacer to use for modeline elements.
23All modeline elements take an optional argument, `spacer', which
24will default to this string.")
25
26;;; Combinators
27
28(defun +modeline-concat (segments &optional separator)
29 "Concatenate multiple functional modeline SEGMENTS.
30Each segment in SEGMENTS is a function returning a mode-line
31construct.
32
33Segments are separated using SEPARATOR, which defaults to
34`+modeline-default-spacer'. Only segments that evaluate to a
35non-zero-length string will be separated, for a cleaner look.
36
37This function returns a lambda that should be `:eval'd or
38`funcall'd in a mode-line context."
39 (let ((separator (or separator +modeline-default-spacer)))
40 (lambda ()
41 (let (this-sep result)
42 (dolist (segment segments)
43 (let ((segstr (funcall segment this-sep)))
44 (when (and segstr
45 (not (equal segstr "")))
46 (push segstr result)
47 (setq this-sep separator))))
48 (apply #'concat
49 (nreverse result))))))
50
51(defun +modeline-spacer (&optional n spacer &rest strings)
52 "Make an N-length SPACER, or prepend SPACER to STRINGS.
53When called with no arguments, insert `+modeline-default-spacer'.
54N will repeat SPACER N times, and defaults to 1. SPACER defaults
55to `+modeline-default-spacer', but can be any string. STRINGS
56should form a mode-line construct when `concat'ed."
57 (declare (indent 2))
58 (let ((spacer (or spacer +modeline-default-spacer))
59 (n (or n 1))
60 (strings (cond((null strings) '(""))
61 ((equal strings '("")) nil)
62 ((atom strings) (list strings))
63 (t strings)))
64 r)
65 (when strings (dotimes (_ n) (push spacer r)))
66 (apply #'concat (apply #'concat r) strings)))
67
68;;; Modeline segments
69
70(defun +modeline-sanitize-string (string)
71 "Sanitize a string for `format-mode-line'."
72 (when string
73 (string-replace "%" "%%" string)))
74
75(defcustom +modeline-buffer-name-max-length 0
76 "Maximum length of `+modeline-buffer-name'.
77If > 0 and < 1, use that portion of the window's width. If > 1,
78use that many characters. If anything else, don't limit. If the
79buffer name is longer than the max length, it will be shortened
80and appended with `truncate-string-ellipsis'."
81 :type '(choice (const :tag "No maximum length" 0)
82 (natnum :tag "Number of characters")
83 (float :tag "Fraction of window's width")))
84
85(defcustom +modeline-buffer-position nil
86 "What to put in the `+modeline-buffer-name' position."
87 :type 'function
88 :local t)
89
90(defun +modeline-buffer-name (&optional spacer) ; gonsie
91 "Display the buffer name."
92 (let ((bufname (string-trim (string-replace "%" "%%%%" (buffer-name)))))
93 (+modeline-spacer nil spacer
94 (if (and +modeline-buffer-position (fboundp +modeline-buffer-position))
95 (funcall +modeline-buffer-position)
96 (propertize (cond
97 ((ignore-errors
98 (and
99 (> +modeline-buffer-name-max-length 0)
100 (< +modeline-buffer-name-max-length 1)))
101 (truncate-string-to-width bufname
102 (* (window-total-width)
103 +modeline-buffer-name-max-length)
104 nil nil t))
105 ((ignore-errors
106 (> +modeline-buffer-name-max-length 1))
107 (truncate-string-to-width bufname
108 +modeline-buffer-name-max-length
109 nil nil t))
110 (t bufname))
111 'help-echo (or (buffer-file-name)
112 (buffer-name))
113 'mouse-face 'mode-line-highlight)))))
114
115(defcustom +modeline-minions-icon "&"
116 "The \"icon\" for `+modeline-minions' button."
117 :type 'string)
118
119(defun +modeline-minions (&optional spacer)
120 "Display a button for `minions-minor-modes-menu'."
121 (+modeline-spacer nil spacer
122 (propertize
123 +modeline-minions-icon
124 'help-echo "Minor modes menu\nmouse-1: show menu."
125 'local-map (purecopy (simple-modeline-make-mouse-map
126 'mouse-1
127 (lambda (event)
128 (interactive "e")
129 (with-selected-window
130 (posn-window (event-start event))
131 (minions-minor-modes-menu)))))
132 'mouse-face 'mode-line-highlight)))
133
134(defcustom +modeline-major-mode-faces '((text-mode . font-lock-string-face)
135 (prog-mode . font-lock-keyword-face)
136 (t . font-lock-warning-face))
137 "Mode->face mapping for `+modeline-major-mode'.
138If the current mode is derived from the car of a cell, the face
139in the cdr will be applied to the major-mode in the mode line."
140 :type '(alist :key-type function
141 :value-type face))
142
143(defface +modeline-major-mode-face nil
144 "Face for modeline major-mode.")
145
146(defun +modeline-major-mode (&optional spacer)
147 "Display the current `major-mode'."
148 (+modeline-spacer nil spacer
149 "("
150 (propertize ;; (+string-truncate (format-mode-line mode-name) 16)
151 (format-mode-line mode-name)
152 'face (when (actually-selected-window-p)
153 ;; XXX: This is probably really inefficient. I need to
154 ;; simply detect which mode it's in when I change major
155 ;; modes (`change-major-mode-hook') and change the face
156 ;; there, probably.
157 ;; (catch :done (dolist (cel +modeline-major-mode-faces)
158 ;; (when (derived-mode-p (car cel))
159 ;; (throw :done (cdr cel))))
160 ;; (alist-get t +modeline-major-mode-faces))
161 '+modeline-major-mode-face)
162 'keymap (let ((map (make-sparse-keymap)))
163 (bindings--define-key map [mode-line down-mouse-1]
164 `(menu-item "Menu Bar" ignore
165 :filter ,(lambda (_) (mouse-menu-major-mode-map))))
166 (define-key map [mode-line mouse-2] 'describe-mode)
167 (bindings--define-key map [mode-line down-mouse-3]
168 `(menu-item "Minions" minions-minor-modes-menu))
169 map)
170 'help-echo (+concat (list (format-mode-line mode-name) " mode")
171 "mouse-1: show menu"
172 "mouse-2: describe mode"
173 "mouse-3: display minor modes")
174 'mouse-face 'mode-line-highlight)
175 ")"))
176
177(defcustom +modeline-modified-icon-alist '((ephemeral . "*")
178 (readonly . "=")
179 (modified . "+")
180 (special . "~")
181 (t . "-"))
182 "\"Icons\" to display depending on buffer status in modeline.
183The CAR of each field is one of `readonly', `modified',
184`special', `ephemeral', or t, and the CDR is a string to display
185in that mode.
186
187`readonly' is true if the buffer is read-only and visiting a file.
188`modified' is true if the buffer is modified.
189`special' is true if the buffer is a special-mode or derived buffer.
190`ephemeral' is true if the buffer is not visiting a file.
191t is the fall-back, shown when nothing else in the alist applies.
192
193The order of elements matters: whichever one matches first is applied."
194 :type '(alist :key-type symbol
195 :value-type string)
196 :options '("readonly" "modified" "special" "t"))
197
198(defcustom +modeline-modified-icon-special-modes '(special-mode)
199 "Modes to apply the `special-mode' icon to in the
200`+modeline-modified'."
201 :type '(repeat function))
202
203(defun +modeline-modified (&optional spacer) ; modified from `simple-modeline-status-modified'
204 "Display a color-coded \"icon\" indicator for the buffer's status."
205 (let* ((icon (catch :icon
206 (dolist (cell +modeline-modified-icon-alist)
207 (when (pcase (car cell)
208 ('ephemeral (not (buffer-file-name)))
209 ('readonly buffer-read-only)
210 ('modified (buffer-modified-p))
211 ('special
212 (apply 'derived-mode-p
213 +modeline-modified-icon-special-modes))
214 ('t t)
215 (_ nil))
216 (throw :icon cell))))))
217 (+modeline-spacer nil spacer
218 (propertize (or (cdr-safe icon) "")
219 'help-echo (format "Buffer \"%s\" is %s."
220 (buffer-name)
221 (pcase (car-safe icon)
222 ('t "unmodified")
223 ('nil "unknown")
224 (_ (car-safe icon))))))))
225
226(defun +modeline-narrowed (&optional spacer)
227 "Display an indication that the buffer is narrowed."
228 (when (buffer-narrowed-p)
229 (+modeline-spacer nil spacer
230 (propertize "N"
231 'help-echo (format "%s\n%s"
232 "Buffer is narrowed."
233 "mouse-2: widen buffer.")
234 'local-map (purecopy (simple-modeline-make-mouse-map
235 'mouse-2 'mode-line-widen))
236 'face 'font-lock-doc-face
237 'mouse-face 'mode-line-highlight))))
238
239(defun +modeline-reading-mode (&optional spacer)
240 "Display an indication that the buffer is in `reading-mode'."
241 (when reading-mode
242 (+modeline-spacer nil spacer
243 (propertize
244 (concat "R" (when (bound-and-true-p +eww-readable-p) "w"))
245 'help-echo (format "%s\n%s"
246 "Buffer is in reading-mode."
247 "mouse-2: disable reading-mode.")
248 'local-map (purecopy
249 (simple-modeline-make-mouse-map
250 'mouse-2 (lambda (ev)
251 (interactive "e")
252 (with-selected-window
253 (posn-window
254 (event-start ev))
255 (reading-mode -1)
256 (force-mode-line-update)))))
257 'face 'font-lock-doc-face
258 'mouse-face 'mode-line-highlight))))
259
260(define-minor-mode file-percentage-mode
261 "Toggle the percentage display in the mode line (File Percentage Mode)."
262 :init-value t :global t :group 'mode-line)
263
264(defun +modeline--percentage ()
265 "Return point's progress through current file as a percentage."
266 (let ((tot (count-screen-lines (point-min) (point-max) :ignore-invisible)))
267 (floor (* 100 (/ (float (line-number-at-pos)) tot)))))
268
269(defun +modeline--buffer-contained-in-window-p ()
270 "Whether the buffer is totally contained within its window."
271 (let ((window-min (save-excursion (move-to-window-line 0) (point)))
272 (window-max (save-excursion (move-to-window-line -1) (point))))
273 (and (<= window-min (point-min))
274 (>= window-max (point-max)))))
275
276(defun +modeline-file-percentage (&optional spacer)
277 "Display the position in the current file."
278 (when file-percentage-mode
279 ;; (let ((perc (+modeline--percentage)))
280 ;; (propertize (+modeline-spacer nil spacer
281 ;; (cond
282 ;; ((+modeline--buffer-contained-in-window-p) "All")
283 ;; ((= (line-number-at-pos) (line-number-at-pos (point-min))) "Top")
284 ;; ((= (line-number-at-pos) (line-number-at-pos (point-max))) "Bot")
285 ;; ;; Why the 10 %s? Not sure. `format' knocks them
286 ;; ;; down to 5, then `format-mode-line' kills all but
287 ;; ;; two. If I use only 8, the margin is much too
288 ;; ;; large. Something else is obviously going on, but
289 ;; ;; I'm at a loss as to what it could be.
290 ;; (t (format "%d%%%%%%%%%%" perc))))
291 ;; ;; TODO: add scroll-up and scroll-down bindings.
292 ;; ))
293 (let ((perc (format-mode-line '(-2 "%p"))))
294 (+modeline-spacer nil spacer
295 "/"
296 (pcase perc
297 ("To" "Top")
298 ("Bo" "Bot")
299 ("Al" "All")
300 (_ (format ".%02d" (string-to-number perc))))))))
301
302(defun +modeline-file-percentage-ascii-icon (&optional spacer)
303 (when file-percentage-mode
304 (+modeline-spacer nil spacer
305 (let ((perc (format-mode-line '(-2 "%p"))))
306 (pcase perc
307 ("To" "/\\")
308 ("Bo" "\\/")
309 ("Al" "[]")
310 (_ (let ((vec (vector "/|" "//" "||" "\\\\" "\\|" "\\|"))
311 (perc (string-to-number perc)))
312 (aref vec (floor (/ perc 17))))))))))
313
314(defun +modeline-file-percentage-icon (&optional spacer)
315 "Display the position in the current file as an icon."
316 (when file-percentage-mode
317 (let ((perc (+modeline--percentage)))
318 (propertize (+modeline-spacer nil spacer
319 (cond
320 ((+modeline--buffer-contained-in-window-p) "111")
321 ((= perc 0) "000")
322 ((< perc 20) "001")
323 ((< perc 40) "010")
324 ((< perc 60) "011")
325 ((< perc 80) "100")
326 ((< perc 100) "101")
327 ((>= perc 100) "110")))
328 'help-echo (format "Point is %d%% through the buffer."
329 perc)))))
330
331(define-minor-mode region-indicator-mode
332 "Toggle the region indicator in the mode line."
333 :init-value t :global t :group 'mode-line)
334
335(defun +modeline-region (&optional spacer)
336 "Display an indicator if the region is active."
337 (when (and region-indicator-mode
338 (region-active-p))
339 (+modeline-spacer nil spacer
340 (propertize (format "%d%s"
341 (apply '+ (mapcar (lambda (pos)
342 (- (cdr pos)
343 (car pos)))
344 (region-bounds)))
345 (if (and (< (point) (mark))) "-" "+"))
346 'font-lock-face 'font-lock-variable-name-face))))
347
348(defun +modeline-line (&optional spacer)
349 (when line-number-mode
350 (+modeline-spacer nil spacer
351 "%3l")))
352
353(defun +modeline-column (&optional spacer)
354 (when column-number-mode
355 (+modeline-spacer nil spacer
356 "|"
357 (if column-number-indicator-zero-based "%2c" "%2C"))))
358
359(defcustom +modeline-position-function nil
360 "Function to use instead of `+modeline-position' in modeline."
361 :type '(choice (const :tag "Default" nil)
362 function)
363 :local t)
364
365(defun +modeline-position (&optional spacer)
366 "Display the current cursor position.
367See `line-number-mode', `column-number-mode', and
368`file-percentage-mode'. If `+modeline-position-function' is set
369to a function in the current buffer, call that function instead."
370 (cond ((functionp +modeline-position-function)
371 (when-let* ((str (funcall +modeline-position-function)))
372 (+modeline-spacer nil spacer str)))
373 (t (funcall (+modeline-concat '(+modeline-region
374 +modeline-line
375 +modeline-column
376 +modeline-file-percentage)
377 "")))))
378
379(defun +modeline-vc (&optional spacer)
380 "Display the version control branch of the current buffer in the modeline."
381 ;; from https://www.gonsie.com/blorg/modeline.html, from Doom
382 (when-let ((backend (vc-backend buffer-file-name)))
383 (+modeline-spacer nil spacer
384 (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)))))
385
386(defun +modeline-track (&optional spacer)
387 "Display `tracking-mode' information."
388 (when tracking-mode
389 tracking-mode-line-buffers))
390
391(defun +modeline-anzu (&optional spacer)
392 "Display `anzu--update-mode-line'."
393 (+modeline-spacer nil spacer
394 (anzu--update-mode-line)))
395
396(defun +modeline-text-scale (&optional spacer)
397 "Display text scaling level."
398 ;; adapted from https://github.com/seagle0128/doom-modeline
399 (when (and (boundp 'text-scale-mode-amount)
400 (/= text-scale-mode-amount 0))
401 (+modeline-spacer nil spacer
402 (concat (if (> text-scale-mode-amount 0) "+" "-")
403 (number-to-string text-scale-mode-amount)))))
404
405(defun +modeline-ace-window-display (&optional spacer)
406 "Display `ace-window-display-mode' information in the modeline."
407 (when (and +ace-window-display-mode
408 ace-window-mode)
409 (+modeline-spacer nil spacer
410 (window-parameter (selected-window) 'ace-window-path))))
411
412(defun +modeline-god-mode (&optional spacer)
413 "Display an icon when `god-mode' is active."
414 (when (and (boundp 'god-local-mode) god-local-mode)
415 (+modeline-spacer nil spacer
416 (propertize "Ω"
417 'help-echo (concat "God mode is active."
418 "\nmouse-1: exit God mode.")
419 'local-map (purecopy
420 (simple-modeline-make-mouse-map
421 'mouse-1 (lambda (e)
422 (interactive "e")
423 (with-selected-window
424 (posn-window
425 (event-start e))
426 (god-local-mode -1)
427 (force-mode-line-update)))))
428 'mouse-face 'mode-line-highlight))))
429
430(defun +modeline-input-method (&optional spacer)
431 "Display which input method is active."
432 (when current-input-method
433 (+modeline-spacer nil spacer
434 (propertize current-input-method-title
435 'help-echo (format
436 (concat "Current input method: %s\n"
437 "mouse-1: Describe current input method\n"
438 "mouse-3: Toggle input method")
439 current-input-method)
440 'local-map (purecopy
441 (let ((map (make-sparse-keymap)))
442 (define-key map [mode-line mouse-1]
443 (lambda (e)
444 (interactive "e")
445 (with-selected-window (posn-window (event-start e))
446 (describe-current-input-method))))
447 (define-key map [mode-line mouse-3]
448 (lambda (e)
449 (interactive "e")
450 (with-selected-window (posn-window (event-start e))
451 (toggle-input-method nil :interactive))))
452 map))
453 'mouse-face 'mode-line-highlight))))
454
455(defface +modeline-kmacro-indicator '((t :foreground "Firebrick"))
456 "Face for the kmacro indicator in the modeline.")
457
458(defun +modeline-kmacro-indicator (&optional spacer)
459 "Display an indicator when recording a kmacro."
460 (when defining-kbd-macro
461 (+modeline-spacer nil spacer
462 (propertize "●"
463 'face '+modeline-kmacro-indicator
464 'help-echo (format (concat "Defining a macro\n"
465 "Current step: %d\n"
466 "mouse-1: Stop recording")
467 kmacro-counter)
468 'local-map (purecopy (simple-modeline-make-mouse-map
469 'mouse-1 (lambda (e)
470 (interactive "e")
471 (with-selected-window
472 (posn-window (event-start e))
473 (kmacro-end-macro nil)))))
474 'mouse-face 'mode-line-highlight))))
475
476(defface +nyan-mode-line nil
477 "Face for nyan-cat in mode line.")
478
479(defun +modeline-nyan-on-focused (&optional spacer)
480 "Display the cat from `nyan-mode', but only on the focused window."
481 (require 'nyan-mode)
482 (when (and (or nyan-mode (bound-and-true-p +nyan-local-mode))
483 (actually-selected-window-p))
484 (+modeline-spacer nil spacer
485 (propertize (nyan-create) 'face '+nyan-mode-line))))
486
487(provide '+modeline)
488;;; +modeline.el ends here